WPF:依赖属性与常规CLR属性的区别是什么?

时间:2010-02-24 01:40:37

标签: wpf dependency-properties

在WPF中,成为“依赖属性”意味着什么呢?

我读过微软的Dependency Properties Overview,但它并没有真正吸引我。部分文章说:

  

样式和模板是使用依赖项属性的两个主要激励方案。样式对于设置定义应用程序用户界面(UI)的属性特别有用。样式通常定义为XAML中的资源。样式与属性系统交互,因为它们通常包含特定属性的“setter”,以及根据另一个属性的实时值更改属性值的“触发器”。

然后示例代码是这样的:

<Style x:Key="GreenButtonStyle">
  <Setter Property="Control.Background" Value="Green"/>
</Style>
....
<Button Style="{StaticResource GreenButtonStyle}">I am green!</Button>

但我没有得到有关此事的特别之处。是否只是暗示,当我将按钮上的Style设置为给定样式时,我实际上是在隐式设置Background?这是它的关键吗?

4 个答案:

答案 0 :(得分:27)

答案 1 :(得分:9)

了解依赖属性试图解决的问题可能会有所帮助。

如果我们将Binding,Animation和Change Event模型放在一边,就像在其他答案中讨论过一样,那么好处就是内存使用,因此可以在窗口中托管数千个WPF对象。

如果一个窗口包含1000个Label个对象,每个Label对象具有通常的ForegroundBackgroundFontFamilyFontSize,{ {1}}等等,传统上这会消耗内存,因为每个属性都有一个私有支持字段来存储值。

大多数应用程序只会更改少数属性,其中大部分将保留其默认值。基本上非常浪费和冗余的信息(每个对象只在内存中保存相同的默认值)

这是依赖属性不同的地方。

FontWeight

没有私人支持字段。注册依赖项属性时,可以指定默认值。因此,在大多数情况下,// Lets register the Dependency Property with a default value of 20.5 public static readonly DependencyProperty ColumnWidthProperty = DependencyProperty.Register("ColumnWidth", typeof(double), typeof(MyWPFControl), new UIPropertyMetadata(20.5, ColWitdhPropChanged)); public double ColumnWidth { get { return (double)GetValue(ColumnWidthProperty); } set { SetValue(ColumnWidthProperty, value); } } 返回的值是默认值,该值仅存储一次,以覆盖应用程序所有窗口中GetValue对象的所有实例。

使用Label设置依赖项属性时,它会将非默认值存储在由对象实例标识的集合中,以便在所有后续SetValue调用中返回。

因此,此存储方法仅消耗已从默认值更改的WPF对象的属性的内存。即仅与默认值的差异。

答案 2 :(得分:8)

  

在WPF中,成为“依赖属性”意味着什么呢?

为了成为依赖属性,该属性实际上必须在类上静态地定义为DependencyProperty。依赖属性系统与标准CLR属性非常不同。

但是,

依赖性属性的处理方式却截然不同。类型定义依赖项属性静态,并提供默认值。在需要之前,运行时实际上不会为实例生成值。这提供了一个好处 - 在请求类型之前该属性不存在,因此您可以拥有大量属性而无需开销。

这就是使样式工作属性的原因,但同样重要的是允许附加属性,通过可视树的属性“继承”以及WPF所依赖的许多其他东西。

例如,获取DataContext依赖项属性。通常,您为Window或UserControl设置DataContext依赖项属性。默认情况下,该窗口中的所有控件都会自动“继承”其父级DataContext proeprty,这允许您为控件指定数据绑定。使用标准CLR属性,您需要为窗口中的每个控件定义DataContext,以便使绑定正常工作。

答案 3 :(得分:0)

简单/基本差异 - 更改通知:对更改依赖属性的更改在UI中在更改时反映/刷新,而CLR属性则不会。

