I have a WPF Window which contains few UserControls, those controls contain another. And now, what is the most principal way how to create ViewModel for this Window and where to bind it.
I do expect that one firstly needs to create ViewModel for each of sub-controls.
There are a few ways to do this.
Inject the VM
I would recommend this method.
If your window is created in the App class like
var window = new MyWindow();
window.Show();
I would assign the VM before showing the window:
var window = new MyWindow();
window.DataContext = GetDataContextForWindow();
window.Show();
If one of your controls needs an own view model assign the VM wile creating the control instance.
DataBind
If you want to set the VM of a control you can bind the DataContext property to an VM instance provided by the surrounding VM.
<Controls:MyControl DataContext={Binding MyControlsVm} />
Code Behind
You may set the VM using the init method in code behind like
public MyWindow()
{
InitializeComponent();
DataContext = CreateViewModel;
}
You may use a trick if you don't want to create a VM for your main page:
public MyWindow()
{
InitializeComponent();
DataContext = this;
}
and just use the code behind class as VM.
I see the view as a visual representation of the ViewModel so I like WPF picking the view based on the instance of the ViewModel it wants to render.
I call this the View Locator pattern, I use this method to instantiate my view because I have found it to be very simple to implement.
It basically puts an entry in the ResourceDictionary of your app that tells WPF to use an IValueConverter to look up and instantiate the View when it comes across a ViewModel.
So a working example would be:
In your app.xaml:
<Application x:Class="MyApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml" >
<Application.Resources>
<ResourceDictionary Source="Resources.xaml"/>
</Application.Resources>
</Application>
In resources.xaml:
<DataTemplate DataType="{x:Type vm:ViewModelBase}">
<ContentControl Content="{Binding Converter={StaticResource ViewModelConverter}}"/>
</DataTemplate>
Set the DataContext of your startup Window Control e.g.
public MainWindow : Window
{
InitializeComponent();
DataContext = new MainViewModel();
}
And you're pretty much done. So if you have a MainViewModel like so:
public class MainViewModel : ViewModelBase
{
public ChildViewModel1 Child1 {get;set;}
public ChildViewModel2 Child2 {get;set;}
}
and you have a UserControl that resolves to your MainViewModel like so:
<UserControl x:Class="MainView">
<StackPanel>
<ContentPresenter Content="{Binding Child1}"/>
<ContentPresenter Content="{Binding Child2}"/>
</StackPanel>
</UserControl>
So your ViewModelConverter will return an instance of the appropriate View without any extra effort on your part.
On the child controls issue, why wouldn't one of the properties of the root view model be an instance of the child view model that you would pass onto the child control? The other option would be a converter that converts the non-view model based property into an instance of the child view model (like an adapter pattern).
You might be interested in the sample applications of the WPF Application Framework (WAF). They show how composite Views and ViewModels can be instantiated and how they interact which each other.
Related
I am trying to create a simple dialog-type control that is based on the WPF Window class (Popup won't do the trick here).
In my app I register a DataTemplate in Application.Resources:
<Application.Resources>
<DataTemplate DataType="{x:Type local:EntitySelectorViewModel}">
<local:EntitySelector></local:EntitySelector>
</DataTemplate>
</Application.Resources>
In my Window control I set Window.Content and I expect WPF will set the ContentTemplate to an instance of EntitySelector based on the DataTemplate registration shown above:
[Export(typeof(EntitySelectorDialog))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public partial class EntitySelectorDialog : Window
{
[ImportingConstructor]
public EntitySelectorDialog(EntitySelectorViewModel vm)
{
InitializeComponent();
// DataContext = vm; // does not work
// EDIT: Per two answers shown below the following should work but it does not.
Content = vm;
}
}
The problem is that WPF does not resolve the ContentTemplate i.e. an instance of EntitySelector is not created. Furthermore, when I look at the XAML for EntitySelectorDialog I see that an instance of the shell has been injected
into the Window control (EntitySelectorDialog).
I don't know enough about Prism to know if I want to go with the flow and use the shell somehow or if I want to prevent Prism from injecting it at all. I don't think I have any need for it in this specific control so if it makes sense to just prevent Prism from injecting it I would prefer that route.
For the record I have tried removing the Prism attributes from my Window control and I new up the components manually. That appears to have no effect - Prism still manages to somehow inject the shell and my ContentTemplate is not resolved.
There is no XAML to show for the Window control (EntitySelectorDialog) except the Window declaration itself - I want the content to come entirely from the ContentTemplate (EntitySelector).
I've looked at this which may provide an answer but I don't know how to implement it without breaking the rest of the app:
Getting Unity to Resolve views in XAML
Set the Content of the window to a ContentControl and set or bind the Content property of this one to the view model:
[Export(typeof(EntitySelectorDialog))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public partial class EntitySelectorDialog : Window
{
[ImportingConstructor]
public EntitySelectorDialog(EntitySelectorViewModel vm)
{
InitializeComponent();
DataContext = vm;
Content = new ContentControl() { Content = vm };
}
}
You need to set vm to EntitySelectorDialog.Content to trigger WPF solve the DataTemplate according to the type. So you either add
Content = vm;
in constructor or add
Content = {Bing}
in Xaml.
I was wondering if it is possible to make a reference to an instance made in code behind of a class, with XAML.
For example:
I have two clasess, MainWindow and MainWindow_ViewModel.
MainWindow is defined like so:
public partial class MainWindow : Window
{
static public MainWindow wn;
private MainWindow_ViewModel _mwvm;
public MainWindow_ViewModel mwvm
{
get
{
return _mwnm;
}
}
public MainWindow()
{
InitializeComponent();
wn = this;
_mwvm = new MainWindow_ViewModel();
}
}
1) How could I, in MainWindow.xaml, make a reference to the property MainWindow.wn.mwvm without creating a new instance of MainWindow_ViewModel (the purpose to this is to do some binding without using DataContext but I need to use the same instance of MainWindow_ViewModel throughout the whole application)
2) Is it possible to make a reference to that same property (MainWindow.wn.mwvm), from a XAML other than MainWindow.xaml ?
Thanks again for all the support.
First of you should set the DataContext of your window to your ViewModel. Only that way will it's properties be visible in the MainWindow's XAML.
Example:
<Window DataContext="{Binding mwvm}">
<TextBlock Text="{Binding PathToYourPropertyInVM} />
</Window>
Allways make sure your viewmodels either implement INotifyPropertyChanged or use DependencyProperties for Bindable properties. That is required in order for the UI to "listen" to the changes in the properties values.
If you want a Globally accessible ViewModel, you should look into the ServiceLocator pattern. All of the MVVM framework implement it for you (MVVMLight, Caliburn, etc). It basically consist of a class where your register your components and it handles all the instatiation when it's required. You should do a little research about that topic because it's rather extensive and hard to put on a single answer.
Hope this helps ;)
To instantiate your class in the app.xaml :
Add the namespace in the app.xaml
xmlns:myApp="clr-namespace:WpfApplication1"
Create the object of your class
<myApp:MainWindow_ViewModel x:Key="mwvm" />
An example of use :
<TextBlock Text="{Binding Source={StaticResource mvvm}, Path=A_PROPERTY}"/>
This question is in reference to this link Opening new window in MVVM WPF.
I want to open new window in some service.
Following is my code
This is a window service which I am calling from ViewModel
public class WindowService : IWindowService
{
public void ShowWindow(object viewModel)
{
var win = new Window {Content = viewModel};
win.Show();
}
}
Following is my App.xaml code
<DataTemplate DataType="{x:Type viewModel:MainViewModel}" >
<viewModel:ChildWindow />
</DataTemplate>
Now this works fine for all windows which have different ViewModel.
But when I want to open another window which is using same view model but their view is not same,I can not define data template of same type in App.xaml.
How do I open multiple new window which have same ViewModel?
Should I create different ViewModel for each window?
I have shown an alternative way of opening and closing windows here.
You could add a DependencyProperty e.g. DataContext to the OpenCloseWindowBehavior, use it to pass the ViewModel, and hook up the window's DataContext to this ViewModel in the code of the behavior. Tell me if you need more help.
<local:OpenCloseWindowBehavior WindowType="local:YellowWindow" Open="{Binding YellowOpen, Mode=TwoWay}" DataContext="{Binding SomeViewModel}" />
I have a UserControl who's DataContext is being set to an instance of a ViewModel (using MVVM). But, I have controls within the UserControl which need to be bound to properties that only pertain to the view (which is why I placed them in code behind). I'm not sure how to bind this in xaml appropriately:
Note: SelectedOrderType is a property on the View-Model, and OrderTypes is a property on the UserControl itself.
<UserControl x:Class="MyNamespace.OrderControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="OrderUserControl">
<Grid>
...
<ComboBox ItemsSource="{Binding Path=OrderTypes, ElementName=OrderUserControl}"
SelectedValue="{Binding Path=SelectedOrderType}"
SelectedValuePath="OrderTypeCode"
DisplayMemberPath="OrderTypeName" />
</Grid>
</UserControl>
public partial class OrderControl : UserControl
{
public OrderControl()
{
InitializeComponent();
OrderTypes = ...;
}
public IReadOnlyCollection<OrderTypeInfo> OrderTypes { get; private set; }
}
Also, I know I can simply create a property on the View-Model, and I get that some people would suggest that that would be the correct place to put it... but I really would like to know how I could do what I'm attempting to do if not for this scenario, maybe for other scenarios in the future?
I may be wrong but would you not need to make a dependency property on your user control for "SelectedOrderType" and bind the the View Model to that property not bind directly to the view model from the user control.
That way your UserControl is not dependent on the view model?
Edit:
I think you could set it up the way you have it, but the binding for SelectedOrderType would need to be something like {Binding Path=DataContext.SelectedOrderType, ElementName=OrderUserControl}
I've built a WPF UserControl View/ViewModel pair: the view XAML handles the layout and bindings, and the ViewModel handles the logic, in-line with the recommended MVVM pattern.
I would like to be able to re-use this as a control.
How do I hide/encapsulate the ViewModel associated with the view, so that I can use the control as I would a standard control [such as a button] ?
i.e. How do I hide the control's viewmodel ?
depends on how you bind ViewModel class to the control.
if you do like this:
YourControl()
{
DataContex = new ViewModel();
}
then I don't see any problems. add reference to your control and use it.
You can create your ViewModel as a StaticResource within your XAML. The problem with setting the DataContext to your ViewModel is that you can't use that you can no longer use your DataContext from the window or page you in which you use the control.
In your XAML declare your ViewModel:
<myNS:MyViewModel x:Key="ViewModel />
Reference your view model within your XAML:
<TextBlock Text="{Binding Source={StaticResource ViewModel}, Path=TextToBind}" />
In your Code Behind you can access and initialize quickly, I usually make a property for easy reference to my view model.
private MyViewModel viewModel
{
get { return this.Resources["ViewModel"] as MyViewModel; }
}