【问题标题】:Event for synchronizing all controls using the same Behavior使用相同行为同步所有控件的事件
【发布时间】:2016-07-03 19:33:01
【问题描述】:

在我的 WPF 应用程序中,我有几个带有大量输入字段的表单供用户使用。事实证明,并非每个用户都需要所有字段,具体取决于其公司的流程,因此我有一个新要求,允许用户根据自己的需要隐藏字段。

我计划为此使用一个行为,它可以附加到基本上每个 WPF 控件。该行为将向每个控件添加一个 ContextMenu,以允许显示/隐藏所有可用字段。我目前看起来运行良好的测试项目有四个 DependencyProperties 来使一切正常:

  1. 字符串 VisibilityGroupName:

这以某种方式作为每个字段的 ID,但不是唯一的,以便将多个字段组合在一起(例如,字段标题的标签到其相应的文本框)。此字符串当前也用作用户在 ContextMenu 中看到的名称。

  1. 字典可见性字典:

此字典跟踪字段的所有可见性状态。在我的应用程序中,我将把它序列化为 XML,以使用户的决策持久化。

  1. bool AllowCustomVisibility:

这只是一个标志,用于关闭整个功能。

  1. bool NotificationDummy:

这就是有趣的地方。目前,我将此属性与 ValueChanged 事件结合使用,以通知所有控件状态已更改,以便他们检查是否受到影响。虽然这按预期工作,但我知道这只是一个糟糕的解决方法,因为我不知道如何正确完成通知。

有人知道它是如何正确完成的吗?我已经用 TODO 标记了代码中的相应位置:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interactivity;

namespace CustomizableUserControlVisibility
{
    public class WPFCustomVisibilityBehavior : Behavior<DependencyObject>
    {
        #region Fields
        private Control _control = null;
        private ContextMenu _contextMenu;
        private bool _contextMenuIsBuilt = false;
        #endregion

        #region Properties
        public bool NotificationDummy
        {
            get { return (bool)GetValue(NotificationDummyProperty); }
            set { SetValue(NotificationDummyProperty, value); }
        }

        public static readonly DependencyProperty NotificationDummyProperty = DependencyProperty.Register("NotificationDummy", typeof(bool), typeof(WPFCustomVisibilityBehavior), new PropertyMetadata(false));

        public bool AllowCustomVisibility
        {
            get { return (bool)GetValue(AllowCustomVisibilityProperty); }
            set { SetValue(AllowCustomVisibilityProperty, value); }
        }

        public static readonly DependencyProperty AllowCustomVisibilityProperty = DependencyProperty.Register("AllowCustomVisibility", typeof(bool), typeof(WPFCustomVisibilityBehavior), new PropertyMetadata(false));

        public string VisibilityGroupName
        {
            get { return (string)GetValue(VisibilityGroupNameProperty); }
            set { SetValue(VisibilityGroupNameProperty, value); }
        }

        public static readonly DependencyProperty VisibilityGroupNameProperty = DependencyProperty.Register("VisibilityGroupName", typeof(string), typeof(WPFCustomVisibilityBehavior), new PropertyMetadata(string.Empty));

        public Dictionary<string, bool> VisibilityDictionary
        {
            get { return (Dictionary<string, bool>)GetValue(VisibilityDictionaryProperty); }
            set { SetValue(VisibilityDictionaryProperty, value); }
        }

        public static readonly DependencyProperty VisibilityDictionaryProperty = DependencyProperty.Register("VisibilityDictionary", typeof(Dictionary<string, bool>), typeof(WPFCustomVisibilityBehavior), new PropertyMetadata(new Dictionary<string, bool>()));
        #endregion

        #region Constructor
        public WPFCustomVisibilityBehavior()
        {
            // TODO: There should be a better way to notify other controls about state changes than this...
            var temp = DependencyPropertyDescriptor.FromProperty(WPFCustomVisibilityBehavior.NotificationDummyProperty, typeof(WPFCustomVisibilityBehavior));
            if (temp != null)
            {
                temp.AddValueChanged(this, OnNotificationDummyChanged);
            }
        }
        #endregion

        #region Overrrides
        protected override void OnAttached()
        {
            base.OnAttached();

            if (this.AllowCustomVisibility == false)
            {
                return;
            }

            this._control = this.AssociatedObject as Control;

            if (!string.IsNullOrEmpty(this.VisibilityGroupName) && this._control != null)
            {
                if (this.VisibilityDictionary.ContainsKey(this.VisibilityGroupName))
                {
                    if (this.VisibilityDictionary[this.VisibilityGroupName])
                    {
                        this._control.Visibility = Visibility.Visible;
                    }
                    else
                    {
                        this._control.Visibility = Visibility.Collapsed;
                    }
                }
                else
                {
                    this.VisibilityDictionary.Add(this.VisibilityGroupName, this._control.Visibility == Visibility.Visible ? true : false);
                }

                // Add a ContextMenu to the Control, but only if it does not already have one (TextBox brings its default ContextMenu for copy, cut and paste)
                if (this._control.ContextMenu == null && !(this._control is TextBox))
                {
                    this._contextMenu = new ContextMenu();
                    ContextMenuService.SetContextMenu(this._control, this._contextMenu);
                    this._control.ContextMenuOpening += (sender, e) => { ContextMenuOpening(e); };
                }
            }
        }
        #endregion

