win10 uwp 依賴屬性

本文告訴大家如何使用依賴屬性,包括在 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 ),不得用於商業目的,基於本文修改後的作品務必以相同的許可發佈。如有任何疑問,請與我聯繫

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章