wpf - 如何使用子定义的附加属性?

标签 wpf dependency-properties attached-properties

1. 问题

众所周知,Attached Property广泛扩展了wpf中的property系统。但是我们熟悉的例子几乎都是父定义 的,例如 DockPanel.Dock/Grid.Row 等等。 但是在查看MSDN中的文档后,我发现Attached Property还有一些其他用法:

From MSDN:Attached Properties Overview / How Attached Properties Are Used by the Owning Type

1.The type that defines the attached property is designed so that it can be the parent element of the elements that will set values for the attached property. The type then iterates its child objects through internal logic against some object tree structure, obtains the values, and acts on those values in some manner.(Parent-Defined)

2.The type that defines the attached property will be used as the child element for a variety of possible parent elements and content models.(Child-Defined)

3.The type that defines the attached property represents a service. Other types set values for the attached property. Then, when the element that set the property is evaluated in the context of the service, the attached property values are obtained through internal logic of the service class.(Use as a Common Service)



2.试用

由于附加属性可以由用户定义,我想也许我们可以使用“CallBackMethod”来处理它。所以我编写了一些试验来验证我的想法(第 4 部分代码):

1.Customize a child control ("Son") which defined a attached property named "CellBorderThicknessProperty" and use "PropertyChangedCallBack" to update the layout;

2.Create a parent control ("Father") which's template contains the child control.

3.Use the parent control in a Window and set the child.CellBorderThickness's value;



3. 问题

1.As you see,It's not a good way to expose the "Parent Type" in "Child Type",especially we won't how many Parents there would be...

2.This Trial didnt work well,'Cus when the "PropertyChangedCallBack" was fired,the Father's template was not applied yet!So,Father.SetBorderThickness() will do nothing!



有没有使用过 Child-Defined 附加属性的例子?它是如何工作的?

我非常想知道 MS 开发人员如何处理 Child-Defined 的。

例如: 怎么样? WPFToolkit 中的 ScrollViwer.VerticalScrollBarVisibility:DataGrid ?

4. 代码

儿子控制( child )
<Style TargetType={x:Type Son}>
   <Setter Property="Template">
     <Setter.Value>
       <ControlTemplate>
         <Border BorderThickness={TemplateBinding CellBorderThickness}>
           ...
         </Border>
       </ControlTemplate>
     <Setter.Value>
   </Setter>


 public class Son: Control
 {
      public static readonly DependencyProperty CellBorderThicknessProperty = DependencyProperty.RegisterAttached("CellBorderThickness", typeof(Thickness), typeof(Son), new FrameworkPropertyMetadata(new Thickness(0.2), FrameworkPropertyMetadataOptions.AffectsRender, CellBorderThicknessProperty_ChangedCallBack));

      public static void SetCellBorderThickness(UIElement obj, Thickness value)
      {
        obj.SetValue(Son.CellBorderThicknessProperty, value);
      }

      public static Thickness GetCellBorderThickness(UIElement obj)
      {
        return (Thickness)obj.GetValue(Son.CellBorderThicknessProperty);
      }

      public Thickness CellBorderThickness
      {
        //With this, CellBorderThickness can be used as a normal dependency property.
        get { return (Thickness)GetValue(CellBorderThicknessProperty); }
        set { SetValue(CellBorderThicknessProperty, value); }
      }

      static void CellBorderThicknessProperty_ChangedCallBack(DependencyObject d, DependencyPropertyChangedEventArgs e)
      {
        if ((d as Father) != null)
        {
            // Try to update the Son's CellBorderThickness which is the child element of  the Father.
            d.SetBorderThickness(e.NewValue);
        }
      }
 }

父控制(父)
 <Style TargetType={x:Type Father}>
   <Setter Property="Template">
     <Setter.Value>
       <ControlTemplate>
         <Border>
           <Son></Son>
         </Border>
       </ControlTemplate>
     <Setter.Value>
   </Setter>


 public class Father:Control
 {
   private Son childControl;

   public void override OnApplyTemplate()
   {
     childControl=(Son)GetTemplateChild("PART_ChildControl");//Here is a problem!!
   }

   public void SetBorderThickness(Thickness value)
   {
     if(childControl==null)
       childControl.CellBorderThickness=value;
   }
 }

window
 <Window>
   <Grid>
     <Father Son.CellBorderThichness="5"></Father>
   </Grid>
 <Window>

