I would like to add a reference to my model in my view model using XAML. I have defined my model and my view model as resources, and I would like to bind the dependency property Model of the resource myViewModel to the resource myModel.
How do I do this using XAML?
Below is my attempt:
<Window.Resources>
<local:ViewModel x:Key="myViewModel" />
<local:Model x:Key="myModel" />
</Window.Resources>
<myViewModel Model="{StaticResource myModel}">
This works fine:
<Window.Resources>
<local:Model x:Key="myModel" />
<local:ViewModel x:Key="myViewModel" Model="{StaticResource myModel}" />
</Window.Resources>
Assuming, of course, the model data structures are declared appropriately (you didn't provide that context).
Related
My WPF application uses a Resource Dictionary. I'm also using MVVM.
I am binding to a ResourceDictionary, but want to bind my MainWindow ViewModel to the MainWindow (of type Window) but MVVM won't let me as MainWindow it's not type UserControl.
<Grid.Resources>
<ResourceDictionary Source="Resources\ResourceDictionary.xaml" />
</Grid.Resources>
<Grid.DataContext>
<Binding Source="{StaticResource Mwvm}" />
</Grid.DataContext>
</Grid>
This means I can't do this
<DataTemplate DataType="{x:Type viewModel:MainWindowViewModel}">
<root:MainWindow x:Key="Mwvm" />
</DataTemplate>
Does any one know how I can do the same thing but when the object is a Window and only using XAML (I know I can do this with code behind in the app.xaml onstartup())?
EDIT
To make this very clear, I know that within my MainWindow I can declare a namespace to my ViewModel, but is this the correct way when the namespace is already referenced in my ResourceDictionary and I'm referencing my ResourceDictionary.
Uhm how about?
<Window>
<Window.DataContext>
<someNs:YourVmClass /> <!-- calls the empty c_tor of the class-->
</Window.DataContext>
</Window>
(I'm not sure, if I understood your question. But I guess, that's what you really want.)
According to your edit:
Sure you could do something like
<!-- Define an instance of your VM in the ResourceDictionary -->
<ResourceDictionary>
<someNs:YourVmClass x:Key="InstOfYourVmClass" />
</ResourceDictionary>
In your view you could do something like this.
<Grid>
<Grid.Resources>
<ResourceDictionary Source="Resources\ResourceDictionary.xaml" />
</Grid.Resources>
<Grid.DataContext>
<StaticResource ResourceKey="InstOfYourVmClass" />
</Grid.DataContext>
</Grid>
But I would strongly recommend not to choose this approach. Problem is, everytime you're referencing this ResourceDictionary the current instance InstOfYourVmClass will be overwritten by a new instantiated version.
Is there some way in WPF to get the same functionality DataTemplateSelector gives you, but for UserControls?
Say I have a StackView to which I want to bind an IEnumerable of objects. What I'd like to do is somehow have a mapping that, for each object type in the bound IEnumerable, looks at the object type and determines what UserControl to add to the StackView.
So, given three classes:
public class House : Building{}
public class Apartment : Building{}
public class Tent : Building{}
where each class inherits from Building and has its own defined UserControl, I'd like to set DataContext to an IEnumerable<Building> and somehow get the StackView to populate its set of children with the type-specific UserControl.
I'd like to do this with as little code behind as possible. The more data binding and XAML duct tape the better.
You can use complex user controls in a DataTemplate; just declare the DataTemplate as your UserControl.
Example:
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication4"
Title="MainWindow" Height="300" Width="300" Name="UI" >
<Window.Resources>
<DataTemplate DataType="{x:Type local:House}" >
<local:HouseUserControl DataContext="{Binding}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Apartment}">
<local:ApartmentUserControl DataContext="{Binding}"/>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding ElementName=UI, Path=ListOfBuildings}" />
</Grid>
</Window>
I'm not sure I see the problem. Just create DataTemplates for each type in your resources somewhere and WPF will use them automatically to render each type.
In the MainWindow.xaml, I set:
<Window.DataContext>
<vm:MainViewModel/>
</Window.DataContext>
In the App.xaml file, I added the following:
<Application.Resources>
<DataTemplate DataType="vm:MainViewModel">
<v:MainView/>
</DataTemplate>
</Application.Resources>
I was hoping the MainWindow will automatically load and show the MainView with its DataContext property set to the windows's one (which was set to MainViewModel at design-time as above), but it won't work - the MainWindow doesn't use the DataTemplate set in App.xaml.
Any better ideas for this scenario?
You should make a minor changes -
First, in your window. Try this:
<Window>
<!-- setup window... -->
<ContentPresenter>
<ContentPresenter.Content>
<vm:MainViewModel/>
</ContentPresenter.Content>
</ContentPresenter>
</Window>
This creates a single content item within your Window. DataTemplates work by mapping content to a new View - in this case, since the Content here is the MainViewModel, it will automatically create and instantiate a new MainView for you. Setting the DataContext will not trigger DataTemplates, since you're never making the ViewModel "content" of an object.
You can shorten this by just setting the Window's Content directly, if you prefer:
<Window>
<Window.Content>
<vm:MainViewModel/>
</Window.Content>
</Window>
Or, even, binding the Content to the DataContext (though this only makes sense if you need the DataContext set for some other purpose):
<Window Content="{Binding}">
<Window.DataContext>
<vm:MainViewModel/>
</Window.DataContext>
</Window>
I think you need
<DataTemplate DataType="{x:Type vm:MainViewModel}">
EDIT:
I really don't think I'm wrong, the code
<Window.DataContext>
<WpfApplication1:ViewModel />
</Window.DataContext>
<Window.Resources>
<DataTemplate DataType="{x:Type WpfApplication1:ViewModel}">
<TextBlock>Custom template</TextBlock>
</DataTemplate>
</Window.Resources>
<ContentPresenter Content="{Binding}" />
shows “Custom template”. If I remove the x:Type, what's shown instead is “WpfApplication1.ViewModel”, which is the result of calling ToString() on the view model object. This is used in the absence of a DataTemplate.
Using the MVVM-pattern you set the DataContext to a specific ViewModel. Now is there any way to tell the XAML the type of the DataContext so that it will validate my bindings?
Looking for something like the typed viewdata in ASP.NET MVC.
You can write each individual binding in a strongly-typed way:
<TextBox Text="{Binding Path=(vm:Site.Contact).(vm:Contact.Name)}" />
However this would not validate the fact that TextBox DataContext is of type ViewModel.Site (and I think this is not possible, but I may be wrong).
No, the current spec does not have strong typing in Xaml. I believe that with .Net 4.0, Xaml should be seeing the capacity for generics. With that, I would think it should be much easier to have strong typing in Xaml.
No. FrameworkElement.DatatContext is the dependency property that enables data binding is of type object.
As pointed out by others, you can specify the expected type of a DataContext for a special template called a DataTemplate. Many controls such as ItemsControl, ControlControl provide access to DataTemplates to allow you to set the visual representation's expectations of the DataContext's type.
Bryan is correct, he did not test his code.
The correct application of a typed DataTemplate looks like this:
<Window>
<Window.Resources>
<DataTemplate x:Key="TypedTemplate" DataType="{x:Type myViewModel}">
...
</DataTemplate>
</Window.Resources>
<ContentControl Content="{Binding}" ContentTemplate="{StaticResource TypedTemplate}" />
</Window>
ContentPresenter inherits directly from FrameworkElement and does not have a Template property. In addition, the Template property commonly refers to Control.Template of type ControlTemplate which is something entirely different than a DataTemplate.
I think Bryan was thinking of the ContentControl which is one of the two root control types (the other being ItemsControl). ContentControl does in fact inherit from Control. Therefore we can specify the Template property on it if we so choose.
<Window>
<Window.Resources>
<DataTemplate x:Key="TypedTemplate" DataType="{x:Type myViewModel}">
...
</DataTemplate>
<ControlTemplate x:Key="ControlSkin" TargetType="{x:Type ContentControl}">
...
</ControlTemplate>
</Window.Resources>
<ContentControl Content="{Binding}" ContentTemplate="{StaticResource TypedTemplate}" Template="{StaticResource ControlSkin}" />
</Window>
I personally declare a static PropertyPath for each property in my viewmodel the reference this using x:static as the binding path -
e.g
public class MyViewModel
{
public static PropertyPath MyPropertyPath = new PropertyPath("MyProperty");
public bool MyProperty{get; set;}
}
xaml : {Binding Path={x:Static local:MyViewModel.MyPropertyPath}}
This way all my bindings get validated on build.
Try this:
<Window>
<Window.Resources>
<DataTemplate x:Key="TypedTemplate" DataType="{x:Type myViewModel}">
...
</DataTemplate>
</Window.Resources>
<ContentPresenter Content="{Binding}" Template="{StaticResource TypedTemplate}" />
</Window>
I haven't tested this code but it should give you the idea. The content presenter will display the current DataContext which will use the DataTemplate. This isn't strongly typed in the compiler but will throw a runtime error immediately on load (in the window's InitializeComponent). You should be able to catch this easily in your testing if something breaks.
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>