5

1. Issue

As we know,Attached Property widely expands the property system in wpf.But all the examples which are familiar to us are almost Parent-Defined ones,such as DockPanel.Dock / Grid.Row and so on.But after checking the document in MSDN,I found there are some other usages of 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. Trial

Since Attached Property Can be defined by user,I thoought maybe we can use "CallBackMethod" to handle it.So I have coded some trials to validate my idea (Part 4.Code):

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. Problem

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!

Is there any example which has used the Child-Defined attached property?And how does it work?

I am so eager to know how MS developers do with the Child-Defined ones.

eg: what about ScrollViwer.VerticalScrollBarVisibility in WPFToolkit:DataGrid?


4. Code

Son Control (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);
        }
      }
 }

Father Control (Parent)

 <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>
Claw
  • 474
  • 5
  • 16
  • Possible duplicate: [Setting WPF nested control properties from the parent control](http://stackoverflow.com/questions/683666/setting-wpf-nested-control-properties-from-the-parent-control?rq=1) But you get +1 for the detailed question ;-) – jwillmer Jan 08 '14 at 10:17
  • @jwillmer Thansks for your answer!It helps a lot! – Claw Jan 09 '14 at 01:30
  • Did you find the solution for your question? If not, then I can take him. – Anatoliy Nikolaev Jan 11 '14 at 16:26
  • @AnatoliyNikolaev Not really yet.Would u plz give some advices?Thanks! – Claw Jan 13 '14 at 02:17

1 Answers1

5

I took your example as a basis, removed a bit too much, and got this example. I removed them to show a minimal example of work.

First, I removed CellBorderThickness property, as is already attached dependency property.

Son

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

In my father control I removed OnApplyTemplate(), and in function SetBorderThickness() use the opportunity of attached dependency properties to set value:

Father

// Removed
OnApplyTemplate() { ... }

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

Below is a complete example. The structure of example:

enter image description here

XAML

Styles

Son

<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>

Father

<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>

MainWindow

<Grid>
    <SonNamespace:Son />

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

Code

Son

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);
        }
    }
}    

Father

public class Father : Control
{
    private Son childControl; 

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

Output

enter image description here

Project is available at this link.

Anatoliy Nikolaev
  • 22,370
  • 15
  • 69
  • 68
  • So much thanks for the detailed answer project! I found that the "CallBackMethod" in Son and the "SetBorderThickness()" in Father may not be necessary.Since we have set the attached property value to Father and the child can get it from the {TemplateBinding}.Is that right? – Claw Jan 13 '14 at 07:43
  • @Claw: Yes, in `Father`: `SonNamespace:Son.CellBorderThickness="6"`, in `Style` we do this through construction `{TemplateBinding SonNamespace:Son.CellBorderThickness}`. Please ask, I might miss something that, in its reply, if the answer in comment is obvious to you, I'll update the answer. – Anatoliy Nikolaev Jan 13 '14 at 08:11