The default value of the attribute can ensure the validity of the attribute.
Attribute verification validity can be used to verify the input attributes and attribute mandatory callbacks, that is, notifications must be made regardless of whether the attributes have changed or not.
Attribute change notification, when an attribute changes, the program can be notified to perform a series of processes.
There is nothing wrong with WPF here, let’s see how dependency properties solve the above problems.
Content summary defines the first and simplest dependency attribute dependency attribute value basic operation attribute wrapper attribute metadata (PropertyMetadata)
Attribute metadata basic behavior
Although the original words of MSDN are blunt, there is no doubt about their accuracy. Once you understand it, you will have a different experience when you look at it.
1. Define the first and simplest dependency attribute
MSDN original words: Windows Presentation Foundation (WPF) provides a set of services that can be used to extend the functionality of common language runtime (CLR) properties. These services are often collectively referred to as the WPF property system. Properties supported by the WPF property system are called dependency properties.
Let's define an Age dependency attribute, as follows:
public class DPCustomPeople
{
public static readonly DependencyProperty AgeProperty =
DependencyProperty.Register("Age", typeof(int), typeof(DPCustomPeople));
public void DisplayAgeProperty()
{
Console.WriteLine("DPName:" + DPCustomPeople.AgeProperty.Name);
Console.WriteLine("DPPropertyType:" + DPCustomPeople.AgeProperty.PropertyType);
Console.WriteLine("DPOWnerType:" + DPCustomPeople.AgeProperty.OwnerType);
}
}
Then call the output result
class Program
{
static void Main(string[] args)
{
DPCustomPeople people = new DPCustomPeople();
people.DisplayAgeProperty();
}
}
You may be unfamiliar with the DependencyProperty class. The DependencyProperty class provides some basic characteristics of dependency properties.
The way to register a dependency property is to call the static Register method of DependencyProperty, which provides multiple overloaded methods, but the following three steps are necessary. Once the registration is completed, it is a static property
Provide the registered name (Name) "Age"
Register property type (PropertyType) typeof (int)
Register the owner type of the dependency attribute (OwnerType) typeof (DPCustomPeople)
Note: The attribute name, attribute type, and attribute owner type cannot be changed once registered.
The following is the output result
2. Basic operations of dependent attribute values (value acquisition and assignment)
After defining the Age dependency property, we should be able to perform value acquisition and assignment operations on the property. DependencyProperty itself does not provide these operations, but is handled by DependencyObject.
DependencyObject represents an object that participates in the dependency property system.
Therefore, it is required that the defined class inherits from DependencyObject, then rewrite DPCustomPeople
public class DPCustomPeople:System.Windows.DependencyObject
{
}
Basic value assignment operations GetValue and SetValue methods
public void DPPropertyBasicOperator()
{
Console.WriteLine("Age:" + this.GetValue(DPCustomPeople.AgeProperty));
this.SetValue(DPCustomPeople.AgeProperty, 24);
Console.WriteLine("ChangedAge:" + this.GetValue(DPCustomPeople.AgeProperty));
}
Output results
3. The attribute wrapper uses the GetValue and SetValue methods to operate on values, so we can wrap it and define the Age attribute.
public int Age
{
get { return (int)GetValue(AgeProperty); }
set { SetValue(AgeProperty, value); }
}
Note: The naming convention for dependent property packaging is to remove the following Property
public void DPPropertyBasicOperatorUsingProperty()
{
Console.WriteLine("Age:" + this.Age);
this.Age=24;
Console.WriteLine("ChangedAge:" + this.Age);
}
Does the above code look more concise?
4. Property Metadata (PropertyMetadata)
MSDN original: The Windows Presentation Foundation (WPF) property system includes a metadata reporting system that is not limited to what can be reported about a property through reflection or regular common language runtime (CLR) features.
Speaking of attribute metadata, the first thing that comes to mind is .net’s Attribute
public class Person
{
[DefaultValue(200),Category("Layout")]
public int Width { get; set; }
}
Attribute needs to use the power of Visual Studio to make the IDE friendly support for Attribute, or rely on reflection to assign values.
But without these technologies, new creates a new instance through normal channels, and adding Attributes has no effect. We cannot rely on these Attributes to ensure some basic characteristics of the attributes (such as default values). We rely on the attribute metadata of the attributes. Different from the metadata described above.
Metadata for dependency properties
Can be uniquely specified by the class in which the dependency property is defined Can be changed when the dependency property is added to another class Can be explicitly overridden by all derived classes that inherit the dependency property from the defining base class The above language is blunt, but But it explains the intention. But we can't always understand the designer's thoughts at the first time. Let's know the existence of this concept first.
5. Basic behavior of attribute metadata The basic behavior of attribute metadata provides three functions for dependent attributes. This is also the issue just raised in this article.
Default property property notification property mandatory callback Let's first take a look at the constructor of a complete PropertyMetadata. If the default PropertyMetadata is not set for the dependent property, a PropertyMetadata object will be automatically created internally for the dependent property.
public PropertyMetadata(object defaultValue, PropertyChangedCallback propertyChangedCallback, CoerceValueCallback coerceValueCallback)
Dependent attributes borrow the concept of attribute metadata to complete attribute default values, attribute notifications, forced callbacks and other behaviors
1.Attribute default value
public static readonly DependencyProperty NameProperty =
DependencyProperty.Register("Name", typeof(string), typeof(DPCustomPeople),
new PropertyMetadata(string.Empty));
Most people will have a question when they see this. Why use PropertyMetadata to set a default value? Why not register it directly in the Register method? The following code
public static readonly DependencyProperty NameProperty =
DependencyProperty.Register("Name", typeof(string), typeof(DPCustomPeople),
string.Empty);
Of course, this question has been with me for a long time, and there is nothing I can do if I can’t solve it, so I’ll leave it alone for now.
Note: When assigning a default value to a property in PropertyMetadata, the type correctness cannot be detected.
Such a definition, because the default value of the dp code segment in vs is 0, this is something worth noting
public static readonly DependencyProperty NameProperty =
DependencyProperty.Register("Name", typeof(string), typeof(DPCustomPeople),
new UIPropertyMetadata(0));
2. Attribute default value restoration operation
After the property is assigned a value, you can restore the default value through the ClearValue method of DependencyObject, as shown in the following code
public string Name
{
get { return (string)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
public static readonly DependencyProperty NameProperty =
DependencyProperty.Register("Name", typeof(string), typeof(DPCustomPeople),
new UIPropertyMetadata(string.Empty));
public void DPPropertyClearOperator()
{
Console.WriteLine("Name:" + this.Name);
this.Name="Terry";
Console.WriteLine("ChangedName:" + this.Name);
this.ClearValue(NameProperty);
Console.WriteLine("Name:" + this.Name);
}
Output results
Note: Distinguish between default assignment and default value
Default assignment is usually done in the constructor, but this is not the default value (this was true before the advent of dependency properties), especially when derived classes override properties.
public class Student : DPCustomPeople
{
publicStudent()
{
this.Name = "Sky";
}
public void TestSubDefaultDpValue()
{
Console.WriteLine("Clear Before:"+this.Name);
this.ClearValue(Student.NameProperty);
Console.WriteLine("Clear After:" + this.Name);
}
}
Output results
3.Notification of property changes
This function is the most commonly used. When the property value changes, the PropertyChangedCallback callback is triggered.
public bool IsBoy
{
get { return (bool)GetValue(IsBoyProperty); }
set { SetValue(IsBoyProperty, value); }
}
public static readonly DependencyProperty IsBoyProperty =
DependencyProperty.Register("IsBoy", typeof(bool), typeof(Student),
new UIPropertyMetadata(false,new PropertyChangedCallback(IsBoyPropertyChangedCallback)));
public static void IsBoyPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Student st = d as Student;
if (st.IsBoy)
{
Console.WriteLine("Hello,Boy");
}
else
{
Console.WriteLine("Hello,Girl");
}
}
public void TestPropertyChangedCallback()
{
this.IsBoy = false;
this.IsBoy = true; this.IsBoy = true;
}
You can view the old value and new value output results through DependencyPropertyChangedEventArgs
Note:
(1). Through the above output results, have you seen that the default value of dependent properties will not trigger property change notifications?
(2). Manually trigger attribute change notifications
If you want the default value to trigger a property change (in fact, sometimes it is really necessary), you don’t have to wait and trigger it manually.
private void RaiseIsBoyPropertyChangedCallback()
{
IsBoyPropertyChangedCallback(this,new DependencyPropertyChangedEventArgs
(Student.IsBoyProperty, Student.IsBoyProperty.DefaultMetadata.DefaultValue, null));
}
(3). When there is an attribute change notification, be sure to ensure the correctness of the attribute default value type.
We know that value types have default values, but reference types do not (that is, they can be assigned to null). Whether a type has a default type can be checked using the default keyword. As shown below
We rewrite the default value of the dependency property defined above to null. It can run well when there is no PropertyChangedCallback, but when there is a property change notification, disaster occurs, and the program will throw an exception, saying that the type does not match.
public static readonly DependencyProperty IsBoyProperty =
DependencyProperty.Register("IsBoy", typeof(bool), typeof(Student),
new UIPropertyMetadata(null,new PropertyChangedCallback(IsBoyPropertyChangedCallback)));
Let’s look at the reference type again. If the default value is null, everything will be fine.
public IList LovedGirl
{
get { return (IList)GetValue(LovedGirlProperty); }
set { SetValue(LovedGirlProperty, value); }
}
public static readonly DependencyProperty LovedGirlProperty =
DependencyProperty.Register("LovedGirl", typeof(IList), typeof(Student),
new UIPropertyMetadata(null, new PropertyChangedCallback(LovedGirlChangedCallback)));
public static void LovedGirlChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Student st = d as Student;
foreach (var item in e.NewValue as IList)
{
Console.WriteLine(item);
}
}
public void TestReferenceDpType()
{
List<string> list = new List<string>();
list.Add("girl 1");
list.Add("girl 2");
this.LovedGirl = list;
}
4. Forced attribute callback
First of all, the default value still does not trigger the callback method.
The forced callback method means that regardless of whether the attribute value changes or not, the callback method will be entered.
public int Score
{
get { return (int)GetValue(ScoreProperty); }
set { SetValue(ScoreProperty, value); }
}
public static readonly DependencyProperty ScoreProperty =
DependencyProperty.Register("Score", typeof(int), typeof(Student),
new UIPropertyMetadata(0,null,new CoerceValueCallback(ScoreCoerceValueCallback)));
public static object ScoreCoerceValueCallback(DependencyObject d, object baseValue)
{
Console.WriteLine(baseValue);
return baseValue;
}
public void TestCoerceValueCallback()
{
this.Score = 0;
this.Score = 0;
this.Score = 0;
}