2016-03-17 3 views
0

dans mon application WPF J'ai plusieurs formulaires avec une multitude de champs de saisie pour l'utilisateur. Comme il s'avère que tous les utilisateurs n'ont pas besoin de tous les champs en fonction du processus de leur entreprise, j'ai donc la nouvelle obligation de permettre à l'utilisateur de cacher les champs en fonction de ses propres besoins.Événement de synchronisation de tous les contrôles utilisant le même comportement

Je prévoyais d'utiliser un comportement pour cela qui pourrait être attaché à pratiquement chaque contrôle WPF. Le comportement ajouterait un ContextMenu à chacun des contrôles permettant d'afficher/masquer tous les champs disponibles. Mon projet de test actuel qui semble fonctionner agréable a quatre DependencyProperties pour faire tout ce travail:

  1. chaîne VisibilityGroupName:

Cela agit en quelque sorte comme un identifiant pour chaque champ mais n'est pas unique pour plusieurs groupes champs togther (eG une étiquette pour une légende de champ à sa TextBox correspondante). Cette chaîne est actuellement également utilisée comme nom que l'utilisateur voit dans le ContextMenu.

  1. Dictionnaire VisibilityDictionary:

Ce dictionnaire conserve la trace de tous les états de visibilité des champs. Dans mon application, je vais sérialiser cela en XML afin de rendre les décisions des utilisateurs persistantes.

  1. bool AllowCustomVisibility:

Ceci est juste un drapeau pour changer toute la fonctionalité au large.

  1. bool NotificationDummy:

C'est là que ça devient intéressant. Actuellement, j'utilise cette propriété conjointement avec un événement ValueChanged pour signaler à tous les contrôles qu'un état a été modifié afin qu'ils puissent vérifier s'ils sont affectés. Bien que cela fonctionne comme prévu je sais que c'est juste une mauvaise solution de contournement parce que je ne sais pas comment la notification serait faite correctement.

Est-ce que quelqu'un a une idée de comment cela se fait correctement? J'ai marqué les endroits correspondants dans le code avec 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 
    } 
} 

Répondre

0

En raison de l'absence d'autres idées que je décide d'utiliser un événement statique qui semble assez bien résoudre mon problème et cette approche à leasts me sauve la propriété NotificationDummy-DependencyProperty que j'ai dû utiliser en premier lieu.

Si quelqu'un est intéressé - voici ma solution finale:

espace de noms

CustomizableUserControlVisibility { public delegate vide VisibilityChangedEventHandler (objet 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 
} 

}