        #region Event handling
        private void ContextMenuOpening(ContextMenuEventArgs e)
        {
            if (this._contextMenuIsBuilt == false)
            {
                this._contextMenu.Items.Clear();

                Dictionary<string, MenuItem> menuItems = new Dictionary<string, MenuItem>();

                foreach (string k in this.VisibilityDictionary.Keys)
                {
                    MenuItem menuItem = new MenuItem() { Header = k, Name = k, IsCheckable = true, StaysOpenOnClick = true };
                    menuItem.Click += MenuItem_Click;

                    menuItems.Add(k, menuItem);
                }

                var keyList = menuItems.Keys.ToList();
                keyList.Sort();

                foreach (string key in keyList)
                {
                    this._contextMenu.Items.Add(menuItems[key]);
                }

                this._contextMenuIsBuilt = true;
            }

            foreach (MenuItem mi in this._contextMenu.Items)
            {
                mi.IsChecked = this.VisibilityDictionary[mi.Name];
            }
        }

        private void MenuItem_Click(object sender, RoutedEventArgs e)
        {
            MenuItem mi = sender as MenuItem;

            if (mi != null && this.VisibilityDictionary != null && this.VisibilityDictionary.ContainsKey(mi.Name))
            {
                this.VisibilityDictionary[mi.Name] = mi.IsChecked;

                // TODO: There should be a better way to notify other controls about state changes than this...
                this.NotificationDummy = !NotificationDummy;
            }
        }

        private void OnNotificationDummyChanged(object sender, EventArgs args)
        {
            // TODO: There should be a better way to notify other controls about state changes than this...
            if (this._control != null && this.VisibilityDictionary != null && !string.IsNullOrEmpty(this.VisibilityGroupName))
            {
                if (this.VisibilityDictionary.ContainsKey(this.VisibilityGroupName))
                {
                    if (this.VisibilityDictionary[this.VisibilityGroupName])
                    {
                        this._control.Visibility = Visibility.Visible;
                    }
                    else
                    {
                        this._control.Visibility = Visibility.Collapsed;
                    }
                }
            }
        }
        #endregion
    }
}

