Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Preferred method for binding in MVVM, Data Template in Resources file or just DataContext in View itself?

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>
like image 272
djangojazz Avatar asked May 08 '13 17:05

djangojazz


People also ask

How do I bind Mvvm?

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 binding why binding of data is necessary?

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.

What is the data binding concept and how binding works in WPF?

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.


2 Answers

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)

like image 86
Rachel Avatar answered Oct 17 '22 07:10

Rachel


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.

like image 31
H.B. Avatar answered Oct 17 '22 09:10

H.B.