DependencyProperty VS无冗余状态管理

时间:2011-04-21 17:54:07

标签: .net wpf dependency-properties redundancy

让我们假设我们有一个只有一个状态变量的简单UI。该状态表示为枚举值,例如。 Phase1,Phase2等取决于UI的状态(阶段),不同的UI元素,窗口应该是可见的或隐藏的。

以下是代码:

public enum Phases { Phase1, Phase2, Phase3 }

public class UIStateModel : DependencyObject
{
    public static DependencyProperty CurrentStateProperty =
        DependencyProperty.Register("CurrentStateProperty",
                                    typeof(Phases),
                                    typeof(UIStateModel));
    public Phases CurrentState
    {
        get { return (Phases)GetValue(CurrentStateProperty); }
        set { SetValue(CurrentStateProperty, value); }
    }
    public Visibility Window1Visible // Databound to Window1.Visibility
    {
        get
        {
            if (this.CurrentState == Phases.Phase1) return Visibility.Visible;
            else return Visibility.Hidden;
        }
    }
    public Visibility Window2Visible // Databound to Window2.Visibility
    {
        get
        {
            if (this.CurrentState == Phases.Phase2) return Visibility.Visible;
            else return Visibility.Hidden;
        }
    } 
    ...
}

问题是与上面的代码绑定的数据不起作用,因为WindowXVisible属性不是DependencyProperty-s。我应该将所有属性都转换为DependencyProperty,然后我会在状态管理中引入冗余。除了使所有内容保持同步的额外负担之外,它甚至可能变得不一致(如果我无法很好地同步)。

避免在UI状态管理中引入冗余的正确方法是什么,但仍然利用DependencyProperty-s促进的数据绑定功能?

4 个答案:

答案 0 :(得分:3)

您可以使用INotifyPropertyChanged。当CurrentState更改时,只需发送给定WindowXVisible的更改通知(DP has a callback为此)。

绑定通常可以通过DependencyProperty或INotifyPropertyChanged通知来监听更改(必须手动发送,不像DP)。

您可以使用this tool自动生成通知调用(不会增加代码的复杂性)。它甚至可以很好地处理这些非常重要的案件。

编辑:

将其注册到CurrentStateProperty的PropertyMetadata中。

    private static void OnCurrentStateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        this.OnPropertyChanged("CurrentState");
        this.OnPropertyChanged("Window1Visible");
        this.OnPropertyChanged("Window2Visible");
    }

OnPropertyChanged只需调用PropertyChanged事件,将其作为发件人,将字符串作为属性名称。

这将导致Window1Visible和Window2Visible绑定更新并获取新值。

顺便说一下,你应该试着找出比Window1和WIndow2更好的名字。

答案 1 :(得分:2)

有几个好的答案,所以我不会直接提出具体的解决方案,但我会解决您所面临的普遍关注。你担心添加一些依赖属性机制来复制你已经拥有的看似简单的状态匹配概念可能是多余的,甚至更糟糕的是它可能无法正常工作。

事实是,您只需使用依赖属性基础结构而不是CLR基础结构重新实现相同的概念。是的,它会更加冗长,但这是整个WPF所依据的坚如磐石的东西。例如,ListBoxSelectedItemSelectedIndex需要保持同步,就像您的示例一样。一旦你做对了,无论你做什么,它们都永远不会失去同步。

可见性改变的唯一时间是状态发生变化,因此您可以设置状态更改回调的可见性,只要您相信将调用回调。事实上,即使没有依赖属性,您有时也会看到程序员使用这种策略来避免在属性的getter中进行计算。因此,想想它不是冗余,而只是实现同一目标的不同方式。

答案 2 :(得分:1)

您可以绑定到CurrentState并撰写转换为可见性的IValueConverter

This question可能有用。

编辑 - 假设您需要一个依赖属性来进行数据绑定。否则,INotifyPropertyChanged更容易。

答案 3 :(得分:1)

