[英]WPF - binding to read-only attached property. Need solution clarification
我試圖將我的視圖模型屬性綁定到文本框的 Validation.HasErrors 附加屬性(這是只讀的)。 我在 Johan Larsson 的這個答案中找到了一個很好的工作解決方案: https ://stackoverflow.com/a/39392158 但我不是 WPF 專家,所以我很難理解它的工作原理和原因。 我真的很困惑,因為我不知道 WPF 和 XAML 引擎的所有隱含規則。 我了解附加屬性、綁定和 XAML 標記的基礎知識,但我不明白它們最終是如何結合在一起的。 有人可以澄清這里發生了什么嗎? 這是解決方案中的代碼:
public static class OneWayToSource
{
public static readonly DependencyProperty BindingsProperty = DependencyProperty.RegisterAttached(
"Bindings",
typeof(OneWayToSourceBindings),
typeof(OneWayToSource),
new PropertyMetadata(default(OneWayToSourceBindings), OnBinidngsChanged));
public static void SetBindings(this FrameworkElement element, OneWayToSourceBindings value)
{
element.SetValue(BindingsProperty, value);
}
[AttachedPropertyBrowsableForChildren(IncludeDescendants = false)]
[AttachedPropertyBrowsableForType(typeof(FrameworkElement))]
public static OneWayToSourceBindings GetBindings(this FrameworkElement element)
{
return (OneWayToSourceBindings)element.GetValue(BindingsProperty);
}
private static void OnBinidngsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((OneWayToSourceBindings)e.OldValue)?.ClearValue(OneWayToSourceBindings.ElementProperty);
((OneWayToSourceBindings)e.NewValue)?.SetValue(OneWayToSourceBindings.ElementProperty, d);
}
}
public class OneWayToSourceBindings : FrameworkElement
{
private static readonly PropertyPath DataContextPath = new PropertyPath(nameof(DataContext));
private static readonly PropertyPath HasErrorPath = new PropertyPath($"({typeof(Validation).Name}.{Validation.HasErrorProperty.Name})");
public static readonly DependencyProperty HasErrorProperty = DependencyProperty.Register(
nameof(HasError),
typeof(bool),
typeof(OneWayToSourceBindings),
new FrameworkPropertyMetadata(default(bool), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
internal static readonly DependencyProperty ElementProperty = DependencyProperty.Register(
"Element",
typeof(UIElement),
typeof(OneWayToSourceBindings),
new PropertyMetadata(default(UIElement), OnElementChanged));
private static readonly DependencyProperty HasErrorProxyProperty = DependencyProperty.RegisterAttached(
"HasErrorProxy",
typeof(bool),
typeof(OneWayToSourceBindings),
new PropertyMetadata(default(bool), OnHasErrorProxyChanged));
public bool HasError
{
get { return (bool)this.GetValue(HasErrorProperty); }
set { this.SetValue(HasErrorProperty, value); }
}
private static void OnHasErrorProxyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
d.SetCurrentValue(HasErrorProperty, e.NewValue);
}
private static void OnElementChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue == null)
{
BindingOperations.ClearBinding(d, DataContextProperty);
BindingOperations.ClearBinding(d, HasErrorProxyProperty);
}
else
{
var dataContextBinding = new Binding
{
Path = DataContextPath,
Mode = BindingMode.OneWay,
Source = e.NewValue
};
BindingOperations.SetBinding(d, DataContextProperty, dataContextBinding);
var hasErrorBinding = new Binding
{
Path = HasErrorPath,
Mode = BindingMode.OneWay,
Source = e.NewValue
};
BindingOperations.SetBinding(d, HasErrorProxyProperty, hasErrorBinding);
}
}
}
Xaml 部分:
<StackPanel>
<TextBox Text="{Binding ValueInVM, UpdateSourceTrigger=PropertyChanged}">
<local:OneWayToSource.Bindings>
<local:OneWayToSourceBindings HasError="{Binding ValueInVM}" />
</local:OneWayToSource.Bindings>
</TextBox>
<CheckBox IsChecked="{Binding ValueInVM, Mode=OneWay}" />
</StackPanel>
您可能會發現這個問題/答案更有幫助。 最受好評的答案(但不是公認的答案......)有一篇相關的博客文章,其中詳細介紹了一些細節。
那篇博文包括PushBinding
和PushBindingManager
- 您需要讓這一切正常工作的兩個主要部分(加上其他幾個小類,但它們並不是什么大事)。
這些比您在帖子中引用的代碼做得更好(在我看來)。 這是我在自己的代碼中使用的(或者,至少是我從中派生出來的)。
首先,要考慮的最關鍵的一點:
只讀依賴屬性沒有公共屬性設置器。 查看Microsoft 文檔中的示例FishCount
屬性:
internal static readonly DependencyPropertyKey FishCountPropertyKey =
DependencyProperty.RegisterReadOnly(
name: "FishCount",
propertyType: typeof(int),
ownerType: typeof(Aquarium),
typeMetadata: new FrameworkPropertyMetadata());
// Declare a public get accessor.
public int FishCount =>
(int)GetValue(FishCountPropertyKey.DependencyProperty);
您可以看到只有一個公共getter - 沒有setter 。 現在,讓我們添加只讀附加屬性狀態的文檔(強調我的)
只讀附加屬性是一種罕見的方案,因為附加屬性的主要方案是它在 XAML 中的使用。 如果沒有公共設置器,則無法在XAML 語法中設置附加屬性。
所以 - 如果我們不能在 XAML 中設置它 - 也許我們可以在 C# 中設置它。
這就是您引用的代碼的作用。
解決這個問題的一般方法是:
Validation.HasErrors
)和特定於實現的屬性(我們稱之為ListenerProperty
)之間創建OneWay
綁定ListenerProperty
的屬性更改事件。ListenerProperty
更改時,更新第二個特定於實現的屬性,例如... MirrorProperty
到相同的值。MirrorProperty
和您的視圖模型之間創建一個OneWayToSource
綁定,以將值推送回您的視圖模型我已經說明了PushBinding
的工作原理(我鏈接的博客文章中的示例)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.