最佳答案

我以你的例子为基础,去掉了太多,得到了这个例子。我删除了它们以显示一个最小的工作示例。

首先,我删除了 CellBorderThickness属性,因为已经附加了依赖属性。

Son

// Removed
public Thickness CellBorderThickness
{
    get { return (Thickness)GetValue(CellBorderThicknessProperty); }
    set { SetValue(CellBorderThicknessProperty, value); }
}

在我父亲的控制中,我删除了 OnApplyTemplate() , 并在函数中 SetBorderThickness()利用附加依赖属性的机会来设置值:

Father
// Removed
OnApplyTemplate() { ... }

// Add
Son.SetCellBorderThickness(childControl, value);

下面是一个完整的例子。示例结构:

enter image description here

XAML
Styles
儿子
<Style TargetType="{x:Type SonNamespace:Son}">
    <Setter Property="Background" Value="Gainsboro" />
    <Setter Property="BorderBrush" Value="Black" />
    <Setter Property="Width" Value="100" />
    <Setter Property="Height" Value="100" />
    <Setter Property="HorizontalAlignment" Value="Left" />
    <Setter Property="HorizontalContentAlignment" Value="Center" />
    <Setter Property="VerticalContentAlignment" Value="Center" />

    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate>
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding SonNamespace:Son.CellBorderThickness}">

                    <ContentPresenter Content="I'am a Son" 
                                      HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                      VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

父亲
<Style TargetType="{x:Type FatherNamespace:Father}">
    <Setter Property="Background" Value="AliceBlue" />
    <Setter Property="BorderBrush" Value="Black" />
    <Setter Property="Width" Value="100" />
    <Setter Property="Height" Value="100" />
    <Setter Property="HorizontalAlignment" Value="Right" />
    <Setter Property="HorizontalContentAlignment" Value="Center" />
    <Setter Property="VerticalContentAlignment" Value="Center" />

    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate>
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding SonNamespace:Son.CellBorderThickness}">

                    <ContentPresenter Content="I'am a Father" 
                                      HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                      VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

主窗口
<Grid>
    <SonNamespace:Son />

    <FatherNamespace:Father SonNamespace:Son.CellBorderThickness="6" />
</Grid>

Code

儿子
public class Son : Control
{
    public static readonly DependencyProperty CellBorderThicknessProperty =
                                              DependencyProperty.RegisterAttached("CellBorderThickness",
                                              typeof(Thickness), typeof(Son),
                                              new FrameworkPropertyMetadata(new Thickness(2),
                                                                            FrameworkPropertyMetadataOptions.AffectsRender,
                                                                            CellBorderThicknessProperty_ChangedCallBack));

    public static void SetCellBorderThickness(UIElement obj, Thickness value)
    {
        obj.SetValue(Son.CellBorderThicknessProperty, value);
    }

    public static Thickness GetCellBorderThickness(UIElement obj)
    {
        return (Thickness)obj.GetValue(Son.CellBorderThicknessProperty);
    }

    private static void CellBorderThicknessProperty_ChangedCallBack(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Father father = d as Father;

        if (father != null)
        {
            father.SetBorderThickness((Thickness)e.NewValue);
        }
    }
}    

父亲
public class Father : Control
{
    private Son childControl; 

    public void SetBorderThickness(Thickness value)
    {
        if (childControl != null)
        {
            Son.SetCellBorderThickness(childControl, value);
        }
    }
}

Output

enter image description here

项目可在此link .

关于wpf - 如何使用子定义的附加属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20987450/

相关文章:

wpf - 如何通过包含它的对象的接口(interface)公开私有(private)内部对象的依赖属性?

wpf - 在类型集合的 AttachedProperty 内绑定(bind)到其他元素

storyboard - 无法通过代码在 Metro 风格应用程序中将 Storyboard.TargetProperty 设置为 CompositeTransform.Rotation

wpf - 在 Xaml 中将 TreeView 与 ContextMenu 绑定(bind)

.net - wpf 绑定(bind)到索引器

c# - 将 ObservableCollection 保存到文件 (.txt)

c# - 为什么动画高度设置后这个属性直接停止工作

wpf - 你能清除触发器中的依赖属性吗?

wpf - 为什么 WPF 将附加属性用于诸如在网格中定位之类的事情?

wpf - 获取滚动查看器中 child 可用的宽度