Binding datacontext to another viewmodel when design mode - silverlight

I would like to bind my silverlight view to another datacontext when I'm in design mode. With the locator pattern, I can do something like :
<UserControl or Window Or Else
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
DataContext="{Binding MyViewModelStatic, Source={StaticResource Locator}}"
d:DataContext="{Binding Path=MyViewModelDesign, Source={StaticResource Locator}}">
If I'm not using this pattern and use a declarative way like this :
<UserControl.DataContext>
<local:MyViewModel />
</UserControl.DataContext>
How can I set the d: to another viewmodel ?
Thanks in advance for any help

This should do the trick:
<d:DesignProperties.DataContext>
<sample:SampleViewModel />
</d:DesignProperties.DataContext>

Related

How to assign DataContext from parent View's ViewModel (Property)

EDIT:
Here is a small sample Solution (VS 2013) showing my exact problem: nakkvarr.net/TestApp.zip
I have two views:
MainView
EmployeeView
I want my EmployeeView to reference the MainViewModel property "employees".
I tried this:
<DockPanel DataContext="{Binding DataContext.MainViewModel,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type EmployeeLisViewModel}}}">
But all I get is the Error: EmployeeListViewModel ist not supported in WPF ?!
(EmployeeListViewModel is also the name of the property of my MainViewModel)
The thing is:
My MainView has some menu items. One is for sorting the employee list, which is inside my UserControl. My UserControl ViewModel contains the Commands for sorting.
EDIT:
I changed it to vm:EmployeeListViewModel ... now the error is gone...but now I don't seem to get access to the ViewModel :/
<ia:InvokeCommandAction Command="{Binding LoadEmployeesCommand}"
Does not trigger, no emplyees get loaded. When I use the ViewModel directly, it works just fine.
MainWindow.xaml
<Window x:Class="de.XXX.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:de.XXX.Views"
xmlns:vm="clr-namespace:de.XXX.ViewModel"
Icon="/Images/App.ico"
Style="{StaticResource MainWindowStyle}"
Title="MainWindow">
<DockPanel DataContext="{Binding MainViewModel, Source={StaticResource Locator}}">
<Menu DockPanel.Dock="Top">
...
</Menu>
<controls:EmployeeListView DockPanel.Dock="Bottom" DataContext="{Binding EmployeeListViewModel}" />
</DockPanel>
</Window>
EmployeeListView.xaml //not model, copy & paste error x_X
<UserControl x:Class="de.XXX.Views.EmployeeListView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:de.XXX.ViewModel"
xmlns:ia="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
d:DesignHeight="350" d:DesignWidth="350">
<DockPanel DataContext="{Binding MainViewModel,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type vm:EmployeeListViewModel}}}">
<ListBox DockPanel.Dock="Bottom" x:Name="EmployeeList" ItemsSource="{Binding EmployeeList}" ScrollViewer.CanContentScroll="False" />
<ia:Interaction.Triggers>
<ia:EventTrigger EventName="Loaded">
<ia:InvokeCommandAction Command="{Binding LoadEmployeesCommand}" />
</ia:EventTrigger>
</ia:Interaction.Triggers>
</DockPanel>
</UserControl>
If you modify your SubControl to below, this should work.
<Grid DataContext="{Binding Path=DataContext.SubVM, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}">
I have a hint on what you are trying to achieve and I guess you have missed some details on Binding especially on other underlying properties needed to be set. This part is working based on the given code. But then, there are numerous ways in making it better. Good luck.
I think there might be a combination of misunderstandings here! Your UserControl has
x:Class="de.XXX.Views.EmployeeListView"
defined as the class name in xaml. However, you're also saying that the file is named EmployeeListViewModel.xaml? A .xaml file should never be named a viewmodel. I believe you should have MainWindow.xaml, EmployeeListView.xaml, MainWindowViewModel.cs, and EmployeeListViewModel.cs, is this the case?
Also, in your MainWindow.xaml, you're already setting the datacontext of your EmployeeListView usercontrol:
<controls:EmployeeListView DockPanel.Dock="Bottom" DataContext="{Binding EmployeeListViewModel}" />
This implies to me that your class structure is that MainViewModel contains an instance of the EmployeeListViewModel... is this true?
If all the above is true, then this part of your UserControl xaml does not make sense:
<DockPanel DataContext="{Binding MainViewModel,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type vm:EmployeeListViewModel}}}">
Whether EmployeeListViewModel is actually your view and not your viewmodel, the property "MainViewModel" does not exist on EmployeeListViewModel, so the binding will never work.
If you're trying to have a reference to your Window's DataContext, MainViewModel, within your UserControl, a possible way to do this is:
MainWindow.xaml:
<controls:EmployeeListView DockPanel.Dock="Bottom" DataContext="{Binding}" />
This should set the DataContext of your EmployeeListView to MainViewModel. Then, in your EmployeeListView, you can do this:
<UserControl x:Class="de.XXX.Views.EmployeeListView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:de.XXX.ViewModel"
xmlns:ia="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
d:DesignHeight="350" d:DesignWidth="350">
<DockPanel
<ListBox DockPanel.Dock="Bottom" x:Name="EmployeeList" ItemsSource="{Binding EmployeeList}" ScrollViewer.CanContentScroll="False" />
<ia:Interaction.Triggers>
<ia:EventTrigger EventName="Loaded">
<ia:InvokeCommandAction Command="{Binding EmployeeListViewModel.LoadEmployeesCommand}" />
</ia:EventTrigger>
</ia:Interaction.Triggers>
</DockPanel>
</UserControl>
There's a lot of assumptions about your views and viewmodels I made for this answer. I hope this at least helps. If it doesn't make any sense, please post your viewmodels as well :)

