依赖属性收集在单独的类中

时间:2011-08-24 23:50:07

标签: 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 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的设计时)用户控件DPTestGroup属性的成员(Group.MyProperty1)改变了它的值< / b>。我设法在运行时实现它,例如使用DPGroup类中定义的事件处理程序,但这在xaml中的设计时不起作用。

<Grid x:Name="LayoutRoot" Background="White">
    <local:DPTest>
        <local:DPTest.Group>
            <local:DPGroup MyProperty1="2"/>
        </local:DPTest.Group>
    </local:DPTest>
</Grid>

它有效,但只是第一次,在创建标签期间:

 <local:DPGroup MyProperty1="2"/>

之后,更改MyProperty1的值,不会触发DPTest.OnPropertyChange。可能会触发DBGroup.OnPropertyChanged,但这当然不会通知用户控件DPTest那么如何让DPTest知道Group.MyProperty1已经改变了?

我不希望从MyProperty1到用户控件DPTest内创建的相应属性进行任何绑定(不重复属性),重点是在一个单独的类中有一组属性,所以我可以不止一次使用这个组,比如:

// my UserControl
public class DPTest : UserControl
{
    public DPGroup Group1 { ... } 
    public DPGroup Group2 { ... } 
}

我看到UIElement.RenderTransform的一些类比(假设它是我的Group属性),例如ScaleTransform

<Grid x:Name="LayoutRoot" Background="White">
    <Grid.RenderTransform>
        <ScaleTransform ScaleX="0.4"/>
    </Grid.RenderTransform>      
</Grid>

ScaleXMyProperty1类似。 不同之处在于,ScaleX(在XAML中)的变化值将反映设计时间的即时变化,而这正是我想要实现的目标。

我试图找到整个google / stack溢出和其他人的解决方案,但没有找到。到处都是在用户控件中创建依赖项属性的示例。

感谢您的时间。 任何帮助非常感谢。

编辑:根据Harlow Burgess的回答,设法在Silverlight中成为一个有效的例子。我把整个解决方案作为一个单独的答案。

2 个答案:

答案 0 :(得分:2)

来自:http://msdn.microsoft.com/en-us/library/ms752914.aspx#setting_properties_data_binding

  

依赖项属性或DependencyObject类本身不具备   支持INotifyPropertyChanged 以生成通知   对数据绑定的DependencyObject源属性值的更改   操作。有关如何创建要使用的属性的更多信息   在数据绑定中,可以报告对数据绑定目标的更改,请参阅   数据绑定概述。

设计一个系统,当任何子属性(任何子属性,任何子属性,......)的任何属性发生变化时,通知整个对象图将是低效的。因此,当您需要在该属性更改时执行某些操作时,或者如果您确实希望在任何子属性更改时收到通知时,您应该使用数据绑定到特定属性,您应该实现 INotifyPropertyChanged

How to: Implement Property Change Notification

示例:

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;

}  

答案 1 :(得分:0)

好的,基于@Harlow Burgess的回答,我设法在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>

最后,我们可以将DPTest嵌入到任何控件的某些内容中,例如在另一个用户控件的Grid中:

<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>

就是这样,再次感谢Harlow Burgess的帮助!