This one has got me stumped as I THOUGHT I looked at everything but I must be missing something. I have went off the traditional MVVM pattern from the MSDN magazine:
http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
while learning MVVM. However I usually copy most of the code and then replace it as I need to but today I wanted to build something from scratch and saw that there may be more to it than I thought. MVVM appeared to not work with bindings when I used the resource dictionary but does with datacontext directly. This question ultimately wants to find other developers suggested use of binding they find.
Summary of question is this: Why does the 'DataTemplate' in the Resource Dictionary appear to not work shown below but a direct 'DataContext' method does with a view right away for bindings?
Is it because I am doing a mixture of code behind with settings views in the code behind. Or potentially because of something else. It appears my property and it's implementation are set correct in the viewmodel if I set the 'DataContext' directly in the View's XAML, but why not in the Resource Dictionary? I thought the advantage of that method was you could set a bunch of relationships all at once. I am curious if there is some other setting need to be done to get it to work. It is curious in the main example of the MVVM method they use Data Templates yet it appears there is more to setting them up than I am doing to get their 'bindings' to work.
WHAT DID NOT WORK FOR ME:
I attempted to do some very basic stuff in a main window xaml, leaving some of my code out to make it even simpler:
Main Window XAML:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dxc="http://schemas.devexpress.com/winfx/2008/xaml/charts" x:Class="WPFTesting12_2.MainWindow"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ResourceDictionary Source="Resources.xaml"/>
</Window.Resources>
<Grid>
<DockPanel x:Name="dockpanel">
<Menu DockPanel.Dock="Top" Height="30">
<MenuItem Header="Charting">
<MenuItem Header="MVVMDataBound" x:Name="mnuDataBoundSeriesMVVMCharting" Click="mnuDataBoundSeriesMVVMCharting_OnClick"/>
</MenuItem>
</Menu>
<TextBlock Height="5" Background="Black" DockPanel.Dock="Top" />
<DockPanel x:Name="dockchildren" DockPanel.Dock="Bottom"/>
</DockPanel>
</Grid>
</Window>
Main Window Code Behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.WindowState = WindowState.Maximized;
}
private void mnuDataBoundSeriesMVVMCharting_OnClick(object sender, RoutedEventArgs e)
{
View.DataBoundMVVMChart c = new DataBoundMVVMChart();
dockchildren.Children.Clear();
dockchildren.Children.Add(c);
}
}
}
Resource Dictionary:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vw="clr-namespace:WPFTesting12_2.View"
xmlns:vm="clr-namespace:WPFTesting12_2.ViewModel"
>
<DataTemplate DataType="{x:Type vm:DataBoundMVVMChartViewModel}">
<vw:DataBoundMVVMChart/>
</DataTemplate>
<Style TargetType="MenuItem">
<Setter Property="Background" Value="Wheat"/>
</Style>
<Style TargetType="Menu">
<Setter Property="Background" Value="Wheat"/>
</Style>
</ResourceDictionary>
View for DataBoundMVVMChart.xaml:
<UserControl x:Class="WPFTesting12_2.View.DataBoundMVVMChart"
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:WPFTesting12_2.ViewModel"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<ResourceDictionary Source="..\Resources.xaml"/>
</UserControl.Resources>
<Grid>
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="TesterContent"/>
</Menu>
<Label DockPanel.Dock="Bottom" Width="300" x:Name="label" Height="50" Foreground="Blue" FontSize="24" Content="{Binding Path=HelloString}"/>
</DockPanel>
</Grid>
</UserControl>
ViewModel to bind to the View above:
namespace WPFTesting12_2.ViewModel
{
class DataBoundMVVMChartViewModel : INotifyPropertyChanged
{
private string _HelloString;
public string HelloString
{
get { return _HelloString; }
set
{
_HelloString = value;
RaisePropertyChanged("HelloString");
}
}
public DataBoundMVVMChartViewModel()
{
HelloString = "Hello there from the ViewModel";
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
}
Okay now the binding will fail in the view but yet the resources of the color will come in. So I was thinking I did something wrong but code behind will get the property right away. So let's move on:
WHAT DID WORK:
Magicially if I just add these four lines to the view:
Add this to the declarations in the 'UserControl' segment at top:
xmlns:local="clr-namespace:WPFTesting12_2.ViewModel"
Then set a reference to the UserControl's DataContext:
<UserControl.DataContext>
<local:DataBoundMVVMChartViewModel/>
</UserControl.DataContext>
MVVM – WPF Data Bindings For data binding you need to have a view or set of UI elements constructed, and then you need some other object that the bindings are going to point to. The UI elements in a view are bound to the properties which are exposed by the ViewModel.
What is data binding? Data binding is the process that establishes a connection between the app UI and the data it displays. If the binding has the correct settings and the data provides the proper notifications, when the data changes its value, the elements that are bound to the data reflect changes automatically.
Data binding is a mechanism in WPF applications that provides a simple and easy way for Windows Runtime apps to display and interact with data. In this mechanism, the management of data is entirely separated from the way data. Data binding allows the flow of data between UI elements and data object on user interface.
Its important to realize when working with WPF that there are two layers: the data layer (DataContext
) and the UI layer (the XAML).
Bindings are used to pull data from the data layer into the View layer.
When you write
<UserControl.DataContext>
<local:DataBoundMVVMChartViewModel/>
</UserControl.DataContext>
You are telling WPF that it should create a new instance of DataBoundMVVMChartViewModel
and use it for the data layer of that UserControl.
When you write
<Label Content="{Binding HelloString}" />
you are telling the Label to look in its data layer (the DataContext
) for a property called "HelloString", and use it for the Content
property.
If the property "HelloString" does not exist in the data layer (which it does not unless you set the DataContext
like you did with <UserControl.DataContext>
), the binding will fail and nothing gets displayed except for a binding error in your output window.
Your DataTemplate
from your ResourceDictionary
is something different from the DataContext
.
In fact, a ResourceDictionary
is just what it sounds like - a dictionary of resources that WPF can use in the application when needed. But objects in the Dictionary are not by default part of the application's UI itself. Instead, they need to be referenced in some way to be used.
But back to your DataTemplate
<DataTemplate DataType="{x:Type vm:DataBoundMVVMChartViewModel}">
<vw:DataBoundMVVMChart/>
</DataTemplate>
WPF uses DataTemplates to know how to draw specific types of objects. In your case, this DataTemplate
is telling WPF that anytime it needs to draw an object of type DataBoundMVVMChartViewModel
, it should do so by using a DataBoundMVVMChart
.
To insert an object into the UI, a Content
property is normally used, such as
MyLabel.Content = new DataBoundMVVMChartViewModel();
or
<ContentControl Content="{Binding SomeDataboundChartViewModelProperty}" />
I actually started out learning MVVM with the exact same article you linked in your question, and had a lot of trouble figuring it out as well, which lead me to doing a little blogging about WPF/MVVM aimed specifically for beginners like me :)
If you're interested, I have a few blog articles about WPF/MVVM that may help you understand the technology better.
What is this "DataContext" you speak of?
A simple MVVM example
As for your actual question:
Preferred method for binding in MVVM, Data Template in Resources file or just DataContext in View itself?
I prefer using a DataTemplate
in the .Resources
somewhere, as then your UI is not specifically tied with one specific DataContext
.
This is especially important when creating re-usable controls with MVVM. For example, if you have a CalculatorUserControl
, and you assigned it a DataContext
in the control itself such as with <UserControl.DataContext>
, then you could never use that CalculatorUserControl
with any other DataContext
other than the one that is created when the control is created.
So typically I set the DataContext
for the entire application once at startup, and use DataTemplates
to tell WPF how to draw the different ViewModels
or Models
in my application.
My entire application exists in the data layer, and the XAML is merely a user-friendly interface to interact with the data layer. (If you want to see a code sample, check out my Simple MVVM Example post)
If you want to use implicit DataTemplates
you have to use the view-model-first approach, you however add a view to your DockPanel
without a view-model as its context. If you simply add the respective view-model (to a ContentControl
or ItemsControl
), the view will be created automatically from the DataTemplate
with the view-model as its DataContext
.
e.g.
<ItemsControl Name="ic"/>
ic.Items.Add(new DataBoundMVVMChartViewModel());
I personally prefer this over view-first (e.g. adding a view which then assigns its own DataContext
), because you usually want references to the view-models, the view is only secondary and manipulated indirectly by interacting with the view-model.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With