绑定对CLR对象不起作用。它仅适用于依赖属性。因此,我会将WindowXStyle转换为只读依赖项属性:

using System;
using System.ComponentModel;
using System.Windows;

namespace MyNamespace
{
    public enum Phases { Phase1, Phase2, Phase3 }

    public class UIStateModel : DependencyObject
    {
        static UIStateModel()
        {
            CurrentStateProperty = DependencyProperty.Register("CurrentState", typeof(Phases), typeof(UIStateModel),
                new FrameworkPropertyMetadata
                {
                    PropertyChangedCallback = new PropertyChangedCallback(OnCurrentStateChanged)
                });

            Window1VisibilityPropertyKey = DependencyProperty.RegisterReadOnly("Window1Visiblity", typeof(Visibility), typeof(UIStateModel),
                new PropertyMetadata());
            Window1VisibilityProperty = Window1VisibilityPropertyKey.DependencyProperty;

            Window2VisibilityPropertyKey = DependencyProperty.RegisterReadOnly("Window2Visiblity", typeof(Visibility), typeof(UIStateModel),
                new PropertyMetadata());
            Window2VisibilityProperty = Window2VisibilityPropertyKey.DependencyProperty;
        }

        public Phases CurrentState
        {
            get { return (Phases)GetValue(CurrentStateProperty); }
            set { SetValue(CurrentStateProperty, value); }
        }

        public static DependencyProperty CurrentStateProperty;

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public Visibility Window1Visibility
        {
            get { return (Visibility)GetValue(Window1VisibilityProperty); }
            protected set { SetValue(Window1VisibilityPropertyKey, value); }
        }

        public static readonly DependencyProperty Window1VisibilityProperty;
        private static readonly DependencyPropertyKey Window1VisibilityPropertyKey;

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public Visibility Window2Visibility
        {
            get { return (Visibility)GetValue(Window2VisibilityProperty); }
            protected set { SetValue(Window2VisibilityPropertyKey, value); }
        }

        public static readonly DependencyProperty Window2VisibilityProperty;
        private static readonly DependencyPropertyKey Window2VisibilityPropertyKey;


        public Visibility Window1Visible // Databound to Window1.Visibility
        {
            get
            {
                if (this.CurrentState == Phases.Phase1) return Visibility.Visible;
                else return Visibility.Hidden;
            }
        }
        public Visibility Window2Visible // Databound to Window2.Visibility
        {
            get
            {
                if (this.CurrentState == Phases.Phase2) return Visibility.Visible;
                else return Visibility.Hidden;
            }
        }

        private static void OnCurrentPageChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            UIStateModel element = (UIStateModel)obj;

            Phases oldPhase = (Phases)e.OldValue;
            Phases newPhase = (Phases)e.NewValue;

            //Probably want to use Collapsed as apposed to Hidden for UI Measure/Arrange purposes
            switch (oldPhase)
            {
                case Phases.Phase1:
                    element.Window1Visibility = Visibility.Hidden;
                    break;
                case Phases.Phase2:
                    element.Window2Visibility = Visibility.Hidden;
                    break;
                case Phases.Phase3:
                    //element.Window3Visiblity = Visibility.Hidden;
                    break;
                default:
                    //??
                    break;
            }

            switch (newPhase)
            {
                case Phases.Phase1:
                    element.Window1Visibility = Visibility.Visible;
                    break;
                case Phases.Phase2:
                    element.Window2Visibility = Visibility.Visible;
                    break;
                case Phases.Phase3:
                    //element.Window3Visiblity = Visibility.Visible;
                    break;
                default:
                    //??
                    break;
            }
        }

        //...
    }
}

请注意,您可能也希望使用Visiblity.Collapsed与Visiblity.Hidden相关...折叠不仅隐藏了对象,而且不影响其他UIElements的测量/排列。隐藏会影响其他元素的测量和排列,但它实际上并没有绘制元素(更多地将其视为“不可见”)。