views:

38

answers:

2

Greetings folks! I'm running into a problem with WPF databinding that I hope you can help out with. I'm new to WPF but an expereienced developer (VB 3.0-6.0, C#).

Here's the scenario: I have a C# project called MasterPartsData which contains a number of classes which reprsent different types of parts (capacitor, diode, etc). They inherit from a base class called clsPart.

I have another C# WPF project which contains WPF UserControls (as well as a MainWindow) to visually represent the values stored in an individual MasterPartsData (MPD) object. I've created a private field in the usercontrol to hold the object with a getter and setter.

If I create a binding explicitly in the setter for the populated object:

_capacitor = value;
Binding binding = new Binding();
binding.Source = _capacitor;
binding.Path = new PropertyPath("C0uf");
this.txtC0uf.SetBinding(TextBox.TextProperty, binding);

(with _capacitor being the private object variable and C0uf being the property name) the value correctly displays.

However I don't wish to have to explicitly create each binding in the code behind. My preference is to create the bindings inline in XAML, perhaps with a DataContext pointing to the object.

Unfortunately every different permutation I've tried fails to work; the text box doesn't show data.

I have a couple of suspicions: 1) The binding is correct, but the text box needs to be refreshed. 2) The binding is confused between the private variable and the properties. 3) Maybe the fact that the class is defined in a different project is causing issues. 4) I'm going mad and should check myself into an asylum before someone gets hurt. :)

Any help you can provide would be most appreciated. I'm more than happy to add more information, but didn't want to clutter the question with pages and pages of source.

+1  A: 

In a basic binding scenario, if your class looks like this

public class MasterPartsData
{
    private string _c0uf;

    public string C0uf
    {
        get { return _c0uf;}
        set { _c0uf = value;}
    }

    public MasterPartsData()
    {
        C0uf = "Hello World!";
    }
}

your XAML would look like this

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication1"
    Title="MainWindow" >
    <Window.DataContext>
        <local:MasterPartsData />
    </Window.DataContext>

    <Grid>
        <TextBlock Text="{Binding Path=C0uf}" />
    </Grid>
</Window>

Note, there are many different approaches to setting the DataContext, you don't necessarily just have to do it in the XAML

Also, typically your MasterDataParts class would implement INotifyPropertyChanged

qntmfred
+2  A: 

With respect to your suspicions:

1) I think the default binding behavior of a TextBox is TwoWay, with a LostFocus update trigger, meaning that your UI focus will have to change to another control before the binding will update, if changes are made in the UI.

If changes are made in the code you need to raise the NotifyPropertyChanged event in order for the binding system to see it.

2) This is probably not the case, but it leaves the impression that you're trying to set bindings on your UserControl properties, which is not the way data binding was designed to be used in this particular kind of use case. What you want is to bind data from non-UI classes to dependency properties on your UserControls.

3) This will never matter, as long as your UI project has a reference to your classes.

4) This is a common reaction people have when beginning to use XAML and WPF. It's like instead of being handed a box of Legos, you just got handed an injection molding machine with insufficient instructions, isn't it?

Overall, this is a situation where you might need to examine your design; elements of the "Model-View-ViewModel" pattern will come in handy. If you're unfamiliar with this, it's a development pattern in which you introduce a "ViewModel" class, perhaps you can call it MasterPartsVM which contains an implementation of INotifyPropertyChanged.

The DataContext of your UserControl would be set to this MasterPartsVM class.

A brief code example, using some generic names. Given a ViewModel class with a small backing class that looks like this:

class PartViewModel : INotifyPropertyChanged
{

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion

    public PartClass Data { get; set; }

    public String SomeVMProperty
    {
        get { return Data.SomeProperty; }

        set
        {
            if (Data.SomeProperty != value)
                Data.SomeProperty = value;
            this.PropertyChanged(this, new PropertyChangedEventArgs("SomeVMProperty"));
        }
    }

}

class PartClass
{
    public string SomeProperty { get; set; }
}

The XAML of a basic UserControl would look like this:

<UserControl x:Class="WpfApplication1.PartUserControl"
            ... >
    <Grid>
        <TextBox Text="{Binding SomeVMProperty}" Margin="68,77,104,176" />
    </Grid>
</UserControl>

To connect your data class to this UserControl, you set the UserControl's DataContext property. If you do this in code, it's a matter of having a reference to your user control and the ViewModel, and then setting the property:

MyUserControlInstance.DataContext = new PartViewModel(); // or some existing PartViewModel

That combination of code should work to produce a textbox whose Text property changes every time the SomeVMProperty property is changed.

Rob Perkins
Thanks kindly for the information! I'm starting to see where the ModelView comes into play. In my case I have an object which contains multiple other classes. The ModelView essentially gives a 'flat' view of the hierarchy so the XAML only has one datacontext to deal with. Correct?
Falconeer
More or less correct, but the MVVM pattern is still just a pattern; there is nothing inherent in the data binding contracts that enforce the pattern. Inside the hierarchy, you can specify other data contexts for sub-elements of your user control, things which likely break good architectural ideals. To me, the point of having a ViewModel is to have something which handles data binding details (two-way or one way, and a place to site converters) which doesn't require you to modify the code in your model.
Rob Perkins