WPF best practice to get current MainWindow instance? - wpf

I got a warning that this may be a subjective question and might be closed, but I'm going to ask anyway.
I'm basically trying to access a button on my MainWindow in a WPF application from a UserControl that gets loaded up from within the MainWindow.
I'm currently accessing it like this from the UserControl's code behind:
((MainWindow)Application.Current.MainWindow).btnNext
But it does look messy, and from what I've read is not considered a best practice. Anyone able to provide an answer that constitutes a best practice for Accessing controls / properties from the current instance of a MainWindow - or any other active windows / views for that matter?

You can get a reference to the parent window of the UserControl using the Window.GetWindow method. Call this once the UserControl has been loaded:
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
this.Loaded += (s, e) =>
{
MainWindow parentWindow = Window.GetWindow(this) as MainWindow;
if (parentWindow != null)
{
//...
}
};
}
}
You could also access all open windows using the Application.Current.Windows property:
MainWindow mainWindow = Application.Current.Windows.OfType<MainWindow>().FirstOrDefault();
Which one to use depends on your requirements. If you want a reference to the application's main window for some reason, you could stick with your current approach. If you want a reference to the parent window of the UserControl, using the Window.GetWindow method would be better.
The best practice is generally to use the MVVM design pattern and bind UI controls to source properties of a view model that may be shared by several views. But that's another story. You could refer to the following link for more information about the MVVM pattern: https://msdn.microsoft.com/en-us/library/hh848246.aspx

Related

Caliburn.Micro - why uses UserControl instead of Window

My question is exactly like in the title.
I'm starting with Caliburn.Micro for MVVM approach (which also is new for me) and in every tutorial the first step is to remove the default MainWindow.xaml file and create a new UserControl file. Why is that? UserControl does not even accept a Title. Isn't it possible to build application using normal Windows? I already tried that, but with every launch I get error "Cannot find view for ViewModel", although both MainView.xaml and MainViewModel.cs are present. When I created a pair of USerControl and ViewModel for it, everything started to work as expected. So again, why Windows don't work?
It wouldn't really be a problem, but I'm thinking that some additions like Modern UI themes for WPF might not work without a window. I'm not sure of that.
Probably one solution would be to display a defined UserControl View inside of a Window, but it's just a workaround.
You could create your own custom shell window by creating a custom WindowManager:
public class CustomWindowManager : WindowManager
{
protected override Window CreateWindow(object rootModel, bool isDialog, object context, IDictionary<string, object> settings)
{
Window window = new Window();
window.Title = "custom...";
return window;
}
}
...that you register in your bootstrapper:
public class HelloBootstrapper : BootstrapperBase
{
...
protected override void Configure()
{
_container.Singleton<IWindowManager, CustomWindowManager>();
...
}
}

Main Window starts to depend on too many things, but I don't want a service locator

