C# 在单独的类中收集的依赖项属性
我的问题与Silverlight有关(但我想WPF也是如此) 基本上,我知道如何在用户控件中创建依赖项属性以及如何使其工作。但我试图做的,但没有成功的是:在一个类中创建依赖属性(或多个),这个类将成为我的用户控件的依赖属性 换言之:C# 在单独的类中收集的依赖项属性,c#,wpf,silverlight,xaml,dependency-properties,C#,Wpf,Silverlight,Xaml,Dependency Properties,我的问题与Silverlight有关(但我想WPF也是如此) 基本上,我知道如何在用户控件中创建依赖项属性以及如何使其工作。但我试图做的,但没有成功的是:在一个类中创建依赖属性(或多个),这个类将成为我的用户控件的依赖属性 换言之: // my UserControl public class DPTest : UserControl { // dependency property, which type is a class, and this class will be holdi
// my UserControl
public class DPTest : UserControl
{
// dependency property, which type is a class, and this class will be holding other dependency properties
public static readonly DependencyProperty GroupProperty =
DependencyProperty.Register("Group", typeof(DPGroup), typeof(DPTest), new PropertyMetadata(new DPGroup(), OnPropertyChanged));
public DPGroup Group
{
get { return (DPGroup)GetValue(GroupProperty); }
set { SetValue(GroupProperty, value); }
}
// this occurs only when property Group will change, but not when a member of property Group will change
static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DPTest g = d as DPTest;
// etc.
}
}
// a class, where I want to hold my dependency properties
public class DPGroup : DependencyObject
{
public static readonly DependencyProperty MyProperty1Property =
DependencyProperty.RegisterAttached("MyProperty1", typeof(int), typeof(DPGroup), new PropertyMetadata(1, OnPropertyChanged));
public int MyProperty1
{
get { return (int)GetValue(MyProperty1Property); }
set { SetValue(MyProperty1Property, value); }
}
// I would like to notify "the parent" (which means user control "DPTest" ), that member MyProperty1 has changed
static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DPTest g = d as DPTest;
if (g != null) g.textBox1.Text = g.Group.MyProperty1.ToString();
}
}
我想要实现的是通知(在XAML的设计时)一个用户控件DPTest
,组属性(Group.MyProperty1
)的成员更改了它的值。我设法在运行时实现了这一点,例如通过使用DPGroup
类中定义的事件处理程序,但这在xaml的设计时不起作用
<Grid x:Name="LayoutRoot" Background="White">
<local:DPTest>
<local:DPTest.Group>
<local:DPGroup MyProperty1="2"/>
</local:DPTest.Group>
</local:DPTest>
</Grid>
我看到一些类似于UIElement.RenderTransform
(假设它是我的组
属性)的东西,例如ScaleTransform
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RenderTransform>
<ScaleTransform ScaleX="0.4"/>
</Grid.RenderTransform>
</Grid>
ScaleX
类似于MyProperty1
。不同之处在于,ScaleX
(以XAML为单位)的值的变化将反映设计时的即时变化,而这正是我试图实现的
我试图在整个google/stack overflow和其他方面找到解决方案,但没有找到。到处都是在用户控件中创建依赖项属性的示例
谢谢你抽出时间。
非常感谢您的帮助
编辑:根据哈洛·伯吉斯的回答,a成功地在Silverlight中制作了一个工作示例。我将整个解决方案作为一个单独的答案放在下面。来源:
依赖项属性或DependencyObject类不是本机的
支持INotifyPropertyChanged以生成通知
数据绑定的DependencyObject源属性值的更改数量
操作。有关如何创建要使用的属性的详细信息,请参见
在可以报告数据绑定目标更改的数据绑定中,请参见
数据绑定概述
如果设计一个系统,在任何子属性(任何子属性、任何子属性等)的任何属性发生更改时通知整个对象图,则效率低下。因此,当需要在属性更改时执行某些操作时,或者如果确实希望在任何子属性更改时收到通知,则应该使用数据绑定到特定属性,您应该实现INotifyPropertyChanged
例如:
public class DPGroup : DependencyObject, INotifyPropertyChanged
{
public static readonly DependencyProperty MyProperty1Property =
DependencyProperty.RegisterAttached(
"MyProperty1",
typeof(int),
typeof(DPGroup),
new PropertyMetadata(1));
public int MyProperty1
{
get { return (int)GetValue(MyProperty1Property); }
set { SetValue(MyProperty1Property, value); }
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
NotifyPropertyChanged(e.Property.Name);
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class DPTest : UserControl
{
public static readonly DependencyProperty GroupProperty =
DependencyProperty.Register(
"Group",
typeof(DPGroup),
typeof(DPTest),
new PropertyMetadata(
new DPGroup(),
new PropertyChangedCallback(OnGroupPropertyChanged)
)
);
public DPGroup Group
{
get { return (DPGroup)GetValue(GroupProperty); }
set { SetValue(GroupProperty, value);}
}
static void OnGroupPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DPTest control = (DPTest)d;
DPGroup oldGroup = e.OldValue as DPGroup;
if (oldGroup != null)
{
oldGroup.PropertyChanged -=new PropertyChangedEventHandler(control.group_PropertyChanged);
}
DPGroup newGroup = e.NewValue as DPGroup;
if (newGroup != null)
{
newGroup.PropertyChanged +=new PropertyChangedEventHandler(control.group_PropertyChanged);
}
control.UpdateTextBox();
}
private void group_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
this.UpdateTextBox();
}
private void UpdateTextBox()
{
this.textBox1.Text = this.Group.MyProperty1.ToString();
}
private TextBox textBox1;
}
好的,基于@Harlow Burgess answer,a成功地在Silverlight中制作了一个工作示例
基本上,区别在于,在SL中,DependencyObject
类没有OnPropertyChanged
方法,因此在DPGroup
类中,我们不能覆盖它,但我们可以通过以下方式附加此方法:
new PropertyMetadata(1, OnPropertyChanged).
因此,DPGroup
类将如下所示:
public class DPGroup : DependencyObject, INotifyPropertyChanged
{
public static readonly DependencyProperty MyProperty1Property =
DependencyProperty.RegisterAttached(
"MyProperty1",
typeof(int),
typeof(DPGroup),
new PropertyMetadata(1, OnPropertyChanged));
public int MyProperty1
{
get { return (int)GetValue(MyProperty1Property); }
set { SetValue(MyProperty1Property, value); }
}
// static method invoked when MyProperty1 has changed value
static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DPGroup g = d as DPGroup;
if (g != null)
{
g.MyProperty1 = (int)e.NewValue;
// invoking event handler, to notify parent class about changed value of DP
if (g.PropertyChanged != null) g.PropertyChanged(g, null);
}
}
// event handler, for use in parent class
public event PropertyChangedEventHandler PropertyChanged;
}
父类,包含类型为DPGroup
的依赖属性:
public partial class DPTest : UserControl
{
public static readonly DependencyProperty GroupProperty =
DependencyProperty.Register(
"Group",
typeof(DPGroup),
typeof(DPTest),
new PropertyMetadata(
new DPGroup(),
new PropertyChangedCallback(OnGroupPropertyChanged)
)
);
public DPGroup Group
{
get { return (DPGroup)GetValue(GroupProperty); }
set { SetValue(GroupProperty, value); }
}
// static method invoked when Group property has changed value
// here we need to attach event handler defined if DPGroup, so it will fire from inside Group property,
// when Group.MyProperty1 will change value
static void OnGroupPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DPTest control = (DPTest)d;
DPGroup oldGroup = e.OldValue as DPGroup;
// removing event handler from prevoius instance of DBGroup
if (oldGroup != null)
oldGroup.PropertyChanged -= new PropertyChangedEventHandler(control.group_PropertyChanged);
DPGroup newGroup = e.NewValue as DPGroup;
// adding event handler to new instance of DBGroup
if (newGroup != null)
newGroup.PropertyChanged += new PropertyChangedEventHandler(control.group_PropertyChanged);
DPTest g = d as DPTest;
if (g != null)
control.UpdateTextBox();
}
private void group_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
UpdateTextBox();
}
// here you can do anything with changed value Group.MyProperty1
private void UpdateTextBox()
{
this.textBox1.Text = this.Group.MyProperty1.ToString();
}
public DPTest()
{
InitializeComponent();
}
}
现在,DPTest
的XAML部分:
<UserControl x:Class="Silverlight_Workbench_2.DPTest"
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:local="clr-namespace:Silverlight_Workbench_2"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400" >
<Grid x:Name="LayoutRoot" Background="White">
<TextBox Height="23" HorizontalAlignment="Left" Margin="76,61,0,0"
x:Name="textBox1" VerticalAlignment="Top" Width="120" />
</Grid>
</UserControl>
就这些,再次感谢哈洛·伯吉斯的帮助 感谢您的回答,但是在哪里实现INotifyPropertyChanged
?在用户控件DPTest
或包含属性的类(DPGroup
)上。我想,我尝试了第二种选择,但没有成功。谢谢你指出了一个效率低下的问题。我会给+1,但我还不能投票:)在DependencyObject上实现InotifyProperty更改。从UserControl订阅该通知。参见上面的示例代码。你不必投票,只要把它标记为答案,如果它对你有帮助的话。谢谢!尽管这段代码只在WPF中工作(做了一些修改),但我理解了一般的概念,并且能够在Silverlight中重现这段代码。为了能够在WPF中运行它,我需要a)从两个类中的DPs中删除只读
,b)在我的环境中,我在XAML中实例化时出现异常…
:无法将组
属性的默认值绑定到特定线程。然后,我让DBGroup继承自Freezable
(不是从DependencyObject继承的),就像这里写的那样:这就开始工作了。谢谢
public partial class DPTest : UserControl
{
public static readonly DependencyProperty GroupProperty =
DependencyProperty.Register(
"Group",
typeof(DPGroup),
typeof(DPTest),
new PropertyMetadata(
new DPGroup(),
new PropertyChangedCallback(OnGroupPropertyChanged)
)
);
public DPGroup Group
{
get { return (DPGroup)GetValue(GroupProperty); }
set { SetValue(GroupProperty, value); }
}
// static method invoked when Group property has changed value
// here we need to attach event handler defined if DPGroup, so it will fire from inside Group property,
// when Group.MyProperty1 will change value
static void OnGroupPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DPTest control = (DPTest)d;
DPGroup oldGroup = e.OldValue as DPGroup;
// removing event handler from prevoius instance of DBGroup
if (oldGroup != null)
oldGroup.PropertyChanged -= new PropertyChangedEventHandler(control.group_PropertyChanged);
DPGroup newGroup = e.NewValue as DPGroup;
// adding event handler to new instance of DBGroup
if (newGroup != null)
newGroup.PropertyChanged += new PropertyChangedEventHandler(control.group_PropertyChanged);
DPTest g = d as DPTest;
if (g != null)
control.UpdateTextBox();
}
private void group_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
UpdateTextBox();
}
// here you can do anything with changed value Group.MyProperty1
private void UpdateTextBox()
{
this.textBox1.Text = this.Group.MyProperty1.ToString();
}
public DPTest()
{
InitializeComponent();
}
}
<UserControl x:Class="Silverlight_Workbench_2.DPTest"
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:local="clr-namespace:Silverlight_Workbench_2"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400" >
<Grid x:Name="LayoutRoot" Background="White">
<TextBox Height="23" HorizontalAlignment="Left" Margin="76,61,0,0"
x:Name="textBox1" VerticalAlignment="Top" Width="120" />
</Grid>
</UserControl>
<UserControl x:Class="Silverlight_Workbench_2.DPTestMain"
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:local="clr-namespace:Silverlight_Workbench_2"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
<Grid x:Name="LayoutRoot" Background="White">
<local:DPTest>
<local:DPTest.Group>
<!--here we can change value, and it will be reflected in design window
as a text in textBox1-->
<local:DPGroup MyProperty1="8"/>
</local:DPTest.Group>
</local:DPTest>
</Grid>
</UserControl>