WPF, зависимые атрибуты и дополнительные атрибуты (объяснено более использования в)

Терн: https: //www.cnblogs.com/zhili/p/WPFDependencyProperty.html

I. Введение

  В последнее время чувство декадентской, я не научился писать сообщения в блоге, из чувства вины, сегодня сильная сила сами, чтобы начать обновление серии WPF. Хотя недавно видел технология WPF является Старики статьи, но я до сих пор не может остановить систематическое изучение WPF. Сегодня продолжают делиться одним из самых важных знаний в WPF - зависимые свойства.

Во-вторых, комплексный анализ свойства зависимостей

  Услышьте свойство зависимостей, естественно думать о концепции в атрибутах C #. атрибуты C # является ядром абстрактной модели, которая основана именно на собственности WPF зависимостей, созданной. В реализации библиотеки WPF, свойство зависимостей, используя обычные C # атрибутов упаковки, так что мы можем использовать свойство зависимостей через прежде, чем таким же образом, но мы должны быть ясно, в WPF большинство из нас использует свойство зависимостей, а не использование имущества. Важность свойства зависимостей, что основные функции WPF, такие как анимация, привязка данных и стили должны полагаться на использование собственности. С введением свойств зависимостей WPF, естественно, есть причина, его введение. свойство зависимостей WPF в основном имеет следующие три преимущества:

  • атрибут Dependency для уведомления об изменении атрибутов, ограниченные функции проверки. Это позволяет более легко реализовать приложения, при этом значительно уменьшая количество кода. Перед многими функциями нужно писать много кода, чтобы достичь, вы можете легко реализовать в WPF.
  • Сохранить память: В WinForm, каждый атрибут управления пользовательского интерфейса дали начальное значение, так что каждый элемент управления сохранит копию одних и тех же начальных значений в памяти. Свойство зависимостей WPF решает эту проблему с помощью хэш - таблицы , чтобы достичь внутреннего механизма хранения, для одних и тех же значений атрибутов из множества одинаковых элементов управления только сохранить копию. Более подробно о том , как сохранить память о зависимости ссылки свойства: свойства зависимостей WPF является , как сохранить память
  • Обеспечение поддержки для различных объектов: зависимое значение атрибута может быть установлен несколькими способами. Он может быть использован с выражениями, стилем и переплетом, чтобы установить значение свойства зависимостей.

Атрибут определяется в зависимости 2.1

  Выше описывает преимущества зависят от свойств привели к этому времени, этот вопрос еще раз, как их собственное определение зависимого его атрибут? C # определяются все атрибуты слишком хорошо знакомы. Следующий C # переписывают атрибут-зависимым образом, чтобы ввести атрибуты, определенные зависимой собственности. Вот определение свойства:

1 открытый класс Person 
2 { 
3 публичная строка Name {получить; задавать; } 
6}

  В приведенных выше свойствах, прежде чем зависимая атрибут переписана, следующие шаги приведены ниже определений свойства зависимостей:

  1. Пусть типа , где свойство зависимостей , унаследованных от DependencyObject класса.
  2. Используйте объявить публичный статический DependencyProperty переменной, переменная является недвижимая зависимость.
  3. В типе статического конструктора по регистру методы завершения использует атрибуты метаданных регистра.
  4. Обеспечивая атрибут свойства пакета зависимостей атрибута этого для завершения операции чтения и записи свойств зависимостей.

  Вышеприведенные четыре шага ниже свойства Name быть переписаны на свойства зависимостей конкретного кода реализации следующим образом:

Скопировать код
@ 1 так DependencyObject типа наследует класс 
    общественного класса Люди: DependencyObject 
    { 
        // объявить статическое поле только для чтения 2. DependencyProperty 
        общественности статической Readonly DependencyProperty NameProperty; 
       
        статическое лицо () 
        { 
            // регистр определения 3. свойство зависимостей 
            nameProperty = DependencyProperty .Register ( "Имя", TypeOf (String), TypeOf (Человек), 
                новый новый PropertyMetadata ( "Hard Learning", OnValueChanged)); 
        } 

        // обертка 4. свойства, чтобы прочитать и установить зависимость мы только зарегистрированную с ним свойства 
        общественного Строка Имя 
        { 
            GET {возвращение (строка) ПолучитьЗначение (NameProperty);} 
            SET {SetValue (NameProperty, значение);} 
        }

        статическая сила OnValueChanged Private (DependencyObject dpobj, DependencyPropertyChangedEventArgs E) 
        { 
            // только тогда , когда происходит изменение метода обратного вызова 
        } 

    }
