How to access a MainWindow variable from a page in C# WPF? - c#

I am trying to code a WPF desktop Application. Currently i have a Main Window (MainWindow) and a page (Pageone) under the same solution. From my MainWindow.xaml.cs page, i have a variable (proc1) which i want to pass to my Pageone.xaml.cs page and maybe even more pages in the future to access and use for some calculation.
However i cant seem to find a method to successfully do this, i have tried making my variable "public", and instantiate the MainWindow object for my page to access it but it doesn't seem to work. (A field initializer cannot reference the non-static field, method, or property 'Pageone.pog')
MainWindow.xaml.cs
public string proc1;
public void startTroubleshootButton_Click(object sender, RoutedEventArgs e)
{
try
{
var selectedProcess = listViewProcesses.SelectedItems[0] as myProcess;
if (selectedProcess == null)
{
MessageBox.Show("no selection made");
return;
}
proc1 = selectedProcess.processName;
MessageBox.Show($"you have selected the {proc1} application ");
Pageone pg = new Pageone(this);
this.Content = pg;
}
catch(ArgumentOutOfRangeException)
{
return;
}
}
Pageone.xaml.cs
public partial class Pageone : Page
{
public Pageone(MainWindow mainWindow)
{
InitializeComponent();
}
MainWindow pog = new MainWindow();
string procName = pog.proc1;
...
I've heard that i will maybe need to use something called the MVVM or code a parameterized constructor but i'm not sure if its related to the code i'm doing. Is there a better way to go about coding this? Thanks.

It can be done like:
var window = (MainWindow)Application.Current.MainWindow;

Related

Unable to set a TextBox text owned by MainWindow from another class

I am trying to set a TextBox value text owned by MainWindow (WPF Window) from another class in the same namespace, but nothing happens. I've gone through many suggestions and answers to similar questions with no avail. What puzzles me is that MainWindow's SetText receives the text as it is being displayed in Visual Studio console, but doesn't have any effect on the actual textbox. The 'ConsoleLog' textbox has the default XAML values.
So my MainWindow is similar to this:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public string SetText
{
get { return ConsoleLog.Text; }
set {
Console.WriteLine(value);
ConsoleLog.Text = value;
}
}
}
In my App class:
public partial class App : Application
{
private void Test()
{
var mw = new MainWindow();
mw.SetText = "Please display something!";
}
}
I tried with several other methods such as:
((MainWindow)System.Windows.Application.Current.MainWindow).ConsoleLog.Text = "Please display something!";
but nothing works so far without any error messages.
I'm fairly new to WPF C# and I'm sure it is something very obvious I'm missing, but it drives me crazy.
WPF project Default project start up MainWindow by configuring its App.xaml file, not by app.xml.cs. if you want to get access to MainWindow , you should initiate MainWindow manually by following code
public App()
{
var mw = new MainWindow();
mw.SetText = "Please display something!";
mw.ShowDialog();
}
and remove the following line from App.xaml
StartupUri="MainWindow.xaml"
if you want to get started with WPF application and make a good app,I recommend you to see some basic tutorial, and try to learn MVVM (or MVC in web) programming methodology.

Detecting tab changed event

I am working on xamarin.forms. I need to detect the event of tab being changed either by swapping left or right or by clicking without using custom renderer
I tried below event but it is firing in both cases when child page being pushed or tab being changed. how can i get isolated event of tab being changed
public class MyTabbedPage : TabbedPage
{
public MyTabbedPage()
{
this.CurrentPageChanged += CurrentPageHasChanged;
}
protected void CurrentPageHasChanged(object sender,EventArgs e)
{
var pages= Navigation.NavigationStack;
if (pages.Count > 0)
{
this.Title = pages[pages.Count - 1].Title;
}
else
this.Title = this.CurrentPage.Title;
}
}
This issue I am facing is: In below screenshot part1 is Homepage(title="Diary") & part2 is Childpage(title="Homework") when I change tab & again come to first tab than navigationbar title getting changed "Homework" to "Diary"(Screeshot2)
As you are on your tabbed page already you can literally just do the following
public partial class MyTabbedPage : TabbedPage
{
public MyTabbedPage()
{
InitializeComponent();
CurrentPageChanged += CurrentPageHasChanged;
}
private void CurrentPageHasChanged(object sender, EventArgs e) => Title = CurrentPage.Title;
}
if you want to use the sender you can do the following
public partial class MyTabbedPage : TabbedPage
{
public MyTabbedPage()
{
InitializeComponent();
CurrentPageChanged += CurrentPageHasChanged;
}
private void CurrentPageHasChanged(object sender, EventArgs e)
{
var tabbedPage = (TabbedPage) sender;
Title = tabbedPage.CurrentPage.Title;
}
}
Or if you can elaborate on what you are trying to do exactly I can give a better answer as I do not know what it is that you are trying to do exactly
I don't think you can achieve what you want to do, at least not like this. The event behaves the way it does and as described: it is fired whenever the current page changes, also for children.
That being said, I think you should focus on implementing the functionality you want with the tools we have. I can't really deduce from your code what you are trying to do, but it looks like you want to change the title? When a tab is changed? Why not just make some kind of condition to only do it for certain pages? For example when the page is contained in the Children collection?

