本文告訴大家如何使用依賴屬性,包括在 UWP 和 WPF 如何使用。
本文不會告訴大家依賴屬性的好處,只是簡單告訴大家如何使用
在 UWP 和 wpf ,如果需要創建自己的依賴屬性,可以使用代碼片,在 VisualStudio 可以使用 propdp
輸入兩個 tab 就可以輸入依賴屬性。
本文最後提供修改的代碼片,可以解決變量名修改出現的界面綁定不刷新。
UWP
什麼時候可以獲取繼承的依賴屬性
依賴屬性一般是不在構造函數寫獲取繼承的屬性的值,因爲一般這時拿到的值都是沒有繼承,請看下面的代碼
創建一個用戶控件 LuenqxuhkRrjbzcf ,在他的構造函數和加載完成事件添加獲得 DataContext 的值
構造: var t = DataContext;
private void LuenqxuhkRrjbzcf_Loaded(object sender, RoutedEventArgs e)
{
var t = DataContext;
}
然後把他加入到其他頁面,這個頁面設置了 DataContext ,但是運行在構造的斷點可以看到拿到的值是空
但是可以在加載完成函數拿到
那麼是在什麼時候纔可以拿到依賴屬性的值?
依賴屬性需要在加邏輯樹纔可以拿到值,所以在加入邏輯樹之後,構造函數是類創建,所以這時不能拿依賴屬性的值。
自定義可繼承依賴屬性
我找了很久,發現 uwp 不支持 FrameworkPropertyMetadata 所以無法自己定義可以繼承的依賴屬性
WPF
後臺綁定 依賴屬性
後臺綁定 依賴屬性可以使用 Binding
Binding bind = new Binding("綁定路徑,就是哪個屬性")
{
Source = 綁定源,如果沒有設置,可以使用 DataContext,
Mode = BindingMode.OneWayToSource
};
一個繼承依賴的類.SetBinding(xx.xProperty, bind);
例如綁定 ViewModel 的 Padding 到 一個 G控件的 Padding, 可以使用下面代碼
Binding bind = new Binding("Padding")
{
Source = ViewModel,
Mode = BindingMode.OneWayToSource
};
G.SetBinding(Border.PaddingProperty, bind);
但我的問題是,綁定只能在包含 G 的類使用?
也就是在 MainPage 寫了 G 這個 類,於是綁定只能寫在 MainPage 類?
實際我用了一個類來測試
我把上面的綁定代碼寫到 ViewModel ,發現還是可以使用。
那麼問題2,如果我的 ViewModel 的綁定屬性是私有的,那麼把綁定寫在ViewModel 裏,那麼是否可以訪問,可以看到,如果寫在ViewModel 的Binding ,那麼這個 Binding 是可以訪問 ViewModel 的屬性,雖然這個屬性是私有的。但是實際綁定需要獲取的不是在創建的時候拿到,所以這時是獲取不到ViewModel 裏的屬性。
我嘗試下面的代碼,把 Padding 設置爲 private ,然後在 ViewModel 裏綁定到他,結果發現無法從綁定獲得。
public partial class MainWindow : Window
{
public MainWindow()
{
ViewModel = new ViewModel();
InitializeComponent();
DataContext = ViewModel;
ViewModel.Board = G;
//Binding bind = new Binding("Padding")
//{
// Source = ViewModel,
// Mode = BindingMode.OneWayToSource
//};
//G.SetBinding(Border.PaddingProperty, bind);
ViewModel.Click();
}
public ViewModel ViewModel { get; set; }
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
G.Padding = new Thickness(G.Padding.Left + 1, G.Padding.Top + 1, G.Padding.Right, G.Padding.Bottom);
Console.WriteLine(ViewModel.Pad());
}
}
public class ViewModel
{
public Thickness Pad()
{
return Padding;
}
private Thickness Padding { get; set; }
public Border Board { get; set; }
public void Click()
{
Binding bind = new Binding("Padding")
{
Source = this,
Mode = BindingMode.OneWayToSource
};
Board.SetBinding(Border.PaddingProperty, bind);
}
}
如果在綁定之前,設置 G 的 Padding 爲一個值,那麼在設置綁定之後,這個值就會被設置默認值。
如果在綁定之前,設置 G 的 Padding 爲20 ,那麼設置綁定之後, G 的 Padding = 0
如果需要保留這個值,可以使用臨時變量。
綁定還有另一個問題,一個屬性只能做一次綁定。
假如我有多個屬性,把這多個屬性綁定在 G 的 Padding ,那麼只有最後的一個綁定可以使用,其他的綁定無法使用。
public Thickness BoardPadding { get; set; }
public void Click()
{
Binding bind = new Binding("Padding")
{
Source = this,
Mode = BindingMode.OneWayToSource
};
BindingOperations.SetBinding(Board, Border.PaddingProperty, bind);
bind = new Binding("BoardPadding")
{
Source = this,
Mode = BindingMode.OneWayToSource
};
Board.SetBinding(Border.PaddingProperty, bind);
}
可以看到,這時 Padding 的值一直沒有。
WPF 獲得依賴屬性值更新
如果需要獲得 G 的 Padding 的值更改,WPF 獲得依賴屬性 值更改可以使用下面代碼
DependencyPropertyDescriptor.FromProperty(Border.PaddingProperty,typeof(Border)).AddValueChanged(Board,
(s, e) =>
{
Padding = Board.Padding;
BoardPadding = Board.Padding;
});
這個方法就是獲得屬性的值更改
但是這個方法會出現內存泄露,可以使用 RemoveValueChanged 清除,爲了使用清除,需要寫一個函數。
不需要擔心清除一個不存在的委託,一般在使用 AddValueChanged 之前都使用 RemoveValueChanged 清除
參見:https://stackoverflow.com/questions/4764916/listen-to-changes-of-dependency-property
初始化出現默認值類型與屬性類型不同
定義的依賴屬性是需要默認值類型和定義的一樣,在一般的代碼,可以使用隱式轉換,但是在定義不可以使用。
例如使用類型是 double 實際給的是 int ,就會在運行出現ArgumentException
public static readonly DependencyProperty FooProperty = DependencyProperty.Register(
"Foo", typeof(double), typeof(MainWindow), new PropertyMetadata(2));
public double Foo
{
get { return (double) GetValue(FooProperty); }
set { SetValue(FooProperty, value); }
}
雖然定義double a=2;
是對的,但是在這裏定義的 2
默認是錯誤的,需要寫2d
纔是對的
修改屬性名稱
默認的代碼片生成代碼的屬性名稱是字符串,但是字符串有個缺點,如果修改了變量名,那麼界面綁定就無法找到。
建議把字符串換爲C# 6.0 帶來的新特性
public static readonly DependencyProperty FooProperty = DependencyProperty.Register(
nameof(Foo), typeof(double), typeof(MainWindow), new PropertyMetadata(2d));
public double Foo
{
get { return (double) GetValue(FooProperty); }
set { SetValue(FooProperty, value); }
}
通過修改代碼片就可以做到,如何修改請看 resharper 自定義代碼片
下面就是修改後的代碼
public static readonly $dependencyProperty$ $propertyName$Property = $dependencyProperty$.Register(
nameof($propertyName$), typeof($propertyType$), typeof($containingType$), new PropertyMetadata(default($propertyType$)));
public $propertyType$ $propertyName$
{
get { return ($propertyType$) GetValue($propertyName$Property); }
set { SetValue($propertyName$Property, value); }
可以直接粘貼進去Resharper的代碼
或者導入我的設置,點擊下載
如果想要使用的是 C# 7 的特性,可以修改代碼片,或者點擊下載導入
public static readonly $dependencyProperty$ $propertyName$Property = $dependencyProperty$.Register(
nameof($propertyName$), typeof($propertyType$), typeof($containingType$), new PropertyMetadata(default($propertyType$)));
public $propertyType$ $propertyName$
{
get => ($propertyType$) GetValue($propertyName$Property);
set => SetValue($propertyName$Property, value);
}
本作品採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。歡迎轉載、使用、重新發布,但務必保留文章署名林德熙(包含鏈接:http://blog.csdn.net/lindexi_gd ),不得用於商業目的,基於本文修改後的作品務必以相同的許可發佈。如有任何疑問,請與我聯繫。