Скопировать код

  Как видно из приведенной выше коды, зависимость свойства выполняется путем вызова чтения и записи DependencyObject SetValue и GetValue зависимых свойств . Он использует хэш - таблицу для хранения соответствующего значения атрибута HashCode ключ, значение (значение) регистрируется DependencyPropery; С # в классе обертки является полем частного атрибута, может работать поле читать и писать атрибуты. Резюмировать следующим образом : Атрибут поля упаковки, WPF с использованием свойств зависимостей атрибутов упаковки.

2,2 в зависимости от атрибута приоритета

   свойство зависимостей WPF позволяет уставку в нескольких местах, естественно, относится к проблеме свойства зависимостей приобретает приоритетное значение. Например, следующий код XMAL, мы устанавливаем цвет фона кнопки в трех местах, и что в конце концов, кнопка считывает значения, которые устанавливают его? Является ли зеленый, желтый или красный?

Скопировать код
<Window х: Class = "DPSample.MainWindow" 
        Xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
        XMLNS: х = "http://schemas.microsoft.com/winfx/2006 / XAML» 
        Название = "MainWindow" Высота = "350" ширина = "525"> 
    <Сетка> 
        <Кнопка х: Name = "" MyButton фона = "зеленый" Ширина = "100" высота = "30"> 
            <Кнопка. Style> 
                <Style TargetType = "{х: Кнопка Тип}"> 
                    <Setter Property = "Фон" Value = "Yellow" /> 
                    <Style.Triggers>
                        <Trigger Property = "IsMouseOver" Value = "True"> 
                            <SetterСвойство = "Фон" Value = "Red" /> 
                        </ Trigger> 
                    </Style.Triggers> 
                </ Style> 
            </Button.Style> 
            Click Me 
        </ Button> 
    </ Grid> 
</ Window>
Скопировать код

  Цвет фона выше кнопки зеленый. Причина, почему цвет фона Зеленый, из-за свойство зависимостей WPF каждый доступ, он будет в следующем порядке от высокого значения в конце процесса. ПОДРОБНОЕ приоритет, как показано ниже:

  В приведенном выше XAML, локальное значение кнопки зеленого цвета, пользовательский стиль Trigger набор для красного, стиль сеттер пользовательские настройки для желтого, с наивысшим приоритетом, где локальное значение, поэтому цвет кнопки фона, или Зеленый является значение. Если в это время значение локального Грина затем удаляется, цвет фона кнопки в это время не желтые красный. Здесь Несмотря Стиль Trigger приоритет выше, чем стиль сеттер, а потому, что в это время IsMouseOver Стиль Trigger собственности является ложным, то есть мышь не находится над кнопкой, когда мыши на кнопку, и цвет кнопки красным светом. В это время будет отражать приоритет Стиль Trigger выше, чем приоритет Стиль сеттер. Таким образом, цифра выше приоритета является идеальной ситуацией, часто требуется конкретный анализ.

2.3 зависимость наследования имущества

  Зависимость от атрибута может наследоваться, то есть, настройки родительского элемента будут автоматически передаются на все дочерние элементы. Следующий код демонстрирует наследование свойств зависимостей.

Скопировать код
<Окно х: Class = "Custom_DPInherited.DPInherited" 
      Xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      XMLNS: х = "http://schemas.microsoft.com/winfx/2006 / XAML " 
      XMLNS: MC = "http://schemas.openxmlformats.org/markup-compatibility/2006" 
      XMLNS: d = "http://schemas.microsoft.com/expression/blend/2008" 
      MC: Ignorable =" д» 
      д: DesignHeight = "300" д: DesignWidth = "300" 
      FontSize = "18" 
      Название = "依赖属性的继承"> 
    <StackPanel> 
        <Метка Содержание = "继承自окно的FontSize"/> 
        <Метка Содержание = "显式设置FontSize" 
               TextElement.= FontSize "36" /> 
        <Строка состояния> Строка состояния не наследуется из окна FontSize </ StatusBar>
    </ StackPanel> 
</ Window>
Скопировать код

