Two-way data binding with converter doesn't update source

人走茶凉 提交于 2019-12-01 03:32:49

问题


I've got a data binding set up with a converter to transform an awkward XML source to a display- and editing- convenient tree of internal classes. Everything works great for reading from the XML source, but I'm having a devil of a time trying to get changes made to the internal classes to propagate back to the XML source.

Here's the XAML for the use site:

        <local:SampleConverter x:Key="SampleConverter" />
        <Expander Header="Sample" >
            <local:SampleControl 
                Sample="{Binding Path=XmlSource, 
                                 Converter={StaticResource SampleConverter}, 
                                 Mode=TwoWay}" />
        </Expander>

XmlSource is a CLR read-write property (not DependencyProperty) of the parent data bound object. It is a .NET type generated from an XSD.

SampleConverter implements IValueConverter. The Convert method is called and returns non-null data, but the ConvertBack method is never called.

SampleControl is a UserControl that encapsulates UI interaction with the Sample data tree. It's XAML looks like this:

<UserControl x:Class="SampleControl">
    [... other stuff ...]

    <UserControl.Content>
        <Binding Path="Sample" RelativeSource="{RelativeSource Mode=Self}" Mode="TwoWay" TargetNullValue="{StaticResource EmptySampleText}" />
    </UserControl.Content>

    <UserControl.ContentTemplateSelector>
        <local:BoxedItemTemplateSelector />
    </UserControl.ContentTemplateSelector>
</UserControl>

The Sample property is a DependencyProperty in the SampleControl code behind:

public static readonly DependencyProperty SampleProperty =
    DependencyProperty.Register("Sample", typeof(SampleType), typeof(SampleControl), new PropertyMetadata(new PropertyChangedCallback(OnSampleChanged)));

public SampleType Sample
{
    get { return (SampleType)GetValue(SampleProperty); }
    set { SetValue(SampleProperty, value); }
}

private static void OnSampleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    if (e.NewValue != null)
    {
        ((INotifyPropertyChanged)e.NewValue).PropertyChanged += ((SampleControl)d).MyPropertyChanged;
    }
    else if (e.OldValue != null)
    {
        ((INotifyPropertyChanged)e.OldValue).PropertyChanged -= ((SampleControl)d).MyPropertyChanged;
    }
}

private void MyPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    ;  // breakpoint here shows change notices are happening
}

The internal classes that the XmlSource is converted to implement INotifyPropertyChanged, and are sending change notifications up the tree, as indicated by a breakpoint in MyPropertyChanged above.

So if the data is reporting that it has changed, why isn't WPF calling my converter's ConvertBack method?


回答1:


With hints from several similar questions and almost answers here on SO, I have a working solution that preserves the binding. You can manually force the binding to update the source in a strategically placed event, such as LostFocus:

private void mycontrol_LostFocus(object sender, RoutedEventArgs e)
{
    if (mycontrol.IsModified)
    {
        var binding = mycontrol.GetBindingExpression(MyControl.SampleProperty);
        binding.UpdateSource();
    }
}



回答2:


XmlSource is a CLR read-write property (not DependencyProperty) of the parent data bound object. It is a .NET type generated from an XSD.

I believe this is your problem. A basic CLR property won't notify, and is thus unable to participate in two-way databinding. My guess would be your are getting one-time binding? Do you have any databinding errors in your output window?

You might try creating a wrapper that contains the XmlSource property's class, make that class implement INotifyPropertyChanged, and expose XmlSource as a notifying property (it does not need to be a dependency property).

Also, regarding this comment:

Are you saying that data binding only works if a new instance is assigned to the Sample property, but not if properties of the existing instance referred to by the Sample property are changed and signal their changes via INotifyPropertyChanged?

Sample implementions of INotifyPropertyChanged usually have the notification occuring when the property itself changes, not when children or other properties change. However, you can absolutely fire the notification event on any property at any time. For example, you might have a "Full Name" property that notifies whenever "First Name" or "Last Name" change.

If the above isn't helpful, I would suggest posting a bit more of your code--maybe a simplified repro of what you are trying to get working.

Edit:

I think I misunderstood your question. I think the problem is what you alluded to in your comments--it's a reference type. Might be worth trying this in your OnSampleChanged:

private static void OnSampleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
{
    var oldValue = Sample;
    Sample = new SampleType();
    Sample = oldValue;
}

I think this will force the binding system to recognize that something changed on the target side.




回答3:


Had similar problem. The solution was the following:

Instead of the two way binding I used one way, and a converter. The converter converts (encapsulates) the object (in your case, the parent object of the xmlsource property) to a viewModel, and the control binds to it.

The viewModel works like a proxy, holds a reference to the object, manages it's properties, and of course implements INotifyPropertyChanged. In this case you don't need to call the ConvertBack method, since the operations are performed on the proper instance through the viewModel.

So you have a clean view, you don't need any code in the xaml.cs



来源:https://stackoverflow.com/questions/6363883/two-way-data-binding-with-converter-doesnt-update-source

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!