inva
inva

Reputation: 761

Understanding DataBinding

I'm new to WPF and the whole data binding thing. I read a couple of posts and I'm quite confused about how to bind my data to a UI element.

I saw a post doing it this way:

<Window
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Name="myWindow">
   <Grid>
       <TextBox Text="{Binding Path=Speed, ElementName=myWindow}" />
   </Grid>
</Window>

This assumes Speed is a property / member defined in the code-behind file. A few other people named binding using a static resource and reference this one and others named DataContext to be used for binding. Now, because I'm new to WPF's data binding, I'm quite unsure if there exists any best practice method about which data binding to use.

Basically I was wondering why there has to be a several class-property defined as kind of connector to the underlying viewmodel, I though this stuff was more "dynamically".

I'm aiming at only having the XAML file, without the need to add anything into the *.xaml.cs code behind file. For example: I have a class named MainWindowViewModel (which will represent my ViewModel) having a member of the type ObservableCollection<string> and I want to bind a ListBox (in my View) to this collection. The only way I got this working so far was using the the first case, ElementName, where I have to add a property to the view-class as a kind of connector. Just like this:

MainWindow.xaml:

<Window x:Class="Sample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Name="MW" Title="MainWindow" Height="419" Width="463">
    <Grid>
        <ListBox ItemsSource="{Binding ElementName=MW, Path=ListerResultConnector}" />
    </Grid>
</Window>

MainWindow.xaml.cs:

private ObservableCollection<string> mListerResultData = MainWindowViewModel.Instance.RetrievalStringResults;
public ObservableCollection<string> ListerResultConnector
{
    get { return mListerResultData; }
}

My question is, if there exists a smarter way to bind data to my UI as using as further "connector" property in the code-behind file.

Upvotes: 2

Views: 1664

Answers (6)

Andrei Gavrila
Andrei Gavrila

Reputation: 863

The simplest way to do it would be:

<Window x:Class="WpfApplication4.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ctrl="clr-namespace:WpfApplication4"
    Title="MainWindow" Height="350" Width="525">
<Window.Resources>
    <ctrl:MainWindowViewModel x:Key="mym" />
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource mym}}">
    <ListBox ItemsSource="{Binding Path=MyListProp}" />
</Grid>

The example above does the following:

  1. Creates a new object of the ViewModel as resource for you window
  2. Assigns the view model as DataContext for the window (more precisley for the root grid)
  3. Binds the List box to your ViewModel ObservableCollection property MyListProp.

Try to get familiar with the properties of the binding class to fully understand this example by using the msdn library. Also WPF 4 Unleashed - Adam Nathan is a great resource for a wpf beginner. Chapter 13 - Data Binding should cover everything you might want to know about bindings.

Upvotes: 1

radarbob
radarbob

Reputation: 5101

Yeah, Binding is nothing if not confusing.

This example from msdn may be helpful. Notice how a DataContext is declared at the Window "level" - it's a complex/compound object in this case and Window sub-elements' {Binding} declarations are therefore implicitly relative to the "Parent level" DataContext.

The main goodness we get is that when the DataContext is set to a given object all the "sub element" data bindings are automagically keep in synch. We can control this synchronization at any/many levels in the UI Control structure heirarchy by using this pattern.

Collection binding under the hood

The bottom line is your collection must implement IList as a minimum. Many .NET Collection classes are "binding ready."

Here's a quote from the msnd doc on ObservableCollection:

Before implementing your own collection, consider using ObservableCollection or one of the existing collection classes, such as List, Collection, and BindingList, among many others. If you have an advanced scenario and want to implement your own collection, consider using IList, which provides a non-generic collection of objects that can be individually accessed by index. Implementing IList provides the best performance with the data binding engine.

Finally, IBinding list vs IList is needed for 2-way binding.

Upvotes: 1

Rachel
Rachel

Reputation: 132558

A binding generally has two parts: a source object and a property name

When you specify ElementName in a binding, you are setting the Source object. Other ways to specify the binding's source object include the RelativeSource and Source properties. You can also leave the binding's source object undefined, in which case it will use whatever the DataContext is of the current object. The Source Object can be a UI Element found in WPF's VisualTree, or it can be a class object that is in the control's DataContext

When you specify Path, you are telling the binding what Property to use. You can also leave this property blank to bind to the current object's DataContext

The DataContext is the data object stored behind the control. This is typically a Model or a ViewModel (if using MVVM), although it can also be almost any object, such as a UI Element.

In your example

<Window
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Name="myWindow">
   <Grid>
       <TextBox Text="{Binding Path=Speed, ElementName=myWindow}" />
   </Grid>
</Window>

you are telling the binding that it's Source Object is the UI Element in the Visual Tree named myWindow, and the property is called Speed. Since your object named myWindow is of type Window, this binding will only work if your Window class actually has a property called Speed.

There are a lot of answers here suggesting you switch to using the MVVM design pattern, and I agree that you should try and use MVVM if you're working with WPF. It makes coding a lot simpler since you are separating your application logic from your UI. If you're interested, I have a simple MVVM example posted on my blog which explains the basics of that design pattern.

Upvotes: 1

SliverNinja - MSFT
SliverNinja - MSFT

Reputation: 31641

You should use a separate ViewModel class (WindowViewModel) which represents your data context for your Window (Window.DataContext).

I found that using the MVVM Light toolkit and following some of the videos on the site helped me fill in the gaps. Take time to learn it before getting started and it will start to sink in. IoC containers are also mixed in with MVVM + WPF for directory management & lifetime management of view models and improved design-time experience (blendability).

Toolkits aren't required and sometimes get in your way of learning the pattern. Once you understand it though, you should leverage a toolkit to speedup the development process. Here is a comparison of various MVVM toolkits.

Upvotes: 1

doblak
doblak

Reputation: 3136

Your ViewModel should be set as the DataContext to the view. Then you don't need any "further connector" in codebehind. Binding actually refers to DataContext and if you don't set it, it remains as 'this', which corresponds to your codebehind file (which is just a partial to your view).

Also, take a look at: WPF Apps With The Model-View-ViewModel Design Pattern

You should get the WPF and it's binding basics first, but once you understand those, I suggest looking at Caliburn Micro and its convention-based bindings, event handling and other features which make your code much cleaner.

Upvotes: 2

GazTheDestroyer
GazTheDestroyer

Reputation: 21251

Your {Binding ElementName=MW means you are binding to the Window itself, so your DataContext is the Window, and any properties you implement need to be defined in that Window (in your code behind - your "connectors").

You need to remove the ElementName=MW bit and assign your ViewModel as the DataContext so you can bind to its properties directly. How you do this depends on whether you use "ViewModel First", or "View First"

In View first, you manually assign the DataContext to the View in code when you create the view.

In ViewModel first, you create a DataTemplate in XAML that ties your View to a particular ViewModel. I think ViewModel first is more common. The link that Darjan posted should help you understand the difference.

Upvotes: 1

Related Questions