Операционные результаты указанного выше кода показан ниже:

  В приведенном выше коде XAML. Настройки Window.FontSize влияют на все дочерние элементы внутри размера шрифта, который зависит от наследственного имущества. Этикетка не определена в качестве первого FontSize, поэтому он наследует значение Window.FontSize. Однако после того, как дочерний элемент обеспечивает явно, это наследование будет прервано, так что чем больше значения второго ярлыка Window.FontSize нет работы.

  На данный момент, вы, возможно, нашли проблему: StatusBar в явном виде не установлено значение FontSize, но он не наследует значение Window.FontSize размера шрифта, но сохранить значение по умолчанию системы. Ну, это то, что вызывает это? На самом деле, это приводит к вопросу: Не все элементы поддерживаются наследование значения свойства, как и StatusBar, Tooptip и управление меню. Кроме того, StatusBar и другие элементы управления перехваченных наследуется от родительского элемента к свойству, и свойство не будет влиять на дочерний элемент управления StatusBar. Например, если мы добавим кнопку StatusBar. Затем FontSize изменение свойств этой кнопки не происходит, что является значением по умолчанию.

  Введенный перед зависимости наследования имущества, то , как мы полагаемся на пользовательские свойства , которые будут наследоваться другими элементами управления это? По AddOwer может зависеть от свойств , унаследованных методов. Конкретный код реализации приведен ниже:

Скопировать код
 1  public class CustomStackPanel : StackPanel
 2     {
 3         public static readonly DependencyProperty MinDateProperty;
 4 
 5         static CustomStackPanel()
 6         {
 7             MinDateProperty = DependencyProperty.Register("MinDate", typeof(DateTime), typeof(CustomStackPanel), new FrameworkPropertyMetadata(DateTime.MinValue, FrameworkPropertyMetadataOptions.Inherits));
 8         }
 9 
10         public DateTime MinDate
11         {
12             get { return (DateTime)GetValue(MinDateProperty); }
13             set { SetValue(MinDateProperty, value); }
14         }
15     }
16 
17     public class CustomButton :Button
18     {
19         private static readonly DependencyProperty MinDateProperty;
20 
21         static CustomButton()
22         {
23             // AddOwner方法指定依赖属性的所有者,从而实现依赖属性的继承,即CustomStackPanel的MinDate属性被CustomButton控件继承。
24             // 注意FrameworkPropertyMetadataOptions的值为Inherits
25             MinDateProperty = CustomStackPanel.MinDateProperty.AddOwner(typeof(CustomButton), new FrameworkPropertyMetadata(DateTime.MinValue, FrameworkPropertyMetadataOptions.Inherits));
26         }
27 
28         public DateTime MinDate
29         {
30             get { return (DateTime)GetValue(MinDateProperty); }
31             set { SetValue(MinDateProperty, value); }
32         }
33     }
Скопировать код

  接下来,你可以在XAML中进行测试使用,具体的XAML代码如下:

Скопировать код
<Window x:Class="Custom_DPInherited.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:Custom_DPInherited"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        Title="实现自定义依赖属性的继承" Height="350" Width="525">
    <Grid>
        <local:CustomStackPanel x:Name="customStackPanle" MinDate="{x:Static sys:DateTime.Now}">
            <!--CustomStackPanel的依赖属性-->
            <ContentPresenter Content="{Binding Path=MinDate, ElementName=customStackPanle}"/>
            <local:CustomButton Content="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=MinDate}" Height="25"/>
        </local:CustomStackPanel>
    </Grid>
</Window>
Скопировать код

  上面XAML代码中,显示设置了CustomStackPanel的MinDate的值,而在CustomButton中却没有显式设置其MinDate值。CustomButton的Content属性的值是通过绑定MinDate属性来进行获取的,关于绑定的更多内容会在后面文章中分享。在这里CustomButton中并没有设置MinDate的值,但是CustomButton的Content的值却是当前的时间,从而可以看出,此时CustomButton的MinDate属性继承了CustomStackPanel的MinDate的值,从而设置了其Content属性。最终的效果如下图所示:

2.4 只读依赖属性

   在C#属性中,我们可以通过设置只读属性来防止外界恶意更改该属性值,同样,在WPF中也可以设置只读依赖属性。如IsMouseOver就是一个只读依赖属性。那我们如何创建一个只读依赖属性呢?其实只读的依赖属性的定义方式与一般依赖属性的定义方式基本一样。只读依赖属性仅仅是用DependencyProperty.RegisterReadonly替换了DependencyProperty.Register而已。下面代码实现了一个只读依赖属性。

