In MVVM Navigation will viewmodel be taken from UserControl.DataContext of MainWindow - wpf

I use MVVM navigaion. I have Main Window, and I navigate to child user controls.
On the user controls I create instance of their viewmodel.
So I wonder, which instance of the viewmodel will be taken, the one created on the mainwindow, or
the one from the usercontrol, and is it problematic, that I create two instances?
Main Window with DataTemplates:
<Window.Resources>
<DataTemplate DataType="{x:Type cust:CustomersListViewModel}">
<cust:CustomerListView></cust:CustomerListView>
</DataTemplate>
<DataTemplate DataType="{x:Type dealer:DealersViewModel}">
<dealer:DealersView></dealer:DealersView>
</DataTemplate>
</Window.Resources>
I Create instances of child user controls :
Customers.CustomersListViewModel customersViewModel = new Customers.CustomersListViewModel();
Dealers.DealersViewModel dealersViewModel = new Dealers.DealersViewModel();
And I bind to child user controls with :
`
On the user controls I create viewmodel instance:
<UserControl.DataContext>
<local:CustomersListViewModel></local:CustomersListViewModel>
</UserControl.DataContext>
So is it problematic that I create to instances of view model?

You should create this:
<Window.Resources>
<StackPanel>
<cust:CustomerListView></cust:CustomerListView>
<dealer:DealersView></dealer:DealersView>
</StackPanel>
</Window.Resources>
and in your UserControls you should do this:
//Create Object of ViewModel in CustomerListView
DataContext = new Customers.CustomersListViewModel();
//Create Object of ViewModel in DealersView
DataContext = new Dealers.DealersViewModel();

You should not "create view-model instence" in the code of view.
In any case where appling DataTemplate by match the TargetType, the DataContext of the controls of DataTemplate will inherit the value from templated parent, because the TargetType of DataTemplate is matched by Type of the instance - this instance is that given object.
Value of DataContext => [Object] => GetType() => Match by TargetType => Apply DataTemplate, the DataContext is [Object]
A simple example:
<Window.Resource>
<DataTemplate TargetType="local:TypeA">
<TextBlock Text="{Binding ValueA}"/>
</DataTemplate>
<DataTemplate TargetType="local:TypeB">
<TextBlock Text="{Binding ValueB}"/>
</DataTemplate>
</Window.Resource>
<ContentControl>
<!-- The content is a TypeA object, will apply the TypeA DataTemplate -->
<!-- displayed 100 -->
<local:TypeA ValueA="100"/>
</ContentControl>
And what your code do:
<Window.Resource>
<DataTemplate TargetType="local:TypeA">
<TextBlock Text="{Binding ValueA}">
<TextBlock.DataContext>
<local:TypeA ValueA="50"/>
</TextBlock.DataContext>
</TextBlock>
</DataTemplate>
</Window.Resource>
<ContentControl>
<!-- The content is a TypeA object, will apply the TypeA DataTemplate -->
<!-- displayed 50, because you create a new instance of TypeA in the DataTemplate -->
<local:TypeA ValueA="100"/>
</ContentControl>

I have found a solution for intellisense by using d:DataContext="{d:DesignInstance myApp:Window2ViewModel, IsDesignTimeCreatable=True}">
So I dont need to create instance in runtime from the view

Related

Associate User Control with ViewModel class

When I define a DataTemplate inline, Visual Studio knows about the type I'm binding to, and properties in that type come up in autocomplete (for example in the code below I was able to select DisplayName from the autocomplete list inside the FirstViewModel template).
<DataTemplate DataType="{x:Type viewmodels:FirstViewModel}">
<StackPanel >
<Label Content="{Binding DisplayName}"/>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:SecondViewModel}">
<views:SecondView/>
</DataTemplate>
However, when the data template references an external control, as for SecondViewModel in the code above, when I'm in the file for the SecondView usercontrol, since it's just a control, the type isn't bound and the editor doesn't help me with anything.
I've tried wrapping my whole control (inside the UserControl element) in the same DataTemplate tag, but then my whole view just shows "System.Windows.DataTemplate".
<UserControl x:Class="Gui.Views.Tabs.ExamsTabViews.ExamInfoView"
xmlns:vm="clr-namespace:Gui.ViewModels"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<DataTemplate DataType="vm:ExamInfoViewModel">
<DockPanel VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<!-- contents of the template -->
</DockPanel>
</DataTemplate>
</UserControl>
Is there a way to achieve this kind of binding for the editor?
<DataTemplate DataType="{x:Type viewmodels:SecondViewModel}">
<views:SecondView/>
</DataTemplate>
when this DataTemplate is instantiated, there will be created SecondView and that SecondView will have a SecondViewModel in DataContext. So there is no need any DataTemplate in SecondViewModel control - bind to DataContext instead ({Binding SecondViewModelProperty}). To have design-time support for such binding use d:DataContext="{d:DesignInstance}:
<UserControl d:DataContext="{d:DesignInstance Type=vm:ExamInfoViewModel,
IsDesignTimeCreatable=True}" ...>

DataTemplate Binding depending on property type and with working property Binding

I check those articles about doing DataTemplate :
WPF DataTemplate Binding
WPF DataTemplate and Binding
WPF DataTemplate Textblock binding
and thoses about DataTemplate depending on property type :
WPF DataTemplate Binding depending on the type of a property
Dynamically display a control depending on bound property using WPF
I'm trying to display a property with different controls depending of the property value. I have this Xaml that is partialy working. I have 2 problems :
The property is displaying with the right control, but when I set the value it doesn't go back to the property. Means the "set" of My property is not call (but was before I creates the DataTemplate). I detect that the problem about setting the property is about the ="{Binding Path=.}" but I cannot find the solution to set it otherwise.
Also To be able to make it work, I had to "isolate" the Value into a single ViewModel so that the DataTemplate doesn't affect all the other control.
Can you help me find betters solutions to resolves those 2 problems?
Here is the xaml code of my View linked with MyContainerViewModel that has a "ChangingDataType" :
<UserControl >
<UserControl.Resources>
<!-- DataTemplate for strings -->
<DataTemplate DataType="{x:Type sys:String}">
<TextBox Text="{Binding Path=.}" HorizontalAlignment="Stretch"/>
</DataTemplate>
<!-- DataTemplate for bool -->
<DataTemplate DataType="{x:Type sys:Boolean}">
<CheckBox IsChecked="{Binding Path=.}" />
</DataTemplate>
<!-- DataTemplate for Int32 -->
<DataTemplate DataType="{x:Type sys:Int32}">
<dxe:TextEdit Text="{Binding Path=.}" MinWidth="50" Mask="d" MaskType="Numeric" HorizontalAlignment="Stretch"/>
<!--<Slider Maximum="100" Minimum="0" Value="{Binding Path=.}" Width="100" />-->
</DataTemplate>
<!-- DataTemplate for decimals -->
<DataTemplate DataType="{x:Type sys:Decimal}">
<!-- <TextBox Text="{Binding Path=.}" MinWidth="50" HorizontalAlignment="Stretch" />-->
<dxe:TextEdit Text="{Binding Path=.}" MinWidth="50" Mask="f" MaskType="Numeric" HorizontalAlignment="Stretch" />
</DataTemplate>
<!-- DataTemplate for DateTimes -->
<DataTemplate DataType="{x:Type sys:DateTime}">
<DataTemplate.Resources>
<DataTemplate DataType="{x:Type sys:String}">
<TextBlock Text="{Binding Path=.}"/>
</DataTemplate>
</DataTemplate.Resources>
<DatePicker SelectedDate="{Binding Path=.}" HorizontalAlignment="Stretch"/>
</DataTemplate>
</UserControl.Resources>
<ContentPresenter Content="{Binding MyChangingPropery}"/>
</UserControl>
More informations about 2 :
I wanted to have in a view a label and a property that changes depending of the object. Something like this :
<UserControl>
<UserControl.Resources>
<!-- ...DataTemplate here... -->
</UserControl.Resources>
<StackPanel>
<Label Content="Allo"/>
<ContentPresenter Content="{Binding MyChangingPropery}"/>
</StackPanel>
</UserControl>
But if I put the DataTemplate on this UserControl resources, it will also affect the Label "allo". So I had to create another view that contains the DataTemplate and MyChangingProperty so that the label Allo would not be affected. But the extra View created just for one property is kind of ugly to me, I'm sure there is a better way to isolate the DataTemplate so it can apply only to one UIControl.
<UserControl >
<StackPanel>
<Label Content="Allo"/>
<ContentPresenter Content="{Binding MyContainerViewModel}"/>
</StackPanel>
</UserControl>
Note : MyContainerViewModel here is linked with the first view described.
Thanks in advance!
One possible solution would be to use a DataTemplateSelector. You cannot bind primitive types using two way bindings because that would have to be somehow by reference via the DataTemplate which I think is not supported by WPF.
The DataTemplateSelector now selects the right DataTemplate based on the property type and searches for the right DataTemplate in the resources by name. This also solves your problem that your DataTemplates interacted with the Label.
So first you need to define a DataTemplateSelector that changes the DataTemplate based on the type of the property:
public class MyDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var fe = (FrameworkElement)container;
var prop = (item as MyViewModelType)?.MyChangingProperty;
if (prop is string)
return fe.FindResource("MyStringDT") as DataTemplate;
else if (prop is bool)
return fe.FindResource("MyBoolDT") as DataTemplate;
// More types...
return base.SelectTemplate(item, container);
}
}
Then you need to change the UserControl like this:
<UserControl>
<UserControl.Resources>
<local:MyDataTemplateSelector x:Key="MyDTSelector" />
<!-- DataTemplate for strings -->
<DataTemplate x:Key="MyStringDT">
<TextBox Text="{Binding MyChangingProperty, Mode=TwoWay}"
HorizontalAlignment="Stretch"/>
</DataTemplate>
<!-- DataTemplate for bool -->
<DataTemplate x:Key="MyBoolDT">
<CheckBox IsChecked="{Binding MyChangingProperty, Mode=TwoWay}" />
<!-- More DataTemplates... -->
</DataTemplate>
</UserControl.Resources>
<StackPanel>
<Label Content="Allo"/>
<ContentPresenter Content="{Binding MyContainerViewModel}"
ContentTemplateSelector="{StaticResource MyDTSelector}" />
</StackPanel>
</UserControl>
You can find a bit more information regarding the DataTemplateSelector here.
You can of course also set a DataType on this new DataTemplates but it isn't required because the x:Key makes them unique anyway. But if you want then it has to look like this:
<DataTemplate x:Key="MyStringDT" DataType="{x:Type local:MyViewModelType}">
In my opinion, the previously posted answer is overkill. While a DateTemplateSelector is a useful thing to know about, it seems unnecessary to me in this scenario.
But if I put the DataTemplate on this UserControl resources, it will also affect the Label "allo".
The reason it affects the Label object is that the Label object is a ContentControl, and so does the same template-matching behavior for content types as your own ContentPresenter element does. And you've set the content of the Label object to a string value. But you can put anything you want as the content for it.
The way to fix the undesired effect is to intercept that behavior by changing the content from a string object to an explicit TextBlock (the control in the template that a string object normally gets assigned). For example:
<UserControl>
<UserControl.Resources>
<!-- ...DataTemplate here... -->
</UserControl.Resources>
<StackPanel>
<Label>
<TextBlock Text="Allo"/>
</Label>
<ContentPresenter Content="{Binding MyChangingPropery}"/>
</StackPanel>
</UserControl>
In that way, you bypass the template-finding behavior (since TextBlock doesn't map to any template and can be used directly), and the content for the Label will just be the TextBlock with the text you want.
This seems like a lot simpler way to fix the issue, than either to create a whole new view or to add a DataTemplateSelector.

WP7 XAML Bind to property in parent

I'm writting an app in WP7 (Silverlight 3). I have a view model that looks like this
public class MainViewModel
{
public List<ActivityTypes> ActivityTypes{get;set;}
public RelayCommand LoadActivity{get;set;}
}
My pages datacontext is set to the view model and I have a listbox with it's item source set to the ActivityTypes collection. In the listbox I'm trying to render a list of buttons who's command is bound to the LoadActivity property on the viewmodel. RelayCommand is part of the MVVM Light toolkit in case you are wondering what it is.
The problem I am having is I can't find a way to bind my button command to the LoadActivity property as my listbox has it's itemsource set to the Activitytypes collection and this property is in the parent. I've read about FindAncester but it doesn't look like this is supported in Silverlight 3.
My XAML looks like this
<UserControl.Resources>
<DataTemplate x:Key="ActivityTypeListTemplate">
<StackPanel>
<Button Command="{Binding LoadActivity}"> <!--binding not found as we're in the collection-->
<TextBlock Text="{Binding Name}" FontSize="50"/>
</Button>
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="{StaticResource PhoneChromeBrush}">
<ListBox Margin="0" ItemsSource="{Binding ActivityTypes}" ItemTemplate="{StaticResource ActivityTypeListTemplate}"/>
</Grid>
What's the best way to code something like this?
There is no "direct" way to do this. You can set the Buttons DataContext to your MainViewModel (preferably as a StaticResource link) and the it would work.
Take a look at Bind to parent object in xaml

Get context in which a view is rendered?

In my WPF application I have a viewmodel class called CompanyViewModel.
Sometimes, an instance of this class is set as the DataContext of my main window, which is defined like this:
<window x:Class= ..... >
<Grid>
<ContentControl Content="{Binding }"></ContentControl>
</Grid>
</Window>
In this case I want a view to be used that displays all the properties of the viewmodel.
Other times, a ListView control has its itemsource set as a collection containing instances of CompanyViewModel. Here, I want a view to be used that renders only some important properties.
I have this in the resource dictionary of MainWindow.xaml:
<DataTemplate DataType="{x:Type vm:CompanyViewModel}">
<vw:CompanyView></vw:CompanyView>
</DataTemplate>
Is it possible to select a view for the viewmodel based on the context where the viewmodel is bound? For instance, to use CompanyView when displayed in the ContentControl of a window or when in a TabControl, and to use CompanyViewSmall where displayed in a ListView?
The DataTemplate to use is first looked for locally, and then looked for further up the Visual Tree hierarchy if it's not found.
Because of this, you can specify the DataTemplate to use further down the hierarchy to use something different than normal.
For example, the following will use the CompanyView anywhere the CompanyViewModel is in the visual tree, except in the specific ListView where the DataTemplate is specified as the smaller view.
<Window.Resources>
<DataTemplate DataType="{x:Type vm:CompanyViewModel}">
<vw:CompanyView />
</DataTemplate>
</Window.Resources>
<ListView>
<ListView.Resources>
<DataTemplate DataType="{x:Type vm:CompanyViewModel}">
<vw:CompanyViewSmall />
</DataTemplate>
</ListView.Resources>
</ListView>
You could also use an implicit style for the ListView telling it to use the smaller template in the .Resources, however this will apply the smaller view to any ListView, not just specific ones, and if you ever apply another style to a ListView you'll have to remember to inherit the default style to keep the smaller DataTemplate.
<Style TargetType="{x:Type ListView}">
<Style.Resources>
<DataTemplate DataType="{x:Type vm:CompanyViewModel}">
<vw:CompanyViewSmall />
</DataTemplate>
</Style.Resources>
</Style>

Change binding source back to view model inside container in XAML

I am using Silverlight 4 and the MVVM pattern.
My view model has two properties:
SomeProperty and
MyCommand
SomeProperty is a complex type and has a lot of subproperties. MyCommand is a property to handle commanding from a Button.
I have a child window (the view) with a Grid as the LayoutRoot which is bound to the SomeProperty property of the view model.
<Grid x:Name="LayoutRoot" DataContext="{Binding SomeProperty, Mode=TwoWay}">
...
</Grid>
However, inside the Grid I want to bind a Button's Command property to the MyCommand property of the view model:
<Button Command={Binding MyCommand} />
But this is not working because MyCommand is a property of the view model, and not a property of the view model's SomeProperty property. (When I click on the Button it does not execute the command.)
Anywho, is there a way using data binding in Silverlight 4 such that I can have a container UI element set its DataContext property explicitly, but then have a different control within the container reference a property that's a sibling (or parent or whatever) of the DataContext of the containing control?
My current workaround is to define the binding in the view's class, but I'd rather have it in the XAML.
Thanks
If you give your root element (ChildWindow, UserControl, whatever) a name, then you can use ElementName to get to the view model.
<UserControl x:Name="MyUserControl">
<Grid x:Name="LayoutRoot" DataContext="{Binding SomeProperty, Mode=TwoWay}">
<Button Command="{Binding MyCommand}" DataContext="{Binding DataContext, ElementName=MyUserControl}" />
</Grid>
</UserControl>
Or, here's another way to do the same thing.
<UserControl x:Name="MyUserControl">
<Grid x:Name="LayoutRoot" DataContext="{Binding SomeProperty, Mode=TwoWay}">
<Button Command="{Binding DataContext.MyCommand, ElementName=MyUserControl}" />
</Grid>
</UserControl>
You try add datacontext to binding? The datacontext have to point to your viewmodel, because the default data context is a parent control or parent data context, in this case your layout root.
See this
and this
I hope this help.
Regards.
I use a version of the BindableProxy described in this post:
http://weblogs.asp.net/dwahlin/archive/2009/08/20/creating-a-silverlight-datacontext-proxy-to-simplify-data-binding-in-nested-controls.aspx
Above your Grid (probably within the UserControl.Resources) you would create:
<UserControl.Resources>
<ns:BindableProxy x:Key="BindableProxy" />
<UserControl.Resources>
Then, in the button binding:
<Button Command="{Binding DataSource.MyCommand, Source={StaticResource BindableProxy}}" />

Resources