Do DataContext assign on XAML pointing to its code behind class

I'm developing a Windows Phone 8 application with a User Control.
On this user control, I want to do the datacontext on XAML pointing to its codebehind. Now I'm doing it on the constructor:
public CustomOptionButton()
{
InitializeComponent();
LayoutRoot.DataContext = this;
}
But, How can I do it on XAML?
Try this:
<UserControl Name="LayoutRoot"
DataContext="{Binding RelativeSource={RelativeSource Self}}" ... />
or:
<UserControl.DataContext>
<local:TestViewModel />
</UserControl.DataContext>
or:
<UserControl.Resources>
<local:MyViewModel x:Key="TestViewModel" />
</UserControl.Resources>
<UserControl.DataContext>
<Binding Source="{StaticResource TestViewModel}" />
</UserControl.DataContext>
when i'm using current datacontext in my custom controls, this is what i am doing:
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
d:DataContext="{Binding}"
EDIT
As #WiredPrairie pointed out i misunderstood the question, so this is more easy way and aleternative to what #AnatoliyNikolaev suggest
x:Name="_this">
<Grid x:Name="LayoutRoot" DataContext="{Binding ElementName=_this}">

How to retrieve a control's viewmodel after creation?

I have a Window and a UserControl. The UserControl creates its own viewmodel like this:
<UserControl x:Class="UiInteraction.UserControl3"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:UiInteraction"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300">
<UserControl.DataContext>
<local:UserControl3Vm/>
</UserControl.DataContext>
<StackPanel>
<TextBlock Text="{Binding String1}"/>
</StackPanel>
</UserControl>
When the Window instantiates the UserControl I want the viewmodel of the Window to be able to retrieve the viewmodel of the UserControl.
<Window x:Class="UiInteraction.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:UiInteraction"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainWindowVm/>
</Window.DataContext>
<StackPanel>
<local:UserControl3 DataContext="{Binding UserControl3Vm, Mode=OneWayToSource}"/>
</StackPanel>
</Window>
The Window's viewmodel has a publicly settable property of type object. With the DataContext binding I'm expecting that once the UserControl3 is created the value of its DataContext (which is a reference to its viewmodel) would be assigned to the UserControl3Vm property on the Window's viewmodel.
What actually happens is that the Window.UserControl3Vm property setter is called with the value null.
Why is this happening, and what is the best way to achieve what I have in mind?
I know it would be easier to instantiate the viewmodel for the UserControl as a property on the viewmodel for the Window and have the UserControl simply bind onto that, (and that would also minimize the coupling of the views to their viewmodels). But where I work they are a bit nutty and prefer view first MVVM instead of viewmodel first, so I'm looking for the most decoupled way to enable viewmodels to collaberate effectively when the viewmodels are instead created by their views.
I don't think it will work to use a OneWayToSource binding without some code-behind.
Initially, your UserControl.DataContext is set to an instance of UserControl3vm, however you are replacing UserControl3vm with a Binding, so your original UserControl3vm is no longer referenced anywhere.
For a OneWayToSource binding to work, you'd have to first set the DataContext to your OneWayToSource binding, and then set the binding's Source to a new instance of UserControl3vm from inside your UserControl.
If I remember correctly, you can obtain the binding using BindingOperations.GetBindingExpression, and update it's DataItem property. You can't simply set the UserControl.DataContext because it would overwrite your OneWayToSource binding.
Personally I would just do it in the code-behind the Loaded event
If they're insisting on View-First MVVM, then the View is controlling the application flow and I don't see any reason why you should keep application logic out of the View's code-behind.
So just set your Window.DataContext.UserControl3Vm property to UserControl3.DataContext in the Loaded event :)
<Window x:Name="MyWindow"
Loaded="MyWindow_Loaded"
... >
<StackPanel>
<local:UserControl3 x:Name="MyUserControl" />
</StackPanel>
</Window>
void MyWindow_Loaded(object sender, EventArgs e)
{
((MainWindowVm)MyWindow.DataContext).UserControl3Vm
= MyUserControl.DataContext;
}
This is possible in XAML using some workaround(to hack access of DataContext of host element). The approach is mentioned here. It uses Freezables.
The XAML is
<Window x:Class="VM2VMBindingInXaml.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vw="clr-namespace:VM2VMBindingInXaml.View"
xmlns:vm="clr-namespace:VM2VMBindingInXaml.ViewModel"
Title="MainWindow" Height="350" Width="525" >
<Window.Resources>
<vm:UserControl1ViewModel x:Key="childVM"></vm:UserControl1ViewModel>
<vm:DataResource x:Key="childVmBinding" BindingTarget="{Binding ElementName=child, Path=DataContext}"/>
</Window.Resources>
<Window.DataContext>
<vm:MainWindowViewModel x:Name="mainViewModel" >
<vm:MainWindowViewModel.ChildViewModel>
<vm:DataResourceBinding DataResource="{StaticResource childVmBinding}">
</vm:DataResourceBinding>
</vm:MainWindowViewModel.ChildViewModel>
</vm:MainWindowViewModel>
</Window.DataContext>
<Grid>
<vw:UserControl1 x:Name="child" DataContext="{Binding Source={StaticResource ResourceKey=childVM}}">
</vw:UserControl1>
</Grid>
</Window>