【问题讨论】:

    标签: c# .net wpf behavior attachedbehaviors


    【解决方案1】:

    由于缺乏任何其他想法,我决定使用静态事件,这似乎很好地解决了我的问题,这种方法至少为我节省了我必须首先使用的 NotificationDummy-DependencyProperty。

    如果有人感兴趣 - 这是我的最终解决方案:

    命名空间CustomizableUserControlVisibility { 公共委托 void VisibilityChangedEventHandler(object visibilityDictionary);

    public class WPFCustomVisibilityBehavior : Behavior<DependencyObject>
    {
        #region Fields
        public static event VisibilityChangedEventHandler OnVisibilityChanged;
    
        private Control _control = null;
        private ContextMenu _contextMenu;
        private bool _contextMenuIsBuilt = false;
        #endregion
    
        #region Properties
        public bool AllowCustomVisibility
        {
            get { return (bool)GetValue(AllowCustomVisibilityProperty); }
            set { SetValue(AllowCustomVisibilityProperty, value); }
        }
    
        public static readonly DependencyProperty AllowCustomVisibilityProperty = DependencyProperty.Register("AllowCustomVisibility", typeof(bool), typeof(WPFCustomVisibilityBehavior), new PropertyMetadata(false));
    
        public string VisibilityGroupName
        {
            get { return (string)GetValue(VisibilityGroupNameProperty); }
            set { SetValue(VisibilityGroupNameProperty, value); }
        }
    
        public static readonly DependencyProperty VisibilityGroupNameProperty = DependencyProperty.Register("VisibilityGroupName", typeof(string), typeof(WPFCustomVisibilityBehavior), new PropertyMetadata(string.Empty));
    
        public Dictionary<string, bool> VisibilityDictionary
        {
            get { return (Dictionary<string, bool>)GetValue(VisibilityDictionaryProperty); }
            set { SetValue(VisibilityDictionaryProperty, value); }
        }
    
        public static readonly DependencyProperty VisibilityDictionaryProperty = DependencyProperty.Register("VisibilityDictionary", typeof(Dictionary<string, bool>), typeof(WPFCustomVisibilityBehavior), new PropertyMetadata(new Dictionary<string, bool>()));
        #endregion
    
        #region Constructor
        public WPFCustomVisibilityBehavior()
        {
            OnVisibilityChanged += VisibilityChanged;
        }
        #endregion
    
        #region Overrrides
        protected override void OnAttached()
        {
            base.OnAttached();
    
            if (this.AllowCustomVisibility == false)
            {
                return;
            }
    
            this._control = this.AssociatedObject as Control;
    
            if (!string.IsNullOrEmpty(this.VisibilityGroupName) && this._control != null)
            {
                if (this.VisibilityDictionary.ContainsKey(this.VisibilityGroupName))
                {
                    if (this.VisibilityDictionary[this.VisibilityGroupName])
                    {
                        this._control.Visibility = Visibility.Visible;
                    }
                    else
                    {
                        this._control.Visibility = Visibility.Collapsed;
                    }
                }
                else
                {
                    this.VisibilityDictionary.Add(this.VisibilityGroupName, this._control.Visibility == Visibility.Visible ? true : false);
                }
            }
    
            // Add a ContextMenu to the Control, but only if it does not already have one (TextBox brings its default ContextMenu for copy, cut and paste)
            if (this._control != null && this._control.ContextMenu == null && !(this._control is TextBox))
            {
                this._contextMenu = new ContextMenu();
                ContextMenuService.SetContextMenu(this._control, this._contextMenu);
                this._control.ContextMenuOpening += (sender, e) => { ContextMenuOpening(e); };
            }
        }
        #endregion
    
        #region Event handling
        private void ContextMenuOpening(ContextMenuEventArgs e)
        {
            if (this._contextMenuIsBuilt == false)
            {
                // Clear Items just to be sure there is nothing in it...
                this._contextMenu.Items.Clear();
    
                // Create default items first
                MenuItem showAll = new MenuItem() { Header = "Show all optional fields", IsCheckable = false, FontWeight = FontWeights.Bold };
                showAll.Click += MenuItem_ShowAll_Click;
                MenuItem hideAll = new MenuItem() { Header = "Hide all optional fields", IsCheckable = false, FontWeight = FontWeights.Bold };
                hideAll.Click += MenuItem_HideAll_Click;
    
                // Create field items and sort them by name
                Dictionary<string, MenuItem> menuItems = new Dictionary<string, MenuItem>();
                foreach (string k in this.VisibilityDictionary.Keys)
                {
                    MenuItem menuItem = new MenuItem() { Header = k, Name = k, IsCheckable = true, StaysOpenOnClick = true };
                    menuItem.Click += MenuItem_Click;
    
                    menuItems.Add(k, menuItem);
                }
                var keyList = menuItems.Keys.ToList();
                keyList.Sort();
    
                // Now add default items followed by field items
                this._contextMenu.Items.Add(showAll);
                this._contextMenu.Items.Add(hideAll);
                this._contextMenu.Items.Add(new Separator());
    
                foreach (string key in keyList)
                {
                    this._contextMenu.Items.Add(menuItems[key]);
                }
    
                this._contextMenuIsBuilt = true;
            }
    
            foreach (Object o in this._contextMenu.Items)
            {
                MenuItem mi = o as MenuItem;
    
                if (mi != null && mi.FontWeight != FontWeights.Bold)
                {
                    mi.IsChecked = this.VisibilityDictionary[mi.Name];
                }
            }
        }
    
        private void MenuItem_Click(object sender, RoutedEventArgs e)
        {
            MenuItem mi = sender as MenuItem;
    
            if (mi != null && this.VisibilityDictionary != null && this.VisibilityDictionary.ContainsKey(mi.Name))
            {
                this.VisibilityDictionary[mi.Name] = mi.IsChecked;
    
                OnVisibilityChanged(this.VisibilityDictionary);
            }
        }
    
        private void MenuItem_HideAll_Click(object sender, RoutedEventArgs e)
        {
            List<string> keys = this.VisibilityDictionary.Keys.ToList<string>();
    
            foreach (string key in keys)
            {
                this.VisibilityDictionary[key] = false;
            }
    
            OnVisibilityChanged(this.VisibilityDictionary);
        }
        private void MenuItem_ShowAll_Click(object sender, RoutedEventArgs e)
        {
            List<string> keys = this.VisibilityDictionary.Keys.ToList<string>();
    
            foreach (string key in keys)
            {
                this.VisibilityDictionary[key] = true;
            }
    
            OnVisibilityChanged(this.VisibilityDictionary);
        }
    
        private void VisibilityChanged(object visibilityDictionary)
        {
            if (this._control != null && this.VisibilityDictionary != null && this.VisibilityDictionary == visibilityDictionary && !string.IsNullOrEmpty(this.VisibilityGroupName))
            {
                if (this.VisibilityDictionary.ContainsKey(this.VisibilityGroupName))
                {
                    if (this.VisibilityDictionary[this.VisibilityGroupName])
                    {
                        this._control.Visibility = Visibility.Visible;
                    }
                    else
                    {
                        this._control.Visibility = Visibility.Collapsed;
                    }
                }
            }
        }
        #endregion
    }
    

    }

    【讨论】:

      猜你喜欢
      • 2013-02-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-21
      • 2020-08-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多