我有這個用戶控制元件代碼隱藏:
public partial class MyControl : UserControl, INotifyPropertyChanged
{
public string MyProperty
{
get => (string)GetValue(MyPropertyProperty);
set
{
SetValue(MyPropertyProperty, value);
PropertyChanged?.Invoke(null, null);
}
}
public event PropertyChangedEventHandler PropertyChanged;
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register(nameof(MyProperty), typeof(string), typeof(string));
public MyControl()
{
InitializeComponent();
}
}
XAML:
<Grid>
<TextBox Text="{Binding MyProperty,
Mode=OneWayToSource,
UpdateSourceTrigger=PropertyChanged,
RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}"
VerticalAlignment="Center"
Height="20"
Margin="5"/>
</Grid>
我的 MainWindow 中有一個這樣的控制元件,當我在“SetValue”行上放置一個斷點并更改 TextBox 的值時,斷點被命中,一切都正常。如果我將 DP 注冊更改為:
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register(nameof(MyProperty), typeof(string), typeof(MyControl));
即使沒有其他任何更改,也不再命中該斷點,并且這是注冊 DP 的更好、正確的方法。
為什么會發生這種情況以及如何解決?
uj5u.com熱心網友回復:
為什么會發生這種情況以及如何解決?
因為您違反了 WPF 約定。而且 XAML 建構式(編譯器)無法猜測您是如何打破這一約定的。事實上,在第一個選項中,您創建了一個常規的 CLR 屬性。而且,如果您嘗試為其設定系結,則很可能會出現編譯錯誤。
按照慣例,根據 XAML 編譯器的作業原理,CLR 包裝器不應包含任何邏輯,除了呼叫 GetValue () 和 SetValue () 并且屬性的所有者應該是宣告它們的類。在這種情況下,編譯器可以找到原始的 DependecyProperty,并且在設定/獲取值時將使用它,而無需呼叫該屬性的 CLR 包裝器。而 CLR 包裝器是為了方便程式員在 Sharp 中“手工編碼”而設計的。
INotifyPropertyChanged 介面看起來也完全沒有意義。DependecyProperty 有自己的更改通知機制,您不需要通過呼叫 PropertyChanged 來復制它。
如果需要跟蹤 DependecyProperty 值的更改,則必須在宣告 DependecyProperty 時指定的回呼方法中執行此操作。
public partial class MyControl : UserControl
{
public string MyProperty
{
get => (string)GetValue(MyPropertyProperty);
set => SetValue(MyPropertyProperty, value);
}
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register(nameof(MyProperty), typeof(string), typeof(MyControl), new PropertyMetadata(string.Empty, MyPropertyChanged));
private static void MyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Here the logic for handling value change
MyControl myControl = (MyControl)d;
// Some Code
}
public MyControl()
{
InitializeComponent();
}
}
我將如何使用 MyPropertyChanged 回呼實作相同的目標?
這是非常錯誤的,但如果需要,您也可以在回呼方法中引發 PropertyChanged。
public partial class MyControl : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string MyProperty
{
get => (string)GetValue(MyPropertyProperty);
set => SetValue(MyPropertyProperty, value);
}
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register(nameof(MyProperty), typeof(string), typeof(MyControl), new PropertyMetadata(string.Empty, MyPropertyChanged));
private static readonly PropertyChangedEventArgs MyPropertyPropertyChangedArgs = new PropertyChangedEventArgs(nameof(MyProperty));
private static void MyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Here the logic for handling value change
MyControl myControl = (MyControl)d;
myControl.PropertyChanged?.Invoke(myControl, MyPropertyPropertyChangedArgs);
// Some Code
}
public MyControl()
{
InitializeComponent();
}
}
當用戶在 TextBox 中鍵入文本時,通知用戶控制元件視窗的 ViewModel 的正確方法是“MyProperty”的值已更改?
如果 VM 需要獲取更改后的 MyProperty 屬性值,則需要在使用控制元件的地方設定對此屬性的系結。
并且已經在 VM 本身中,處理其屬性的更改。通常,VM 的基類實作中有相應的方法。
但是如果沒有這樣的方法,那么你可以使用這個屬性的setter。
例子:
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register(
nameof(MyProperty),
typeof(string),
typeof(MyControl),
new FrameworkPropertyMetadata(string.Empty, MyPropertyChanged) { BindsTwoWayByDefault = true });
<local:MyControl MyProperty="{Binding VmProperty}" .../>
public class MainViewModel: ....
{
public string VmProperty
{
get => _vmProperty;
set
{
if(Set(ref _vmProperty, value))
{
// Some code to handle value change.
}
}
}
}
Additional advice.
Your given implementation is not very suitable for UserControl.
You'd better change it to Custom Control.
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/313951.html
