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)
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;
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!
I am so eager to know how MS developers do with the Child-Defined ones.
eg: what about ScrollViwer.VerticalScrollBarVisibility in WPFToolkit:DataGrid?
<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>
<Grid>
<Father Son.CellBorderThichness="5"></Father>
</Grid>
<Window>
Custom Dependency Properties Follow the steps given below to define custom dependency property in C#. Declare and register your dependency property with system call register. Provide the setter and getter for the property. Define an instance handler which will handle any changes that occur to that particular instance.
Attached properties are a type of dependency property. The difference is in how they're used. With an attached property, the property is defined on a class that isn't the same class for which it's being used. This is usually used for layout.
A DependencyProperty maintains a static reference of all the DependencyProperty you register in WPF object hierarchy. It maintains a HashTable named PropertyFromName which it uses internally to get the DependencyProperty object. So in other word, each dependencyProperty object is registered in a global HashTable.
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:
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
Project is available at this link.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With