views:

1170

answers:

3

I have a very similar issue as described in this post.

I have a UserControl to encapsulate an address. This contains a number of basic controls, mostly textboxes. I then have backing dependecy properties in the code-behind for each property...

#region Line1

    /// <summary> 
    /// Gets or sets the Line1.
    /// </summary> 
    public string Line1
    {
        get
        {
            return (string)GetValue(Line1Property);
        }

        set
        {
            SetValue(Line1Property, value);
        }
    }

    /// <summary> 
    /// The Line1 dependency property.
    /// </summary> 
    public static readonly DependencyProperty Line1Property =
                DependencyProperty.Register(
                      "Line1",
                      typeof(string),
                      typeof(AddressControl),
                      new PropertyMetadata(OnLine1PropertyChanged));

    /// <summary>
    /// Line1Property property changed handler. 
    /// </summary>
    /// <param name="d">AddressControl that changed its Line1.</param>
    /// <param name="e">DependencyPropertyChangedEventArgs.</param> 
    private static void OnLine1PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var control = d as AddressControl;
        if (control != null)
        {
            control.OnLine1Changed((string)e.OldValue, (string)e.NewValue);
        }
    }

    /// <summary>
    /// Called when the Line1 changes.
    /// </summary>
    /// <param name="oldValue">The old value.</param>
    /// <param name="newValue">The new value.</param>
    private void OnLine1Changed(string oldValue, string newValue)
    {
        Line1TextBox.Text = newValue;
    }

    #endregion Line1

I then use this control in a view...

<myControls:AddressControl Grid.Row="0" Grid.Column="3" 
                           Line1="{Binding Path=Line1, Mode=TwoWay}"/>

This seems to set the textbox value when the view model property is updated as you would expect however my problem is getting an update from the usercontrol back to the viewmodel?

According to the link above I should check my DataContext on the control. Surley the DataContext will be the same as the parent?

I'm hoping an answer to this will apply to multiple nesting levels of controls ie. Control1 used in Control2 that is used in Control3, the whole point of reusability!

Driving me nuts so any help really grateful.

+3  A: 

In MVVM you should bind to the ViewModel properties, not to properties defined in your control's code-behind

Thomas Levesque
Thanks for taking the time to answer. If I understand you I would agree that that the property of the custom user control when on a view should be bound. However I can't bind directly to the underlying textboxes as this would restrict the binding to one instance. If I bound to "Line1" directly in the user control and used this in a page then I could create a property in the VM called Line1 all good as the datacontext is passed down. The prob is that I need to reuse and bind the line1 textbox to different VM properties. Eg ClientLine1, AnotherLine1. Hence UC in the first place first for reuse.
NeilE
You are right if you mean a backing VM class that is bound to the controls as I mention at the end of my answer or made by JamesC78.
NeilE
A: 

I don't think I explained myself well enough initially and also think I have found a solution.

For others; My goal was to create a simple user control to encapsulate an address (line 1 - 4 and Postcode, told you it was simple) made up of TextBoxes for use on many pages (sometimes used more than once on the same page). Each of these pages (views) is backed by a ViewModel from the MVVM pattern. This is all standard so far. What I couldn't figure out was how to get a change to any of these textboxes to propegate back up to the ViewModel of the page containing the AddressControl. With me?

As it turns out the solution (and it seems obvious now) was to handle the lost focus of each textbox and update the dependancy property, this causes the binding to fire up to the viewmodel.

private void AddressTextBox_LostFocus(object sender, RoutedEventArgs e)
    {
        switch (((TextBox)sender).Name)
        {
            case "Line1TextBox":
                Line1 = Line1TextBox.Text;
                break;

            case "Line2TextBox":
                Line2 = Line2TextBox.Text;
                break;

            case "Line3TextBox":
                Line3 = Line3TextBox.Text;
                break;

            case "Line4TextBox":
                Line4 = Line4TextBox.Text;
                break;

            case "LinePostcodeTextBox":
                Postcode = PostcodeTextBox.Text;
                break;
        }
    }

Another option was to bind the textboxes to a backing ViewModel in the codebehind of the control and have changes to this update the controls. Same effect using local binding rather than handling events.

NeilE
You shouldn't need to handle events to update the data context - that functionality is provided by SL data binding.
James Cadd
yuk! one possible problem may have been a lack of 'Mode=TwoWay' on the textbox bindings themselves. you had it for the control, but perhaps not for the textbox
Simon_Weaver
Was the start of my SL experience and over a year ago so can't really remember. Learnt a lot since then mind.
NeilE
A: 

It looks like a the code behind for a UserControl is being used to provide properties that the control itself is bound to. In this case you've probably got some code like "this.DataContext = this" or "this.AddressLine1Control.DataContext = this" which can be problematic (it even crashes SL2). Create a separate class to hold your data properties, then do something like "this.DataContext = new MyAddressClass()." You'll notice that the DataContext propagates all the way down the control tree so that nested controls inherit their parent's DataContext as you'd expect.

Also, it doesn't look like you need DependencyProperties here. It would be simpler to create traditional CLR properties instead and implement the INotifyPropertyChanged interface on your data class (assuming you separate the data from the UserControl class). This is a standard and recommended way to go about data binding in SL2.

James Cadd
The code-behind is providing properties to be bound to from other "Pages" hence the need for DPs as I understand it as you can't see normal CLR properties. ie <myControls:AddressControl Line1="This is Line1"/> or as I'm trying <myControls:AddressControl Line1="{Binding Path=MyVMProperty, Mode=TwoWay}"/>. These just happen to be one-to-one with textboxes. There are others that are just properties and not backed by a control.
NeilE
I also found that DataContext = this problem along th way as well as as you rightly point out I could bind the textboxes in the control to a backing class. <quote>Another option was to bind the textboxes to a backing ViewModel in the codebehind of the control and have changes to this update the controls. Same effect using local binding rather than handling events.</quote>. I just chose event handling as I was doing some other stuff that way anyway. Tannsk for the comments though, really would have saved me a load of head-aches if I'd found an answer like yours sooner.
NeilE