Dependency Properties & Attached properties
Dependency Properties looks similar to normal CLR properties
but its more complex and more powerful feature of XAML.
The main difference is, that the normal CLR properties are
backed a private variable of your class, whereas the Dependency Properties are
backed by DependencyObject’s GetValue and SetValue which resolved value dynamically
by calling these two methods.
Getter and setter value mechanism
XML processor resolves the value by the precedence from high
to low.
|
GetValue()
|
SetValue()
|
1
|
Animation
|
|
2
|
Binding Expression
|
|
3
|
Local Value
|
Local Value
|
4
|
Custom Style trigger
|
|
5
|
Custom template trigger
|
|
6
|
Custom Style Setter
|
|
7
|
Default Style Trigger
|
|
8
|
Default Style Setter
|
|
9
|
Inherited Value
|
|
10
|
Default Value
|
|
The Advantage of dependency properties
·
Reduce memory footprint: most of the UI element have a no value just it
use the default value.
·
Value inheritance: if there is no value defined
then inherit from parents.
·
Change notification: built-in mechanism which binding
get notification by registering of property metadata, when the value of the
property has been changed.
Example of a Dependency property
To create a Dependency property, add a static type of DependencyProperty and call Register static method of DependencyProperty class to create
instance of a dependency property. To create a property of your class, create
normal CLR property and replace with GetValue() and SetValue() instead of private variable like a wrapper property.
Note : Do not write any logic in getter and setter of the dependency
property, because getter might not call some time as explain in above table and
setter might not call by XML but it may call SetValue() directly instead of
your setter.
public static readonly DependencyProperty TempratureProperty =
DependencyProperty.Register( " Temprature", typeof(int),
typeof(MyClockControl), new
FrameworkPropertyMetadata(0));
// .NET Property wrapper
public int Temprature
{
get { return (int)GetValue(TempratureProperty); }
set { SetValue(TempratureProperty, value); }
}
Each dependency property provides Callbacks for value change notification, value
coercion and value validation. These callbacks can be registered with FrameworkPropertyMetadata object of the dependency property.
public static readonly DependencyProperty TempratureProperty
=
DependencyProperty.Register( " Temprature", typeof(int),
new FrameworkPropertyMetadata(0,
OnTempraturePropertyChanged,
OnCoerceTempratureProperty),
OnValidateTempratureProperty);
// .NET Property wrapper
[Bindable(true)]
public int Temprature
{
get { return (int)GetValue(TempratureProperty); }
set { SetValue(TempratureProperty, value); }
}
Value Change Callback
private static void OnTempraturePropertyChanged(DependencyObject source,
DependencyPropertyChangedEventArgs e)
{
var control = source as MyTempratureControl;
var temprature = (int)e.NewValue;
// write your logic here...
}
Coerce Value Callback
private static object OnCoerceTempratureProperty( DependencyObject sender, object data )
{
if ((int)data > MaxTemprature )
{
data = MaxTemprature;
} else if ((int)data < MinTemprature )
{
data = MinTemprature;
}
return data;
}
Validation Callback
private static bool OnValidateTempratureProperty(object data)
{
// write your logic here...
return (int)data>0 && (int)data <100;
}
Readonly DependencyProprites
Some dependency properties of UI controls are read only. They
are often used to report state of a control, like IsMouseOver property. We can
use normal CLR property but Instead on
normal CLR property, we might need trigger to get notification.
private static readonly DependencyPropertyKey IsHotPropertyKey =
DependencyProperty.RegisterReadOnly("IsHot",
typeof(bool), typeof(MyClass),
new FrameworkPropertyMetadata(false));
// Register the public property to get the value
public static readonly DependencyProperty IsHotProperty =
IsHotPropertyKey.DependencyProperty;
// .NET Property wrapper
public int IsHot
{
get { return (bool)GetValue(IsHotProperty); }
private set { SetValue(IsHotPropertyKey, value); }
}
Attached Properties
Attached properties are a special kind of properties. They allow
you to attach a value to an object that object doesn’t know anything about this
value.
<Grid>
<!—Row s &columns definitions -->
<Button Grid.Row="0" Grid.Column="0" Content="Click me!"/>
</Grid>
See ClickCommnad in the following example which on click execute a command
from viewModel
<ListView x:Name="demoListView" >
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Extensions:ListViewItemBehavior.ClickCommand" Value="{Binding ElementName=demoListView, Path=DataContext.ClickCommand}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<Border>
<GridViewRowPresenter Columns="{TemplateBinding GridView.ColumnCollection}" Content="{TemplateBinding Content}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
public static class ListViewItemBehavior
{
public static readonly DependencyProperty ClickCommandProperty =
DependencyProperty.RegisterAttached(
"ClickCommand",
typeof(ICommand),
typeof(ListViewItemBehavior),
new FrameworkPropertyMetadata(null, ClickCommandChanged));
public static ICommand GetClickCommand(ListViewItem listViewItem)
{
return (ICommand)listViewItem.GetValue(ClickCommandProperty);
}
public static void SetClickCommand(
ListViewItem listViewItem, ICommand value)
{
listViewItem.SetValue(ClickCommandProperty, value);
}
static void ClickCommandChanged(
DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
var item = depObj as ListViewItem;
if (item == null)
return;
if (e.NewValue is ICommand)
{
item.PreviewMouseDown += OnListViewItemMouseDown;
}
else
{
item.PreviewMouseDown -= OnListViewItemMouseDown;
}
}
static void OnListViewItemMouseDown(object sender, RoutedEventArgs e)
{
var item = sender as ListViewItem;
if (item == null)
return;
var command = GetClickCommand(item);
if (command.CanExecute(item.Content))
command.Execute(item.Content);
}
}
Clear Local Value
Assign null to dependency property is not really clearing the Locale
Value of dependency property because null is also valid local value, there is
static object DependencyProperty.UnsetValue that describe as an unset value. Dependency property’s
local value can be clear by invoking listView.ClearValue(ListView.ItemsSourceProperty) method.