与转换器的双向数据绑定不会更新源
我已经设置了一个带有转换器的数据绑定,用于将笨拙的 XML 源转换为方便显示和编辑的内部类树。从 XML 源读取一切都很顺利,但我在尝试将对内部类所做的更改传播回 XML 源时遇到了麻烦。
下面是使用站点的 XAML:
<local:SampleConverter x:Key="SampleConverter" />
<Expander Header="Sample" >
<local:SampleControl
Sample="{Binding Path=XmlSource,
Converter={StaticResource SampleConverter},
Mode=TwoWay}" />
</Expander>
XmlSource 是父数据绑定对象的 CLR 读写属性(不是 DependencyProperty)。它是从 XSD 生成的 .NET 类型。
SampleConverter 实现了IValueConverter
。调用 Convert
方法并返回非空数据,但从未调用 ConvertBack
方法。
SampleControl 是一个 UserControl,封装了与 Sample 数据树的 UI 交互。它的 XAML 如下所示:
<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>
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 方法?
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
通过几个类似问题的提示和几乎在这里的答案,我有一个保留绑定的可行解决方案。您可以手动强制绑定更新策略性放置的事件中的源,例如 LostFocus:
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:
我相信这是你的问题。基本 CLR 属性不会通知,因此无法参与双向数据绑定。我的猜测是您正在获得一次性绑定?您的输出窗口中是否有任何数据绑定错误?
您可以尝试创建一个包含 XmlSource 属性的类的包装器,使该类实现 INotifyPropertyChanged,并将 XmlSource 公开为通知属性(它不需要是依赖项属性)。
另外,关于这个评论:
INotifyPropertyChanged 的示例实现通常会在属性本身更改时发出通知,而不是在子属性或其他属性更改时发出通知。但是,您绝对可以随时在任何属性上触发通知事件。例如,您可能有一个“全名”属性,每当“名字”或“姓氏”发生更改时,该属性都会发出通知。
如果上述内容没有帮助,我建议您发布更多的代码——也许是您想要工作的简化重现。
编辑:
我想我误解了你的问题。我认为问题在于你在评论中提到的——它是一个引用类型。可能值得在您的 OnSampleChanged 中尝试一下:
我认为这将迫使绑定系统认识到目标端发生了某些变化。
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:
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:
I think this will force the binding system to recognize that something changed on the target side.
有类似的问题。解决方案如下:
我使用一种方式和一个转换器,而不是两种方式绑定。
转换器将对象(在您的情况下是 xmlsource 属性的父对象)转换(封装)为 viewModel,并将控件绑定到它。
viewModel 的工作方式类似于代理,保存对对象的引用,管理其属性,当然还实现 INotifyPropertyChanged。
在这种情况下,您不需要调用 ConvertBack 方法,因为操作是通过 viewModel 在正确的实例上执行的。
所以你有一个干净的视图,你不需要 xaml.cs 中的任何代码
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