如何向 Silverlight 控件添加用户自定义属性






4.79/5 (9投票s)
为任何 Silverlight 控件添加用户自定义属性都很简单。
引言
您是否曾经需要将某些信息传递给一个UserControl
(甚至是一个原生控件),却发现自己不得不对该控件的Tag
属性进行CSV字符串操作?
我曾遇到过这种情况,但最终发现,为任何控件创建附加属性其实非常简单。
为了演示,我将使用一个Button
,但您可以在任何您喜欢的控件上使用此方法。
让我们来看看如何操作。
我将使用 Silverlight 4、VS 2010 和 Blend 4。
演示页面
为了演示其工作原理,首先创建一个名为“PropertiesDemo
”的 Silverlight 项目,并在MainPage
中添加 3 个Buttons
和一个TextBox
。
<UserControl x:Class="PropertiesDemo.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400" >
<Grid x:Name="LayoutRoot" Background="White">
<Button Content="Powered Up Button" Height="23"
HorizontalAlignment="Left" Margin="10,10,0,0"
x:Name="Button1" VerticalAlignment="Top" Width="126" />
<Button Content="Value 1" Height="23"
HorizontalAlignment="Left" Margin="10,43,0,0"
Name="Button2" VerticalAlignment="Top" Width="61" />
<Button Content="Value 2" Height="23"
HorizontalAlignment="Left" Margin="77,43,0,0"
Name="Button3" VerticalAlignment="Top" Width="59" />
<TextBox Height="23" HorizontalAlignment="Left"
Margin="157,10,0,0" Name="TextBox1"
VerticalAlignment="Top" Width="120" />
</Grid>
</UserControl>
Using the Code
首先,为了创建这个附加属性,我们需要创建一个新Class
,它继承自Button
类,并将其作为基类。
向客户端项目添加一个名为“ButtonEx
”的新Class
文件,并将代码替换为以下行。
using System.Windows;
using System.Windows.Controls;
namespace PropertiesDemo
{
public class ButtonEx : Button
{
public ButtonEx() : base() { }
}
}
由于Button1
将是功能增强的按钮,我们需要在MainPage XAML
的UserControl
标签中引用我们刚刚创建的新类。
xmlns:my="clr-namespace:PropertiesDemo"
并将Button1
的类型标签“<Button
”替换为“<my:ButtonEx
”。
<my:ButtonEx Content="Powered Up Button" Height="23"
HorizontalAlignment="Left" Margin="10,10,0,0" x:Name="Button1"
VerticalAlignment="Top" Width="126" />
现在,我们可以在ButtonEx
类中创建这个附加属性,它将成为
using System.Windows;
using System.Windows.Controls;
namespace PropertiesDemo
{
public class ButtonEx : Button
{
private static DependencyProperty ExtraPropProperty = DependencyProperty.Register
("ExtraProp", typeof(string), typeof(ButtonEx),
new PropertyMetadata("No extra prop set"));
public string ExtraProp
{
get { return (string)GetValue(ExtraPropProperty); }
set { SetValue(ExtraPropProperty, value); }
}
public ButtonEx() : base() { }
}
}
如果此时我们生成项目,该属性将与其他按钮属性一样可用。
现在是关于Click
事件的部分。Button1 Click
事件将弹出一个MessageBox
,显示ExtraProp
属性的值。Button2
和Button3
将只更改Button1
上新属性的值。
<my:ButtonEx Content="Powered Up Button" Height="23"
HorizontalAlignment="Left" Margin="10,10,0,0" x:Name="Button1"
VerticalAlignment="Top" Width="126" Click="Button1_Click"/>
<Button Content="Value 1" Height="23" HorizontalAlignment="Left"
Margin="10,43,0,0" Name="Button2" VerticalAlignment="Top"
Width="61" Click="Button2_Click" />
<Button Content="Value 2" Height="23" HorizontalAlignment="Left"
Margin="77,43,0,0" Name="Button3" VerticalAlignment="Top"
Width="59" Click="Button3_Click" />
private void Button1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(Button1.ExtraProp.ToString());
}
private void Button2_Click(object sender, RoutedEventArgs e)
{
Button1.ExtraProp = "Hallo from first button";
}
private void Button3_Click(object sender, RoutedEventArgs e)
{
Button1.ExtraProp = "Bye from second button";
}
按 F5 试试看!太棒了!
现在,是关于绑定部分。
正如我之前所说的,这个新属性与其他任何属性一样可用,并且会出现在VS和Blend的Property窗口中。
将MainPage XAML
中的TextBox1
行替换为
<TextBox Height="23" HorizontalAlignment="Left"
Margin="157,10,0,0" Name="TextBox1" VerticalAlignment="Top"
Width="120" Text="{Binding Path=ExtraProp, Mode=TwoWay, ElementName=Button1}" />
按 F5 并享受。
TwoWay
绑定功能齐全,您可以通过更改TextBox
内容来更改属性值。
当然,您可以根据需要添加任意数量的DependencyProperties
,并且可以使用它们在任何其他类型的控件中。
希望这对您有所帮助。
DependencyProperty - 关键点
我在此示例中使用的DependencyProperty
,虽然简单,但有些地方必须按此方式处理。
基本上,我们将使用DependencyProperty
类中的Register
方法在类上注册一个新属性。
对于该方法,我们有三种选择。我们将使用第二个选项,“Register(String, Type, Type, PropertyMetadata)
”。
private static DependencyProperty ExtraPropProperty = DependencyProperty.Register
("ExtraProp", typeof(string), typeof(ButtonEx),
new PropertyMetadata("No extra prop set"));
命名约定规定,DependencyProperty
的名称必须是已注册属性的名称加上后缀Property
。
因此,如果我们的新属性是ExtraProp
(Register
方法中的String
参数),那么DependencyProperty
的名称将是ExtraPropProperty
。
第一个Type
是属性类型(在我们的例子中是string
),第二个是属性所有者(要创建此 DP 的类,在我们的例子中是我们基类的名称ButtonEx
)。
PropertyMetadata
用于在基类初始化时设置新属性的默认值。
属性包装器
public string ExtraProp
{
get { return (string)GetValue(ExtraPropProperty); }
set { SetValue(ExtraPropProperty, value); }
}
它没什么特别的,只是它与 DP 的注册名称相同,并且get;set;
引用了创建的 DP。
更改通知
DependencyProperty
的一个优点是它内置了INotifyPropertyChange
,因此XAML
上的绑定效果很好,并且在 DP 值发生任何更改时都会得到通知和更新。
您甚至可以添加其他代码在值更改时执行。
一种方法
public string ExtraProp
{
get { return (string)GetValue(ExtraPropProperty); }
set
{
SetValue(ExtraPropProperty, value);
//Additional code goes here
}
}
关于更改通知的两个警告
更改通知将在且仅在(这一点很重要)DependencyProperty
的值更改时引发。
使用原生类型(string
、int
、bool
等)时效果很好。
但是,在使用复杂类型或用户定义类型时,您必须意识到只有类成员的更改才会引发通知更改。这意味着 DP 的值不会改变,只有它的某个成员改变了,因此 DP 不会引发任何更改通知事件。
如果您想让 DP 引发通知事件,您必须从您的用户定义类中将其冒泡。
请参阅本文中的Items_Class
类作为示例,其中我使用了该技术来冒泡ObservableCollection
中某个项的通知更改事件。
另一个您会遇到麻烦的地方是,当您在一个UserControl
中使用 DP,并且该UserControl
将被用作绑定集合的ItemTemplate
时。
您会发现,在属性包装器的set;
中SetValue()
之后添加的附加代码不会运行。我不知道原因,但它就是不运行。
这时我使用Register()
方法的变体,并使用PropertyMetadata
类的第二种形式,“PropertyMetadata(Object, PropertyChangedCallback)
”,并定义一个在属性值更改时执行的回调函数。
private static DependencyProperty ExtraPropProperty = DependencyProperty.Register("ExtraProp", typeof(string), typeof(ButtonEx), new PropertyMetadata("No extra prop set", new PropertyChangedCallback(ExtraPropChanged)));
private static void ExtraPropChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { (d as ButtonEx).ExtraPropChanged(e); }
private void ExtraPropChanged(DependencyPropertyChangedEventArgs e)
{
MessageBox.Show("Old value: " + (e.OldValue != null ?
e.OldValue.ToString() : "null") + "\n" + "New value: " +
(e.NewValue != null ? e.NewValue.ToString() : "null"));
}
更多关于 DependencyProperty
请查看这些链接以获得更深入的DependencyProperty
知识
- http://msdn.microsoft.com/en-us/library/ms753358(v=vs.110).aspx
- https://codeproject.org.cn/Articles/42203/How-to-Implement-a-DependencyProperty
- http://wpftutorial.net/DependencyProperties.html
完整代码
MainPage.xaml
<UserControl x:Class="PropertiesDemo.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:my="clr-namespace:PropertiesDemo"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400" >
<Grid x:Name="LayoutRoot" Background="White">
<my:ButtonEx Content="Powered Up Button" Height="23"
HorizontalAlignment="Left" Margin="10,10,0,0" x:Name="Button1"
VerticalAlignment="Top" Width="126" Click="Button1_Click"/>
<Button Content="Value 1" Height="23"
HorizontalAlignment="Left" Margin="10,43,0,0" Name="Button2"
VerticalAlignment="Top" Width="61" Click="Button2_Click" />
<Button Content="Value 2" Height="23"
HorizontalAlignment="Left" Margin="77,43,0,0" Name="Button3"
VerticalAlignment="Top" Width="59" Click="Button3_Click" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="157,10,0,0"
Name="TextBox1" VerticalAlignment="Top" Width="120"
Text="{Binding Path=ExtraProp, Mode=TwoWay, ElementName=Button1}" />
</Grid>
</UserControl>
MainPage.cs
using System.Windows.Controls;
using System.Windows;
namespace PropertiesDemo
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
}
private void Button1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(Button1.ExtraProp.ToString());
}
private void Button2_Click(object sender, RoutedEventArgs e)
{
Button1.ExtraProp = "Hallo from first button";
}
private void Button3_Click(object sender, RoutedEventArgs e)
{
Button1.ExtraProp = "Bye from second button";
}
}
}
ButtonEx.cs
using System.Windows;
using System.Windows.Controls;
namespace PropertiesDemo
{
public class ButtonEx : Button
{
private static DependencyProperty ExtraPropProperty = DependencyProperty.Register
("ExtraProp", typeof(string), typeof(ButtonEx),
new PropertyMetadata("No extra prop set", new PropertyChangedCallback(ExtraPropChanged)));
public string ExtraProp
{
get { return (string)GetValue(ExtraPropProperty); }
set { SetValue(ExtraPropProperty, value); }
}
private static void ExtraPropChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e) { (d as ButtonEx).ExtraPropChanged(e); }
private void ExtraPropChanged(DependencyPropertyChangedEventArgs e)
{
MessageBox.Show("Old value: " + (e.OldValue != null ?
e.OldValue.ToString() : "null") + "\n" +
"New value: " + (e.NewValue != null ? e.NewValue.ToString() : "null"));
}
public ButtonEx() : base() { }
}
}
结论
这是为控件添加附加属性的一种非常简单的方法,并且它带来了学习和使用DependencyProperties
的便捷方式。
我的建议:使用并滥用DependencyProperties
。它可以为您节省大量代码行。
请不要忘记投票和/或评论!
历史
- 2014 年 1 月 29 日 - 添加了一些关于
DependencyProperties
的解释