How to pass window and string parameter from code behind into User Control

I have a bit off header that I need global to a few files so I have made a user control (I can't use the main window as this isn't global to all files) however there's two parameters that my user control needs, ScreenName and MainWindow to navigate on the home button click. Here is what I have tried so far:
public Header(MainWindow mainWindow, string screenName)
{
InitializeComponent();
DataContext = this;
ScreenName = screenName;
MainWindow = mainWindow;
ScreenNameTextBlock.Text = ScreenName;
}
public string ScreenName { get; set; }
public MainWindow MainWindow { get; set; }
private void Hyperlink_OnClick(object sender, RoutedEventArgs e)
{
//need to navigate home here
MainWindow.LoadScreenByCode("Menu");
}
You can probably assume the corresponding XAML as it's just a hyperlink and a textblock, but if you need it let me know.
I can include the user control like so:
<controls:Header x:Name="Header"></controls:Header>
But I can't figure out how to assign the parameters. If I try in the codebehind I can access the values like this:
Header.MainWindow = Shell;
Header.ScreenName = "Name";
But this causes the values to be null. Sorry if this is an easy issue, I am new to UserControls.
To access the main window of your application you should get the running instance of it as the following:
MainWindow myRunnningMainWindow = (Application.Current.MainWindow as MainWindow);
if(myRunningWindow!=null)
{
//Do what you want with the main window
}

List+Detail - Best Approach?