I am creating a WPF application. Naturally my entry point is MainWindow.xaml, which is opened up by App.xaml
var mainWindow = container.Resolve<MainWindow>();
Application.Current.MainWindow = mainWindow;
Application.Current.MainWindow.Show();
I am using Dependency Injection and so far all the dependencies are passed as parameters in the ctor of the MainWindow's View Model.
i.e. my Main Window is
public partial class MainWindow : MetroWindow
{
private readonly MainWindowModel mainViewModel;
public MainWindow(MainWindowModel mainViewModel)
{
and its View model is:
public MainWindowModel(IDataRepository dataRepo, ICommand command1, ICommand command2, etc ...)
{
However, I am now starting to realize this might be a problem. Given that the MainWindow is the entry point to the entire app, it seems like any dependency, anywhere in the application will have to first pass through the MainWindow View Model constructor. This seems crazy.
I am coming from the background of ASP.NET MVC and there we have Controllers, which receive only the dependencies that they need. i.e. the concept of a main entry point there is missing and this makes things easier and more manageable.
Here is an example in my WPF app. A control, on the Main View needs to open up a dialog. This dialog is another Window and of course that window receives its ViewModel in its ctor. To me, it seems like to be able to resolve the dialog properly, I need to pass it through the Main Window View Model ctor first, keep it as private readonly field of the Main Window View Model and launch it when necessary. Ok, but what if I have 100 dialogs. That's just one of the examples. I have such issue with the ICommand implementations too.
To sum up my question:
How do I manage the dependencies in WPF properly, without using the Service Locator anti-pattern and without passing every single abstraction through the ctor of the main window view model? I could very easily pass a Container around and let, e.g., the create ABC command solve the ABCDialog before opening it, but I feel this will cause more issues than it would solve.
I am probably doing something wrong. Please advise me what is the best practice.

Notify button clicked in popup window to main window on wpf

I have two windows ,say window1 and window2(this one should be a pop up) on WPF.
What I want to do is, when a button in popup window(window2) is clicked,
I want to run method in window1.
I can achieve this by passing window1 to windows2, but I think it's not an memory-efficient way.
I have red article about routedCommand, but it's hard to understand.
I'm working on c# and any help is appreciated
Thank you
Quite often I have a static property for Current on my MainViewModel (or which ever ViewModel), and I set that property in the constructor for the ViewModel. Then from anywhere else in the application, I can get a reference to the ViewModel in question.
On the ViewModel
public MainViewModel()
{
Current = this;
}
public static MainViewModel Current { get; set; }
Anywhere else in the application:
MainViewModel.Current.DoSomething();
Routed Command
Routed commands are typically databound, and thus the command logic depends on which Data Context it is written on. If Window1's DataContext is MainViewModel, and Window2's DataContext is SecondViewModel, in order to have a button on Window2 execute a command on MainViewModel, you will have to have a reference to that instance of MainViewModel as the DataContext for the button in question.

How to use Caliburn Micro in a WinForms app with one WPF form

We have a (massive) legacy WinForms app which, through a menu item, opens up a WPF form. This WPF form will host an Infragistics grid, and some buttons/drop-downs.
This lone WPF form represents the nascent stage of a migration to WPF. Later on, more components of the app will move to WPF, and ultimately the entire app itself.
As part of the migration, we would like to use Caliburn Micro. Hence, it would be nice if we could start by using it with this lone WPF form.
Can someone please provide some pointers on how to use Caliburn Micro with the WPF form?
Or perhaps tell me why it may not make sense to use Caliburn Micro just yet?
The documentation I've read so far involves boot strappers that ensure the application starts with the desired root view model, rather than the scenario above.
Many thanks!
After much Googling and going through the Caliburn Micro source code, I've come up with an approach that works in a sample test application. I can't post the test application here for certain reasons, but here's the approach in a nutshell.
Create a WinForm with a button.
On button click, show a ChildWinForm
In the load handler of the ChildWinForm:
// You'll need to reference WindowsFormsIntegration for the ElementHost class
// ElementHost acts as the "intermediary" between WinForms and WPF once its Child
// property is set to the WPF control. This is done in the Bootstrapper below.
var elementHost = new ElementHost{Dock = DockStyle.Fill};
Controls.Add(elementHost);
new WpfControlViewBootstrapper(elementHost);
The bootstrapper above is something you'll have to write.
For more information about all it needs to do, see Customizing the Bootstrapper from the Caliburn Micro documentation.
For the purposes of this post, make it derive from the Caliburn Bootstrapper class.
It should do the following in its constructor:
// Since this is a WinForms app with some WPF controls, there is no Application.
// Supplying false in the base prevents Caliburn Micro from looking
// for the Application and hooking up to Application.Startup
protected WinFormsBootstrapper(ElementHost elementHost) : base(false)
{
// container is your preferred DI container
var rootViewModel = container.Resolve();
// ViewLocator is a Caliburn class for mapping views to view models
var rootView = ViewLocator.LocateForModel(rootViewModel, null, null);
// Set elementHost child as mentioned earlier
elementHost.Child = rootView;
}
Last thing to note is that you'll have to set the cal:Bind.Model dependency property in the XAML of WpfControlView.
cal:Bind.Model="WpfControls.ViewModels.WpfControl1ViewModel"
The value of the dependency property is used passed as a string to Bootstrapper.GetInstance(Type serviceType, string key), which must then use it to resolve the WpfControlViewModel.
Since the container I use (Autofac), doesn't support string-only resolution, I chose to set the property to the fully qualified name of the view model. This name can then be converted to the type, and used to resolve from the container.
Following up on the accepted answer (good one!), I'd like to show you how to implement the WinForms Bootstrapper in a ViewModel First approach, in a way that:
You won't have to create a WPF Window and,
You won't have to bind directly to a ViewModel from within a View.
For this we need to create our own version of WindowManager, make sure we do not call the Show method on the Window (if applicable to your case), and allow for the binding to occur.
Here is the full code:
public class WinformsCaliburnBootstrapper<TViewModel> : BootstrapperBase where TViewModel : class
{
private UserControl rootView;
public WinformsCaliburnBootstrapper(ElementHost host)
: base(false)
{
this.rootView = new UserControl();
rootView.Loaded += rootView_Loaded;
host.Child = this.rootView;
Start();
}
void rootView_Loaded(object sender, RoutedEventArgs e)
{
DisplayRootViewFor<TViewModel>();
}
protected override object GetInstance(Type service, string key)
{
if (service == typeof(IWindowManager))
{
service = typeof(UserControlWindowManager<TViewModel>);
return new UserControlWindowManager<TViewModel>(rootView);
}
return Activator.CreateInstance(service);
}
private class UserControlWindowManager<TViewModel> : WindowManager where TViewModel : class
{
UserControl rootView;
public UserControlWindowManager(UserControl rootView)
{
this.rootView = rootView;
}
protected override Window CreateWindow(object rootModel, bool isDialog, object context, IDictionary<string, object> settings)
{
if (isDialog) //allow normal behavior for dialog windows.
return base.CreateWindow(rootModel, isDialog, context, settings);
rootView.Content = ViewLocator.LocateForModel(rootModel, null, context);
rootView.SetValue(View.IsGeneratedProperty, true);
ViewModelBinder.Bind(rootModel, rootView, context);
return null;
}
public override void ShowWindow(object rootModel, object context = null, IDictionary<string, object> settings = null)
{
CreateWindow(rootModel, false, context, settings); //.Show(); omitted on purpose
}
}
}
I hope this helps someone with the same needs. It sure saved me.
Here are somethings you can start with
Create ViewModels and inherit them from PropertyChangedBase class provided by CM framework.
If required use the EventAggregator impelmentation for loosly coupled communication \ integration
Implement AppBootStrapper without the generic implementation which defines the root view model.
Now you can use the view first approach and bind the view to model using the Bind.Model attached property on view. I have created a sample application to describe the approach here.

Using Base classes in WPF

I have problem with base classes in WPF. I try to make a base class with some base elements, so that other windows can inherit these components. But all that i have, when I inherit base class is only empty window, without these elements. For better understanding i put my code here:
using XSoftArt.WPFengine;
namespace XSoftArt
{
public class WindowBase : Window
{
public WindowBase()
{
}
}
Code of the Windows, whitch inherits WindowBase:
namespace XSoftArt.WPFengine
{
public partial class NewAbility : WindowBase
{
public NewAbility()
{
base.ChildForm = this; InitializeComponent();
}
}
}
Or maybe someone can put an working example or link with implemented base classes in wpf?
Thanks
I don't think you really need to do what you are doing, but it is feasible. I think you are just forgetting to call the base class constructor.
using XSoftArt.WPFengine;
namespace XSoftArt
{
public class WindowBase : Window
{
//call base ctor
public WindowBase() : base()
{
}
}
}
You'll need to do this from your inherited classes as well:
namespace XSoftArt.WPFengine
{
public partial class NewAbility : WindowBase
{
public NewAbility() : base()
{
base.ChildForm = this; InitializeComponent();
}
}
}
And if you also have a XAML-defined view, you'll need to make sure your view is a WindowBase. To do this, change this:
<Window x:Class="MyApp.MyView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
...
>
<Grid>
</Grid>
</Window>
To this:
<local:WindowBase x:Class="MyApp.MyView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:XSoftArt;"
...
>
<Grid>
</Grid>
</local:WindowBase>
If you look at this class in Reflector you will see that the constructor calls the Window class's own "Initialize()" method, which sets a lot of things in motion. Specifically it appears to hook itself up to the Dispatcher, which is the work queue for all UI events.
In particular, you want to ensure that the InitializeComponent() method of the base class is called - this is the function that creates the controls that you defined in XAML.
Making a derived class is great if you want to inherit both controls and behaviour, but consider using Templates for a more flexible way of managing a common set of controls.
I don't think I'd ever use inheritance in WPF the way you're trying to use it.
I'll try and take a stab at answering your question. If I'm understanding you correctly, you're trying something like this:
You're creating a window that has both a XAML file and a code-behind.
You're adding "base elements" to the XAML for your window... I'm not sure what you mean by "base element", but I'm going to assume you mean you're adding UI elements to your window.
You're creating another window that "derives" from your first window in the code-behind, and the problem is that you're not seeing the UI elements on it from your "base" window.
If that is what you want to accomplish with WPF, I'd personally recommend against it, just because I'm personally not a fan of inheritance and have seen firsthand the dangers of letting inheritance get out of hand.
What you could try instead is organize your "base" UI elements into WPF UserControls. This tutorial might be able to guide you in the right direction. Good luck!

Resources