WPF ListBox ItemsSource StaticResource/Binding question

Given the following code:
<Window x:Class="WpfApplication76.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:col="clr-namespace:System.Collections;assembly=mscorlib"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<CollectionViewSource x:Key="myCol">
<CollectionViewSource.Source>
<col:ArrayList>
<ListBoxItem>Uno</ListBoxItem>
<ListBoxItem>Dos</ListBoxItem>
<ListBoxItem>Tres</ListBoxItem>
</col:ArrayList>
</CollectionViewSource.Source>
</CollectionViewSource>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{StaticResource myCol}" />
<ListBox ItemsSource="{Binding Source={StaticResource myCol}}" />
</Grid>
</Window>
In this example, the
<ListBox ItemsSource="{StaticResource myCol}" />
Gives me an error complaining that it cannot bind to a "CollectionViewSource" object.
But the other listbox:
<ListBox ItemsSource="{Binding Source={StaticResource myCol}}" />
binds perfectly fine.
So my question is why does one work and the other one does not? AT the end, aren't both ItenSources being set to the same "CollectionViewSource" object?
Thank you.
The ItemsSource property is of type IEnumerable. A CollectionViewSource is not an IEnumerable. CollectionViewSource's View property will give you an IEnumerable.
When you Bind to a CollectionViewSource the Binding is smart enough to grab the View property and actually bind to that. Maybe CollectionViewSource has a [DefaultBindingProperty] on it.
It boils down to the fact that when you go through the Binding you don't actually bind to the CollectionViewSource, but its View property.