I hope you guys can help me out as I can't find anything useful that helps with the understanding of my problem:
I'm trying to realize a passive MVP approach on my C# WinForms application which has list views and corresponding detail views.
So far I've got the following structure (pseudo code):
ListPresenter(new Repository(), new ListView(), new DetailPresenter(new DetailView());
Implementation:
public class UserDetailPresenter : IPresenter<IUserDetailView>
{
private IDetailView _view;
public UserDetailPresenter(IDetailView detailView)
{
_view = detailView;
}
public void Show(IUser user)
{
InitializeView(user);
_view.Show();
}
}
public class UserListPresenter
{
//private members (_userRepo, _listView, _detailPresenter)
public UserListView(IUserRepository userRepo, IListView listView, IDetailPresenter detailPresenter)
{
//wire up private members..
_listView.EditCommandFired += this.ShowEditForm;
}
private void OnListViewEditCommandFired(object sender, EventArgs args)
{
_detailPresenter.LoadUser(_listView.SelectedUser);
_detailPresenter.Show(); //modal
}
}
public class UserListForm : Form, IUserListView
{
public event EventHandler EditCommandFired;
public IUser SelectedUser { get { return gridView.FocusedRowHandle as IUser; } }
public void LoadUsers(List<IUser> users)
{
gridView.DataSource = users;
}
// other UI stuff
}
My problem is: I can only show the edit form once. As soon as I try to open it for a second time my View (the form) is disposed (System.ObjectDisposedException).
How do I fix that? Do I have the wrong approach here? Do I either cancel the form's close and just hide it and trust the garbage collector to collect it once the DetailPresenter is disposed? Do I create (new() up) a new presenter each time the Edit event is fired? I would then have to introduce some kind of factory as I somehow lose dependency injection. I'd appreaciate if someone could point out how the best practice in this case would look like and what I may be doing wrong here..
I was doing Winforms MVP a while ago so not sure if I can help, but the case my be as follows.
In my approach, the view was owning presenter, pseudo code:
MyForm form = new MyForm(new PresenterX);
form.Show(); //or showdialog
In this case instance is still there after closing.
In your case since presenter owns the view, its possible that once presenter is not used, GC disposes presenter and contained view.
Or even if presenter is still in use, since view is private GC may collect it once closed.
Try to debug in Release mode and see what happens with closed form instance.
EDIT:
Other idea is:
Create instance of view first and then pass to presenter
So approach that may fail (I don' see full code so guessing)
UserDetailPresenter p = new UserDetailPresenter(new YourView());
Try
YourForm view = new YourForm(); //as global variable, view should be reusable anyway
Somewhere in code
UserDetailPresenter p = new UserDetailPresenter(view);
p.Show(userInstance);
You're using one instance of DetailPresenter to show details for different objects. So you'll have to initialize the view of the DetailPresenter each time you want to show it, in your current implementation. This could be one way of doing it, the ListPresenter can inject a new instance of DetailsView everytime it asks the DetailPresenter to show it.
public class UserDetailPresenter : IPresenter<IUserDetailView>
{
private IDetailView _view;
public UserDetailPresenter()
{
}
public void Show(IUser user, IDetailView detailView)
{
_view = detailView;
InitializeView(user);
_view.Show();
}
}
Or another cleaner way could be some sort of ViewFactory to get a new instance of the view before showing it.
private IDetailViewFactory _detailViewFactory;
public UserDetailPresenter(IDetailViewFactory detailViewFactory)
{
_detailViewFactory = detailViewFactory;
}
public void Show(IUser user )
{
_view = _detailViewFactory.Resolve();//Some method to get a new view
InitializeView(user);
_view.Show();
}
But if you want to do it a bit differently, this is more passive view way.
In the ListPresenter:
private void OnListViewEditCommandFired(object sender, EventArgs args)
{
_listView.Show(_listView.SelectedUser);//tells view to show another view
}
In the ListView:
public ListView()
{
new ListPresenter(this); // initializes presenter
}
public void Show(IUser user)
{
new DetailsView(user); // creates a new view
}
In the DetailsView:
public DetailsView(IUser user)
{
new DetailsPresenter(this, user); //creates presenter
}
Finally:
public class UserDetailPresenter : IPresenter<IUserDetailView>
{
private IDetailView _view;
public UserDetailPresenter(IDetailView detailView, IUser user)
{
_view = detailView;
LoadUser(user);
_view.SomeProperty = _userData;//to populate view with data
_view.Show(); // tells the view to show data
}
}

passing to a form which is already open

What I'm trying to do it load some information from a database.
To do this I open a form that lists everything that can be loaded.
When you click load I want to pass the ID back to the original form.
However I can't seem to be able to call a method in that form.
Any help would be appreciated.
I would flip this around:
Make the selection form into a modal dialog that is created and displayed by the form where you want to load something
Expose the selection made in the dialog through a property or method in the dialog form
This way the selection form will be decoupled from the caller, and can be reused wherever it makes sense without the need to modify it.
In the selection dialog form class:
public string GetSelectedId()
{
return whateverIdThatWasSelected;
}
In the calling form:
using(var dlg = new SelectionDialogForm())
{
if (dlg.ShowDialog() == DialogResult.OK)
{
DoSomethingWithSelectedId(dlg.GetSelectedId());
}
}
You could add a property to your form class and reference it from your other form.
eg.
public class FormA : Form
{
private string _YourProperty = string.empty;
public string YourProperty
{
get
{
return _YourProperty;
}
set
{
_YourProperty = value;
}
}
}
public class FormB : Form
{
public void ButtonClick(object sender, EventArgs args)
{
using (FormA oForm = new FormA)
{
if (oForm.ShowDialog() == DialogResult.OK)
{
string Variable = oForm.YourProperty;
}
}
}
You just need to set your property on a button click on form A then you can access it from form B
}
Why not create a public property for the selected item in the dialog form, something like this.
public int SelectedItemId {get;private set;}
//In your item selected code, like button click handler..
this.SelectedItemId = someValue;
Then just open the form as a Dialog
//Open the child form
using (var form = new ChildForm())
{
if (form.ShowDialog(this) == DialogResult.OK)
{
var result = form.SelectedItemId;//Process here..
}
}
The proper way to do this is to introduce a Controller class which is used by both forms. You can then use a property on the Controller, when that is set will trigger the NotifiyPropertyChanged event.
see INotifyPropertyChanged for more info

Categories

Resources