Do DataContext assign on XAML pointing to its code behind class - silverlight

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}">

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 :)

Define Blend Expression ViewModel definition for RelativeSource binding

I have an UserControl (MyCompositeView) where I add some other UserControls (MyDetailView). To get intellisense and refactoring ability I add to the CompositeView the d:DataContext in xaml.
To Manipulate the UserControl while adding them an new DataContext I bind the Visiblity to the RelativeSource (my CompositeViewModel). Unfortunately the matching against the MyCompositeViewModel isn't working for this Binding in the Editor.
Is there a way to let visual studio know that my DataContext of DataContext.DetailsIsVisible is from the type MyCompositeViewModel?
<UserControl
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
x:Class="Example.MyCompositeView"
d:DataContext="{d:DesignInstance Type=MyCompositeViewModel}"
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
<Grid>
<detail:MyDetailView
DataContext="{Binding Path=DetailViewModel}"
Visibility="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type UserControl}},
Path=DataContext.DetailsIsVisible,
Converter={StaticResource BooleanToVisibilty}}" />
</Grid>
</UserControl>
--
public class MyCompositeViewModel : IMyCompositeViewModel {
public bool DetailisVisible{get;set;}
public MyDetailViewModel DetailViewModel { get; }
}
Try binding by element name instead:
<UserControl
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
x:Class="Example.MyCompositeView"
x:Name="root"
d:DataContext="{d:DesignInstance Type=MyCompositeViewModel}"
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
<Grid>
<detail:MyDetailView
DataContext="{Binding Path=DetailViewModel}"
Visibility="{Binding ElementName="root"},
Path=DataContext.DetailsIsVisible,
Converter={StaticResource BooleanToVisibilty}}" />
</Grid>
</UserControl>
or flip the property order:
<UserControl
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
x:Class="Example.MyCompositeView"
d:DataContext="{d:DesignInstance Type=MyCompositeViewModel}"
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
<Grid>
<detail:MyDetailView
Visibility="{Binding DetailsIsVisible, Converter={StaticResource BooleanToVisibilty}}"
DataContext="{Binding DetailViewModel}"/>
</Grid>
</UserControl>

Binding one dependency property to another

I have a custom Tab Control that I have created, but I am having an issue. I have an Editable TextBox as part of the custom TabControl View.
<Controls:EditableTextControl x:Name="PageTypeName"
Style="{StaticResource ResourceKey={x:Type Controls:EditableTextControl}}" Grid.Row="0" TabIndex="0"
Uid="0"
AutomationProperties.AutomationId="PageTypeNameTextBox"
AutomationProperties.Name="PageTypeName"
Visibility="{Binding ElementName=PageTabControl,Path=ShowPageType}">
<Controls:EditableTextControl.ContextMenu>
<ContextMenu x:Name="TabContextMenu">
<MenuItem Header="Rename Page Type" Command="{Binding Path=PlacementTarget.EnterEditMode, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
AutomationProperties.AutomationId="RenamePageTypeMenuItem"
AutomationProperties.Name="RenamePageType"/>
<MenuItem Header="Delete Page Type" Command="{Binding Path=PageTypeDeletedCommand}"
AutomationProperties.AutomationId="DeletePageTypeMenuItem"
AutomationProperties.Name="DeletePageType"/>
</ContextMenu>
</Controls:EditableTextControl.ContextMenu>
<Controls:EditableTextControl.Content>
<!--<Binding Path="CurrentPageTypeViewModel.Name" Mode="TwoWay"/>-->
<Binding ElementName="PageTabControl" Path="CurrentPageTypeName" Mode ="TwoWay"/>
</Controls:EditableTextControl.Content>
</Controls:EditableTextControl>
In the Content section I am binding to a Dependency Prop called CurrentPageTypeName. This Depedency prop is part of this custom Tab Control.
public static DependencyProperty CurrentPageTypeNameProperty = DependencyProperty.Register("CurrentPageTypeName", typeof(object), typeof(TabControlView));
public object CurrentPageTypeName
{
get { return GetValue(CurrentPageTypeNameProperty) as object; }
set { SetValue(CurrentPageTypeNameProperty, value); }
}
In another view, where I am using the custom TabControl I then bind my property, with the actual name value, to CurrentPageTypeName property as seen below:
<Views:TabControlView Grid.Row="0" Name="RunPageTabControl"
TabItemsSource="{Binding RunPageTypeViewModels}"
SelectedTab="{Binding Converter={StaticResource debugConverter}}"
CurrentPageTypeName="{Binding Path=RunPageName, Mode=TwoWay}"
TabContentTemplateSelector="{StaticResource tabItemTemplateSelector}"
SelectedIndex="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.SelectedTabIndex}"
ShowPageType="Hidden" >
<!--<Views:TabControlView.TabContentTemplate>
<DataTemplate DataType="{x:Type ViewModels:RunPageTypeViewModel}">
<RunViews:RunPageTypeView/>
</DataTemplate>
</Views:TabControlView.TabContentTemplate>-->
</Views:TabControlView>
My problem is that nothing seems to be happening. It is grabbing its Content from the Itemsource, and not from my chained Dependency props. Is what I am trying even possible? If so, what have I done wrong.
Thanks for looking.
Unless I'm missing something this is definitely possible. Here is a simplified working example.
User control with a dependency property named TestValue, containing a TextBox bound to this property:
<UserControl x:Class="TestApp.TestControl" 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" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"
x:Name="TestControlName">
<Grid>
<TextBox Text="{Binding ElementName=TestControlName, Path=TestValue, Mode=TwoWay}"/>
</Grid>
</UserControl>
A different view using this user control, binding the above mentioned dependency property to something:
<Window x:Class="TestApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:TestApp="clr-namespace:TestApp" Title="MainWindow"
Height="350" Width="525">
<StackPanel>
<TestApp:TestControl TestValue="{Binding ElementName=SourceTextBox, Path=Text, Mode=TwoWay}" />
<TextBox Name="SourceTextBox" />
</StackPanel>
</Window>
It sounds that the issue is somewhere in the part of the code you have not posted (e.g. wrong name used in Content binding).
I think you already solved this yourself for the "SelectedIndex" property. Just do the same thing for the "CurrentPageType" property i.e. use RelativeSource

Binding datacontext to another viewmodel when design mode

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>

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