Silverlight - Setting DataContext in XAML rather than in constructor?

How can I set the DataContext on my Grid in XAML, instead of in the constructor?
Here is how I do it in the constructor (LayoutRoot is the XAML Grid defined in the XAML):
this.LayoutRoot.DataContext = this.HPVM;
I would prefer to do it right in the XAML, but I do not know how to reference the HPVM object in XAML. HPVM is a public property on the USerControl class.
It works fine as listed above, but again, I just want to know how to properties of the UserControl class in XAML, rather than always having to do it in code.
Here is all the relevant code:
<UserControl x:Class="SilverlightApplication1.SLHolePattern" x:Name="HolePatternsControl"
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:controls="clr-namespace:Microsoft.Windows.Controls;assembly=Microsoft.Windows.Controls"
xmlns:local="clr-namespace:SilverlightApplication1"
xmlns:GeoPatterns="clr-namespace:GeoPatterns"
Height="700">
<UserControl.Resources>
...
And here is my constructor where the DataContext is currently set:
namespace SilverlightApplication1
{
public partial class SLHolePattern : UserControl, INotifyPropertyChanged
{
public HolePatternsViewModel HPVM;
public SLHolePattern()
{
InitializeComponent();
this.HPVM=new HolePatternsViewModel();
this.LayoutRoot.DataContext = this.HPVM;
...more code here
}
It all works fine, but I just want to learn how to set the DataContext in XAML, not in code.
The answer Chris gave works just fine.
I have tested and it worked for me.
You can instantiate your class in XAML (within the UserControl.Resources) and
then bind the datacontext to a static resource.
Follow code:
<UserControl ...>
<UserControl.Resources>
<myNS:MyClass x:Name="TheContext" x:Key="TheContext"></myNS:MyClass>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White" DataContext="{StaticResource TheContext}" >
<TextBlock Text="{Binding Path=Field1}">
</TextBlock>
</Grid>
</UserControl>
The following monstrosity works in Silverlight 4
<UserControl
DataContext="{Binding HPVM, RelativeSource={RelativeSource Self}}">
<UserControl.DataContext>
<vm:ThisUCViewModel />
</UserControl.DataContext>
try something like this.....
<Grid DataContext="{Binding Path=HPVM}">
</Grid>
where HPVM is a public member of this--> your form etc.
Create the instance of your class in the xaml, by adding something like this to your resources section.... (don't forget to add your xmlns namespace)
<my:bogart x:Key="franken"/>
then, bind the data context to the static resource you just added....
<Grid x:Name="LayoutRoot" Background="White" DataContext="{StaticResource franken}">
<TextBox Background="Red" Foreground="White" Text="{Binding Path=sum}" />
</Grid>
In Silverlight 4, I was able to get this working by doing the following:
Give the Page/UserControl an x:Name="myPage"
In your control binding use normal Element bidning syntax. In my case I want to bind to an observable collection of objects in my code behind for my ItemsSource property:
<ComboBox
ItemsSource={Binding ElementName=myPage, Path=MyObservableObjectList, Mode=TwoWay}
I haven't tried this with DataContext but know you can do element to element binding for DataContext as I do this for Grids whose context is based on the selected item of some other drop down on the page.
This is not possible (It is possible in WPF with {Binding RelativeSource={RelativeSource Self}}, but Silverlight is more limited.
You have to do it through code.
<UserControl.Resources>
<ResourceDictionary>
<vm:YourModelx:Key="myModel"/>
</ResourceDictionary>
</UserControl.Resources>
<UserControl.DataContext>
<Binding Source="{StaticResource myModel}"/>
</UserControl.DataContext>

Resources