【问题标题】:dependency properties gathered in separate class依赖属性收集在单独的类中
【发布时间】:2011-08-24 23:50:06
【问题描述】:

我的问题涉及 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) 的成员更改了它的值。我设法让它在运行时发生,例如通过使用在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>

ScaleX 类似于MyProperty1不同之处在于,ScaleX(在 XAML 中)的更改值将反映设计时的即时更改,而这正是我想要实现的。

我试图在整个 google/stack 溢出等中找到解决方案,但没有找到。 Everywhere 只是在用户控件中创建依赖属性的示例。

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

编辑:根据 Harlow Burgess 的回答,a 设法在 Silverlight 中制作了一个工作示例。我将整个解决方案作为单独的答案放在下面。

【问题讨论】:

    标签: c# wpf silverlight xaml dependency-properties


    【解决方案1】:

    发件人:http://msdn.microsoft.com/en-us/library/ms752914.aspx#setting_properties_data_binding

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

    设计一个系统在任何子属性(任何子属性,任何子属性,...)的任何属性发生变化时通知整个对象图是低效的。因此,当您需要在该属性更改时执行某些操作时,或者如果您真的想在 any 子属性更改时收到通知,则应该使用数据绑定到特定属性,您应该实现 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;
    
    }  
    

    【讨论】:

    • 感谢您的回答,但在哪里实施INotifyPropertyChanged?在用户控件 DPTest 或包含属性的类 (DPGroup) 上。我想,我尝试了第二种选择,但没有运气。感谢您指出效率低下的问题。我会给 +1,但我还不能投票 :)
    • 在 DependencyObject 上实现 INotifyPropertyChanged。从 UserControl 订阅该通知。请参阅上面的示例代码。您不必投票,如果对您有帮助,只需将其标记为答案即可。
    • 谢谢!尽管此代码仅在 WPF 中有效(有一些更改),但我理解了一般概念,并且能够在 Silverlight 中重现它。为了能够在 WPF 中运行它,我需要 a) 从两个类中的 DP 中删除 readonly,并且 b) 在我的环境中,我在 XAML 中实例化时出现异常 &lt;local:DPTest&gt;...&lt;/local:DPTest&gt;@987654330 的默认值@ 属性不能绑定到特定线程。然后我让 DBGroup 继承自 Freezable(不是从 DependencyObject),如下所示:link,这开始起作用了。谢谢!
    【解决方案2】:

    好的,根据@Harlow Burgess 的回答,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>
    

    最后,我们可以将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 的帮助!

    【讨论】:

      猜你喜欢
      • 2013-02-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-23
      相关资源
      最近更新 更多