<Window x:Class="SampleWPF.MainWindow"
        x:Name="MainForm"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:SampleWPF"
        Title="About WPF Unleashed" SizeToContent="WidthAndHeight"
        Background="OrangeRed"
        >
    <StackPanel DataContext="{Binding ElementName=MainForm}">
        <!-- Bind to Dependency Property -->
        <Label Name="txtCount1" FontWeight="Bold" FontSize="20" Content="{Binding ElementName=MainForm, Path=Count1, Mode=OneWay}" />

        <!-- Bind to CLR Property -->
        <Label Name="txtCount2" Content="{Binding ElementName=MainForm, Path=Count2, Mode=OneWay}"></Label>

        <!-- Bind to Dependency Property (Using DataContext declared in StackPanel) -->
        <Label Name="txtCount3" FontWeight="Bold" FontSize="20" Content="{Binding Count1}" />

        <!-- Child Control binding to Dependency Property (Which propagates down element tree) -->
        <local:UserControl1 />

        <!-- Child Control binding to CLR Property (Won't work as CLR properties don't propagate down element tree) -->
        <local:UserControl2 />

        <TextBox Text="{Binding ElementName=txtCount1, Path=Content}" ></TextBox>
        <TextBox Text="{Binding ElementName=txtCount2, Path=Content}" ></TextBox>

        <Button Name="btnButton1" Click="btnButton1_Click_1">Increment1</Button>
        <Button Name="btnButton2" Click="btnButton1_Click_2">Increment2</Button>
    </StackPanel>
</Window>

<UserControl x:Class="SampleWPF.UserControl1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="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="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <StackPanel>
        <Label Content="{Binding Count1}" ></Label>
        <!--
        <Label Content="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=Count1}"></Label>
        -->
    </StackPanel>
</UserControl>

<UserControl x:Class="SampleWPF.UserControl2"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="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="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <StackPanel>
        <Label Content="{Binding Count2}" ></Label>
        <!--
        <Label Content="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=Count2}"></Label>
        -->
    </StackPanel>
</UserControl>

这里的代码(声明CLR和Dependency属性):

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    namespace SampleWPF
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            public static readonly DependencyProperty Count1Property;
            private int _Count2 = 2;
            public int Count2
            {
                get { return _Count2; }
                set { _Count2 = value; }
            }
            public MainWindow()
            {
                return;
            }
            static MainWindow()
            {
                // Register the property
                MainWindow.Count1Property = 
                    DependencyProperty.Register("Count1",
                    typeof(int), typeof(MainWindow),
                    new FrameworkPropertyMetadata(1,
                    new PropertyChangedCallback(OnCount1Changed)));
            }
            // A .NET property wrapper (optional)
            public int Count1
            {
                get { return (int)GetValue(MainWindow.Count1Property); }
                set { SetValue(MainWindow.Count1Property, value); }
            }
            // A property changed callback (optional)
            private static void OnCount1Changed(
              DependencyObject o, DependencyPropertyChangedEventArgs e) {

            }
            private void btnButton1_Click_1(object sender, RoutedEventArgs e)
            {
                Count1++;
            }
            private void btnButton1_Click_2(object sender, RoutedEventArgs e)
            {
                Count2++;
            }
        }
    }

依赖属性提供的另一个功能是值继承 - 顶级元素中设置的值沿元素树向下传播 - 在以下从http://en.csharp-online.net获取的示例中,在“Window”标记上声明的FontSize和FontStyle应用于所有子级下面的元素:

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  Title="About WPF Unleashed" SizeToContent="WidthAndHeight"
  FontSize="30" FontStyle="Italic"
  Background="OrangeRed">
  <StackPanel>
    <Label FontWeight="Bold" FontSize="20" Foreground="White">
      WPF Unleashed (Version 3.0)
    </Label>
    <Label>© 2006 SAMS Publishing</Label>
    <Label>Installed Chapters:</Label>
    <ListBox>
      <ListBoxItem>Chapter 1</ListBoxItem>
      <ListBoxItem>Chapter 2</ListBoxItem>
    </ListBox>
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
      <Button MinWidth="75" Margin="10">Help</Button>
      <Button MinWidth="75" Margin="10">OK</Button>
    </StackPanel>
    <StatusBar>You have successfully registered this product.</StatusBar>
  </StackPanel>
</Window>

<强>参考文献: http://www.codeproject.com/Articles/29054/WPF-Data-Binding-Part-1 http://en.csharp-online.net/WPF_Concepts%E2%80%94Property_Value_Inheritance