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}" />
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 am working on refactoring an application to use property injection. This has required me to create the views from the code behind like such. Originally everything was created in XAML and bindings were set to the view models by static resources. This gave no control over injecting the service for getting data into the view model.
This is the App.xaml.cs:
public App()
{
this.MainWindow = new MainWindow(new MainWindowViewModel(new DbDataService()));
MainWindow.Show();
}
MainWindowViewModel is set as the datacontext.
App.xaml contains this resource for viewing the product type class in a listbox:
<DataTemplate x:Key="DataTemplate" DataType="{x:Type classes:Product}">
The MainWindow has a tab for each view. I have a tab's view created like this in MainWindow's constructor:
ProductsTab.Content = new MainView(mainWindowViewModel);
When I do this, I get the following error in the MainView on this line:
<dxe:ListBoxEdit Grid.Column="0" ItemTemplate="{StaticResource DataTemplate}" ...etc... />
"'Provide value on 'System.Windows.StaticResourceExtension' threw an exception.' Line number '37' and line position '31'."
What I have tried:
I have tried creating the Tab's view in XAML and setting its data context to the parents, however, I still got this exception.
I have tried changing my static resources to dynamic resources, which causes no error at runtime, but the list box does not display the information correctly.
What I need to know:
How to use static/dynamic resources with decency injection.
If there is a better approach to this problem, such as creating the templates in code and injecting them into the views?
Some of my concerns:
I want to add dependency injection to this application, however, I feel like the approach I'm using not only breaks the resources but breaks the MVVM pattern.
Try to create and call your MainWindow in the OnStartup method of your App class:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
this.MainWindow = new MainWindow(new MainWindowViewModel(new DbDataService()));
MainWindow.Show();
}
}
This should work provided that you define the "DataTemplate" resource in your App.xaml (or in a ResourceDictionary that you merge from App.xaml).
In a CM WPF application I have following items:
<Window> <!-- Shell Window -->
<ContentControl x:Name="MainRegion">
<ContentControl x:Name="SearchRegion">
<Window>
Views:
MainRegionView
SearchRegionView
View Models:
MainRegionViewModel //implements Conductor<Screen>
SearchRegionViewModel //implements Conductor<Screen>
I neet to switch the view of SearchRegionViewModel from MainRegionViewModel.
Following code is executed inside MainRegionViewModel.
ViewModelTest test = new ViewModelTest();
//Calling the method from object reference
searchRegionViewModel.ActivateItem(test);
Problem:
the view is not loaded in SearchRegionView.
How can i load the view to SearchRegion?
Your MainRegionViewModel & SearchRegionView need to implement Screen and your ShellViewModel is the one that need to implement the Conductor. After that, you need to bind the ActiveItem in the XAML (ShellView.xaml).
<ContentControl x:Name="ActiveItem"></ContentControl>
Inside your ShellViewModel use Activate(YourViewModel).
Here is an Example
If you want to change the ActiveItem inside your MainRegionViewModel, You need to look at the EventAggregator to publish a message to your ShellViewModel to Activate the desire ViewModel
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.
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; }
}