Скопировать код
 1 public partial class MainWindow : Window
 2     {
 3         public MainWindow()
 4         {
 5             InitializeComponent();
 6 
 7             // 内部使用SetValue来设置值
 8             SetValue(counterKey, 8);
 9         }
10 
11         // 属性包装器,只提供GetValue,你也可以设置一个private的SetValue进行限制。
12         public int Counter
13         {
14             get { return (int)GetValue(counterKey.DependencyProperty); }
15         }
16 
17         // 使用RegisterReadOnly来代替Register来注册一个只读的依赖属性
18         private static readonly DependencyPropertyKey counterKey =
19             DependencyProperty.RegisterReadOnly("Counter",
20             typeof(int),
21             typeof(MainWindow),
22             new PropertyMetadata(0));
23     }
Скопировать код

  对应的XAML代码为:

Скопировать код
<Window x:Class="ReadOnlyDP.MainWindow" 
        Name="ThisWin"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ReadOnly Dependency Property" Height="350" Width="525">
    <Grid>
        <Viewbox>
            <TextBlock Text="{Binding ElementName=ThisWin, Path=Counter}"/>
        </Viewbox>
    </Grid>
</Window>
Скопировать код

  此时Counter包装的counterKey就是一个只读依赖属性,因为其定义为private的,所以在类外也不能使用DependencyObject.SetValue方法来对其值,而包装的Counter属性又只提供了GetValue方法,所以类外部只能对该依赖属性进行读取,而不能对其赋值。此时运行效果如下图所示。

2.5 附加属性

  WPF中还有一类特殊的属性——附加属性。附加是一种特殊的依赖属性。它允许给一个对象添加一个值,而该对象可能对这个值一无所知。附加属性最常见的例子就是布局容器中DockPanel类中的Dock附加属性和Grid类中Row和Column附加属性。那问题又来了,我们怎样在自己的类中定义一个附加属性呢?其实定义附加属性和定义一般的依赖属性一样没什么区别,只是用RegisterAttached方法代替了Register方法罢了。下面代码演示了附加属性的定义。

Скопировать код
public class AttachedPropertyClass
    {
        // 通过使用RegisterAttached来注册一个附加属性
        public static readonly DependencyProperty IsAttachedProperty =
            DependencyProperty.RegisterAttached("IsAttached", typeof(bool), typeof(AttachedPropertyClass),
            new FrameworkPropertyMetadata((bool)false));

        // 通过静态方法的形式暴露读的操作
        public static bool GetIsAttached(DependencyObject dpo)
        {
            return (bool)dpo.GetValue(IsAttachedProperty);
        }

        public static void SetIsAttached(DependencyObject dpo, bool value)
        {
            dpo.SetValue(IsAttachedProperty, value);
        }
    }
Скопировать код

  在上面代码中,IsAttached就是一个附加属性,附加属性没有采用CLR属性进行封装,而是使用静态SetIsAttached方法和GetIsAttached方法来存取IsAttached值。这两个静态方法内部一样是调用SetValue和GetValue来对附加属性读写的。

2.6 依赖属性验证和强制

   在定义任何类型的属性时,都需要考虑错误设置属性的可能性。对于传统的CLR属性,可以在属性的设置器中进行属性值的验证,不满足条件的值可以抛出异常。但对于依赖属性来说,这种方法不合适,因为依赖属性通过SetValue方法来直接设置其值的。然而WPF有其代替的方式,WPF中提供了两种方法来用于验证依赖属性的值。

  • ValidateValueCallback:该回调函数可以接受或拒绝新值。该值可作为DependencyProperty.Register方法的一个参数。
  • CoerceValueCallback:该回调函数可将新值强制修改为可被接受的值。例如某个依赖属性Age的值范围是0到120,在该回调函数中,可以对设置的值进行强制修改,对于不满足条件的值,强制修改为满足条件的值。如当设置为负值时,可强制修改为0。该回调函数可作为PropertyMetadata构造函数参数进行传递。

  当应用程序设置一个依赖属性时,所涉及的验证过程如下所示:

  1. 首先,CoerceValueCallback方法可以修改提供的值或返回DependencyProperty.UnsetValue
  2. 如果CoerceValueCallback方法强制修改了提供的值,此时会激活ValidateValueCallback方法进行验证,如果该方法返回为true,表示该值合法,被认为可被接受的,否则拒绝该值。不像CoerceValueCallback方法,ValidateValueCallback方法不能访问设置属性的实际对象,这意味着你不能检查其他属性值。即该方法中不能对类的其他属性值进行访问。
  3. 如果上面两个阶段都成功的话,最后会触发PropertyChangedCallback方法来触发依赖属性值的更改。

  下面代码演示了基本的流程。

