C# 与转换器的双向数据绑定不';t更新源

C# 与转换器的双向数据绑定不';t更新源,c#,wpf,xaml,data-binding,two-way-binding,C#,Wpf,Xaml,Data Binding,Two Way Binding,我已经用转换器设置了一个数据绑定,将一个笨拙的XML源转换成一个便于显示和编辑的内部类树。对于从XML源读取来说,一切都很好,但是我花了很长时间试图对内部类进行更改,以便传播回XML源 以下是使用站点的XAML: <local:SampleConverter x:Key="SampleConverter" /> <Expander Header="Sample" > <local:SampleControl

我已经用转换器设置了一个数据绑定,将一个笨拙的XML源转换成一个便于显示和编辑的内部类树。对于从XML源读取来说,一切都很好,但是我花了很长时间试图对内部类进行更改,以便传播回XML源

以下是使用站点的XAML:

        <local:SampleConverter x:Key="SampleConverter" />
        <Expander Header="Sample" >
            <local:SampleControl 
                Sample="{Binding Path=XmlSource, 
                                 Converter={StaticResource SampleConverter}, 
                                 Mode=TwoWay}" />
        </Expander>
Sample属性是SampleControl代码背后的DependencyProperty:

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
}
XmlSource转换为实现INotifyPropertyChanged的内部类,并向树上发送更改通知,如上面MyPropertyChanged中的断点所示

所以,如果数据报告它已经更改,为什么WPF不调用我的转换器的ConvertBack方法呢

XmlSource是一个CLR读写属性 父母的(非从属财产) 数据绑定对象。它是.NET类型 从XSD生成

我相信这是你的问题。基本CLR属性不会通知,因此无法参与双向数据绑定。我猜你会得到一次性装订吗?在输出窗口中是否有任何数据绑定错误

您可以尝试创建包含XmlSource属性的类的包装器,使该类实现INotifyPropertyChanged,并将XmlSource作为通知属性公开(它不需要是依赖项属性)

此外,关于这一评论:

你是说数据绑定只是 如果将新实例指定给 Sample属性,但如果 现有实例的属性 示例属性所引用的是 已更改,并通过 InotifyProperty是否已更改

INotifyPropertyChanged的示例实现通常在属性本身更改时发出通知,而不是在子属性或其他属性更改时。但是,您完全可以随时在任何属性上触发通知事件。例如,您可能有一个“全名”属性,当“名字”或“姓氏”更改时,该属性会发出通知

如果上面的内容没有什么帮助,我建议您多发布一些代码——也许是对您正在尝试的工作的简化复制

编辑:

我想我误解了你的问题。我认为问题在于你在评论中提到的——这是一种引用类型。可能值得在您的OnSampleChanged中尝试此功能:

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

我认为这将迫使绑定系统认识到目标端发生了一些变化。

根据几个类似问题的提示和这里的基本答案,我有一个保留绑定的有效解决方案。您可以手动强制绑定在策略性放置的事件中更新源,例如LostFocus:

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

我也有类似的问题。解决办法如下:

我没有使用双向绑定,而是使用单向绑定和转换器。 转换器将对象(在您的例子中是xmlsource属性的父对象)转换(封装)为viewModel,控件绑定到它

viewModel的工作方式类似于代理,保存对对象的引用,管理对象的属性,当然还实现了INotifyPropertyChanged。 在这种情况下,您不需要调用ConvertBack方法,因为操作是通过viewModel在适当的实例上执行的


因此,您有一个干净的视图,在xaml.cs中不需要任何代码

您的属性更改代码示例仅表明示例的属性正在更改,而不是示例本身。@Ragepotato:您是说数据绑定仅在将新实例分配给示例属性时才起作用,但如果Sample属性引用的现有实例的属性已更改,并通过INotifyPropertyChanged发出更改信号,则不会更改?是。样品从未改变。它是同一个物体。其中的属性已更改。你可以测试一下。加载所有内容后,在控件上放置一个按钮,Sample从VM获得其初始分配。然后将样本分配给新的样本类型。请记住,它必须是正确的类型,否则绑定引擎将忽略它。如果您正确设置双向绑定,您将看到您的convert back被调用。此外,我发现一些有价值的东西,一旦发现它,如果您希望在DP上默认双向绑定,您可以使用FrameworkPropertyMetadata而不是PropertyMetadata,并默认设置BindsTwoWay。这对于内容是原子的值类型是有意义的,但对于引用类型则没有意义,因为引用类型通常(从概念上)被视为其属性的总和。特别是当涉及转换器时-如果转换值的属性更改,则转换值的状态已更改,需要通过ConvertBack将其传回源属性。否则,转换器仅对值类型和不可变对象有用。XmlSource CLR属性已实现INotifyPropertyChanged。此外,需要首先调用转换器的ConvertBack,以获取要分配给XmlSource属性的值,而不会调用ConvertBack。
private void mycontrol_LostFocus(object sender, RoutedEventArgs e)
{
    if (mycontrol.IsModified)
    {
        var binding = mycontrol.GetBindingExpression(MyControl.SampleProperty);
        binding.UpdateSource();
    }
}