Скопировать код
 1 class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             SimpleDPClass sDPClass = new SimpleDPClass();
 6             sDPClass.SimpleDP = 2;
 7             Console.ReadLine();
 8         }
 9     }
10 
11     public class SimpleDPClass : DependencyObject
12     {
13         public static readonly DependencyProperty SimpleDPProperty =
14             DependencyProperty.Register("SimpleDP", typeof(double), typeof(SimpleDPClass),
15                 new FrameworkPropertyMetadata((double)0.0,
16                     FrameworkPropertyMetadataOptions.None,
17                     new PropertyChangedCallback(OnValueChanged),
18                     new CoerceValueCallback(CoerceValue)),
19                     new ValidateValueCallback(IsValidValue));
20 
21         public double SimpleDP
22         {
23             get { return (double)GetValue(SimpleDPProperty); }
24             set { SetValue(SimpleDPProperty, value); }
25         }
26 
27         private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
28         {
29             Console.WriteLine("当值改变时,我们可以做的一些操作,具体可以在这里定义: {0}", e.NewValue);
30         }
31 
32         private static object CoerceValue(DependencyObject d, object value)
33         {
34             Console.WriteLine("对值进行限定,强制值: {0}", value);
35             return value;
36         }
37 
38         private static bool IsValidValue(object value)
39         {
40             Console.WriteLine("验证值是否通过,返回bool值,如果返回True表示验证通过,否则会以异常的形式暴露: {0}", value);
41             return true;
42         }
43     }
Скопировать код

  其运行结果如下图所示:

  从运行结果可以看出,此时并没有按照上面的流程先Coerce后Validate的顺序执行,这可能是WPF内部做了一些特殊的处理。当属性被改变时,首先会调用Validate来判断传入的value是否有效,如果无效就不继续后续操作。并且CoerceValue后面并没有运行ValidateValue,而是直接调用PropertyChanged。这是因为CoerceValue操作并没有强制改变属性的值,而前面对这个值已经验证过了,所以也就没有必要再运行Valudate方法来进行验证了。但是如果在Coerce中改变了Value的值,那么还会再次调用Valudate操作来验证值是否合法。

2.7  依赖属性的监听

  我们可以用两种方法对依赖属性的改变进行监听。这两种方法是:

  下面分别使用这两种方式来实现下对依赖属性的监听。

  第一种方式:定义一个派生于依赖属性所在的类,然后重写依赖属性的元数据并传递一个PropertyChangedCallback参数即可,具体的实现如下代码所示:

Скопировать код
 1 public class MyTextBox : TextBox
 2     {
 3         public MyTextBox()
 4             : base()
 5         {
 6         }
 7 
 8         static MyTextBox()
 9         {
10             //第一种方法,通过OverrideMetadata
11             TextProperty.OverrideMetadata(typeof(MyTextBox), new FrameworkPropertyMetadata(new PropertyChangedCallback(TextPropertyChanged)));
12         }
13 
14         private static void TextPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
15         {
16             MessageBox.Show("", "Changed");
17         }
18     }
Скопировать код

  第二种方法:这个方法更加简单,获取DependencyPropertyDescriptor并调用AddValueChange方法为其绑定一个回调函数。具体实现代码如下所示:

Скопировать код
 public MainWindow()
        {
            InitializeComponent();
            //第二种方法,通过OverrideMetadata
            DependencyPropertyDescriptor descriptor = DependencyPropertyDescriptor.FromProperty(TextBox.TextProperty, typeof(TextBox));
            descriptor.AddValueChanged(tbxEditMe, tbxEditMe_TextChanged);
        }

        private void tbxEditMe_TextChanged(object sender, EventArgs e)
        {
            MessageBox.Show("", "Changed");
        }
Скопировать код

三、总结

   到这里,依赖属性的介绍就结束了。WPF中的依赖属性通过一个静态只读字段进行定义,并且在静态构造函数中进行注册,最后通过.NET传统属性进行包装,使其使用与传统的.NET属性并无两样。在后面一篇文章将分享WPF中新的事件机制——路由事件。

  Эта статья загрузить весь исходный код: DependencyPropertyDemo.zip

рекомендация

отwww.cnblogs.com/lsgsanxiao/p/11330655.html