Laden...

TreeView mit Nodes die Events unterstützen

Erstellt von jaensen vor 15 Jahren Letzter Beitrag vor 14 Jahren 5.664 Views
jaensen Themenstarter:in
2.760 Beiträge seit 2006
vor 15 Jahren
TreeView mit Nodes die Events unterstützen

Beschreibung:

Folgendes Snippet ist ein TreeView welches auch spezielle Nodes mitbringt die folgende Events beherrschen:*Selected *Clicked *CheckedChanged *Expanded *Collapsed *MouseDown *MouseMove *MouseUp

Das hat den Vorteil das man seinen Code nicht mehr im AfterSelect-Handler des Trees unterbringen muss sondern die Nodes nun selber die entsprechenden Events anbieten was der Kapselung sehr zu gute kommt.

Außerdem unterstützt das TreeView rudimentäres Multiselect mittels gedrückt halten der Strg-Taste. Multiselect lässt sich mit dem "AllowMultiselect"-Property ein- und ausschalten. Die selektierten Nodes liegen in der Reihenfolge in der sie selektiert wurden im SelectedNodes-Property des TreeView vor.

Das Snippet enthält mehrere Klassen:*EventTree (von TreeView geerbt) *EventNode (von TreeNode geerbt) *NodeCollection *IGetInternalIList

Das TreeView sollte ohne weiteres in einem bestehendes Projekt benutzt werden können da die regulären Events des Tree's natürlich weiterhin vorhanden sind und man die Funktionalität nach und nach auf die Events der Nodes umbauen kann. Wer z.B. ohnehin schon mit einem abgeleiteten TreeView und Nodes gearbeitet hat muss eigentlich nur die Basisiklasse durch die aus dem Snippet ersetzen.

Der Code hat noch ein wenig Verbeserungsbedarf (auf jeden Fall im Bereich Multiselect) und ist bescheiden dokumentiert aber er funktioniert 😉 Wer die angesprochenen Mankos beheben möchte kann dies gerne tun.


   /// <summary>
   /// A special implementation of the <see cref="System.Windows.Forms.TreeView"/> which notifies it's nodes about
   /// events which concern them.
   /// </summary>
   public class EventTree : TreeView, IGetInternalIList
   {
      #region Private variables / Public properties

      /// <summary>
      /// Contains the nodes.
      /// </summary>
      private NodeCollection _nodes;

      /// <summary>
      /// Contains the selected nodes.
      /// </summary>
      public List<EventNode> SelectedNodes
      {
         get { return _selectedNodes; }
      }
      private List<EventNode> _selectedNodes;

      /// <summary>
      /// Specifies if multiselect should be allowed.
      /// </summary>
      public bool AllowMultiselect
      {
         get { return _allowMultiselect; }
         set { _allowMultiselect = value; }
      }
      private bool _allowMultiselect;

      #endregion

      #region Public hidden properties


      public new NodeCollection Nodes
      {
         get { return _nodes; }
      }

      #endregion

      #region Constructors

      public EventTree()
         : base()
      {
         _nodes = new NodeCollection(this);
         _selectedNodes = new List<EventNode>();

         DrawMode = TreeViewDrawMode.OwnerDrawText;
      }

      #endregion

      #region Protected overridden methods

      protected override void OnAfterSelect(TreeViewEventArgs e)
      {
         base.OnAfterSelect(e);

         EventNode node = null;
         if (!(e.Node is EventNode))
            return;
         else
            node = (EventNode)e.Node;

         // Notify the node
         node.InvokeSelected(EventArgs.Empty);
      }

      protected override void OnAfterCheck(TreeViewEventArgs e)
      {
         base.OnAfterCheck(e);

         EventNode node = null;
         if (!(e.Node is EventNode))
            return;
         else
            node = (EventNode)e.Node;

         // Notify the node
         node.InvokeCheckedChanged(EventArgs.Empty);
      }

      protected override void OnMouseDown(MouseEventArgs e)
      {
         base.OnMouseDown(e);

         EventNode node = null;
         TreeViewHitTestInfo tvhti = HitTest(e.Location);
         if (tvhti == null)
            return;
         if (tvhti.Node == null)
            return;
         if (!(tvhti.Node is EventNode))
            return;
         else
            node = (EventNode)tvhti.Node;
         if (!node.Bounds.Contains(e.Location))
            return;

         // Notify the node
         node.InvokeMouseDown(e);
      }

      protected override void OnMouseMove(MouseEventArgs e)
      {
         base.OnMouseMove(e);

         EventNode node = null;
         TreeViewHitTestInfo tvhti = HitTest(e.Location);
         if (tvhti == null)
            return;
         if (tvhti.Node == null)
            return;
         if (!(tvhti.Node is EventNode))
            return;
         else
            node = (EventNode)tvhti.Node;
         if (!node.Bounds.Contains(e.Location))
            return;

         // Notify the node
         node.InvokeMouseMove(e);
      }

      protected override void OnMouseUp(MouseEventArgs e)
      {
         base.OnMouseUp(e);

         EventNode node = null;
         TreeViewHitTestInfo tvhti = HitTest(e.Location);
         if (tvhti == null)
            return;
         if (tvhti.Node == null)
            return;
         if (!(tvhti.Node is EventNode))
            return;
         else
            node = (EventNode)tvhti.Node;
         if (!node.Bounds.Contains(e.Location))
            return;

         // Notify the node
         node.InvokeMouseUp(e);
      }

      protected override void OnAfterExpand(TreeViewEventArgs e)
      {
         base.OnAfterExpand(e);

         EventNode node = null;
         if (!(e.Node is EventNode))
            return;
         else
            node = (EventNode)e.Node;

         // Notify the node
         node.InvokeExpanded(EventArgs.Empty);
      }

      protected override void OnAfterCollapse(TreeViewEventArgs e)
      {
         base.OnAfterCollapse(e);

         EventNode node = null;
         if (!(e.Node is EventNode))
            return;
         else
            node = (EventNode)e.Node;

         // Notify the node
         node.InvokeCollapsed(EventArgs.Empty);
      }

      protected override void OnDrawNode(DrawTreeNodeEventArgs e)
      {
         if (!AllowMultiselect)
         {
            e.DrawDefault = true;
            base.OnDrawNode(e);
         }
         else
         {
            if (e.Node.BackColor == Color.Empty)
               e.DrawDefault = true;   // Initially draw the node as usual
            else
            {
               SolidBrush backGroundBrush = new SolidBrush(e.Node.BackColor);
               SolidBrush foreColorBrush = new SolidBrush(e.Node.ForeColor);

               e.Graphics.FillRectangle(backGroundBrush, e.Bounds);
               e.Graphics.DrawString(e.Node.Text, Font, foreColorBrush, e.Bounds);

               backGroundBrush.Dispose();
               foreColorBrush.Dispose();
            }
         }
      }

      #endregion

      #region IGetInternalIList Members

      System.Collections.IList IGetInternalIList.Get()
      {
         return (IList)base.Nodes;
      }

      #endregion
   }

   /// <summary>
   /// The node which belongs to the <see cref="CEventTree"/>.
   /// </summary>
   public class EventNode : TreeNode, IGetInternalIList
   {
      #region Private variables / Public properties

      /// <summary>
      /// Returns the collection of sub nodes.
      /// </summary>
      public new NodeCollection Nodes
      {
         get { return _nodeCollection; }
      }
      private NodeCollection _nodeCollection;

      /// <summary>
      /// If a mouse button is currently pressed.
      /// </summary>
      private bool _mouseDown;

      /// <summary>
      /// Inidicates whether the node is selected.
      /// </summary>
      public new bool IsSelected
      {
         get { return _selected; }
         set 
         { 
            _selected = value;
            if (_selected)
            {
               BackColor = SystemColors.Highlight;
               ForeColor = SystemColors.HighlightText;
            }
            else
            {
               BackColor = SystemColors.Window;
               ForeColor = SystemColors.WindowText;
            }
         }
      }
      private bool _selected;

      #endregion

      #region Constructors

      /// <summary>
      /// Creates a new node and sets the parent tree.
      /// </summary>
      public EventNode()
         : base()
      {
         _nodeCollection = new NodeCollection(this);
      }

      /// <summary>
      /// Creates a new node, sets the parent tree and it's text.
      /// </summary>
      /// <param name="text">The node's text</param>
      public EventNode(string text)
         : base(text)
      {
         _nodeCollection = new NodeCollection(this);
      }

      /// <summary>
      /// Creates a new node, sets the parent tree, it's text and children.
      /// </summary>
      /// <param name="text">The node's text</param>
      /// <param name="children">The node's children</param>
      public EventNode(string text, TreeNode[] children)
         : base(text, children)
      {
         _nodeCollection = new NodeCollection(this);
      }

      /// <summary>
      /// Creates a new node, sets the parent tree, it's text and the image indices.
      /// </summary>
      /// <param name="text">The node's text</param>
      /// <param name="imageIndex">The image index</param>
      /// <param name="selectedImageIndex">The selected image index</param>
      public EventNode(string text, int imageIndex, int selectedImageIndex)
         : base(text, imageIndex, selectedImageIndex)
      {
         _nodeCollection = new NodeCollection(this);
      }

      /// <summary>
      /// Creates a new node, sets the parent tree, it's text, the image indices and children.
      /// </summary>
      /// <param name="text">The node's text</param>
      /// <param name="imageIndex">The image index</param>
      /// <param name="selectedImageIndex">The selected image index</param>
      /// <param name="children">The node's children</param>
      public EventNode(string text, int imageIndex, int selectedImageIndex, TreeNode[] children)
         : base(text, imageIndex, selectedImageIndex, children)
      {
         _nodeCollection = new NodeCollection(this);
      }

      #endregion

      #region Internal methods

      /// <summary>
      /// Invokes the Selected event.
      /// </summary>
      internal void InvokeSelected(EventArgs e)
      {
         OnSelected(e);
      }
      /// <summary>
      /// Invokes the MouseDown event.
      /// </summary>
      internal void InvokeMouseDown(MouseEventArgs e)
      {
         OnMouseDown(e);
      }
      /// <summary>
      /// Invokes the MouseMove event.
      /// </summary>
      internal void InvokeMouseMove(MouseEventArgs e)
      {
         OnMouseMove(e);
      }
      /// <summary>
      /// Invokes the MouseUp event.
      /// </summary>
      internal void InvokeMouseUp(MouseEventArgs e)
      {
         OnMouseUp(e);
      }
      /// <summary>
      /// Invokes the CheckedChanged event.
      /// </summary>
      internal void InvokeCheckedChanged(EventArgs e)
      {
         OnCheckedChanged(e);
      }
      /// <summary>
      /// Invokes the Expanded event.
      /// </summary>
      internal void InvokeExpanded(EventArgs e)
      {
         OnExpanded(e);
      }
      /// <summary>
      /// Invokes the Collapsed event.
      /// </summary>
      internal void InvokeCollapsed(EventArgs e)
      {
         OnCollapsed(e);
      }

      #endregion

      #region Protected virtual methods

      /// <summary>
      /// Handles and fires the Selected event.
      /// </summary>
      protected virtual void OnSelected(EventArgs e)
      {
         EventHandler handler = Selected;
         if (handler != null)
            handler(this, e);
      }

      /// <summary>
      /// Handles and fires the Click event.
      /// </summary>
      protected virtual void OnClick(EventArgs e)
      {
         if (((EventTree)TreeView).AllowMultiselect)
         {
            if (IsSelected && Control.ModifierKeys == Keys.Control)
            {
               if (((EventTree)TreeView).SelectedNodes.Contains(this))
                  ((EventTree)TreeView).SelectedNodes.Remove(this);
               IsSelected = false;
            }
            else
            {
               IsSelected = true;
               ((EventTree)TreeView).SelectedNodes.Add(this);
            }

            if (Control.ModifierKeys != Keys.Control)
               for (int i = ((EventTree)TreeView).SelectedNodes.Count - 1; i >= 0; i--)
               {
                  EventNode node = ((EventTree)TreeView).SelectedNodes[i];
                  if (node == this)
                     continue;
                  node.IsSelected = false;
                  ((EventTree)TreeView).SelectedNodes.RemoveAt(i);
               }
         }

         EventHandler handler = Clicked;
         if (handler != null)
            handler(this, e);
      }

      /// <summary>
      /// Handles and fires the MouseDown event.
      /// </summary>
      protected virtual void OnMouseDown(MouseEventArgs e)
      {
         _mouseDown = true;

         MouseEventHandler handler = MouseDown;
         if (handler != null)
            handler(this, e);
      }

      /// <summary>
      /// Handles and fires the MouseMove event.
      /// </summary>
      protected virtual void OnMouseMove(MouseEventArgs e)
      {
         MouseEventHandler handler = MouseMove;
         if (handler != null)
            handler(this, e);
      }

      /// <summary>
      /// Handles and fires the MouseUp event.
      /// </summary>
      protected virtual void OnMouseUp(MouseEventArgs e)
      {
         if (_mouseDown)
            OnClick(EventArgs.Empty);
         _mouseDown = false;

         MouseEventHandler handler = MouseUp;
         if (handler != null)
            handler(this, e);
      }

      /// <summary>
      /// Handles and fires the CheckedChanged event.
      /// </summary>
      protected virtual void OnCheckedChanged(EventArgs e)
      {
         EventHandler handler = CheckedChanged;
         if (handler != null)
            handler(this, e);
      }

      /// <summary>
      /// Handles and fires the Expanded event.
      /// </summary>
      protected virtual void OnExpanded(EventArgs e)
      {
         EventHandler handler = Expanded;
         if (handler != null)
            handler(this, e);
      }

      /// <summary>
      /// Handles and fires the Collapsed event.
      /// </summary>
      protected virtual void OnCollapsed(EventArgs e)
      {
         EventHandler handler = Collapsed;
         if (handler != null)
            handler(this, e);
      }

      #endregion

      #region Public events

      /// <summary>
      /// Happens when the node was selected.
      /// </summary>
      public event EventHandler Selected;
      /// <summary>
      /// Happens when the node was clicked.
      /// </summary>
      public event EventHandler Clicked;
      /// <summary>
      /// Happens when the check state changed.
      /// </summary>
      public event EventHandler CheckedChanged;
      /// <summary>
      /// Happens when the node was expanded.
      /// </summary>
      public event EventHandler Expanded;
      /// <summary>
      /// Happens when the node was collapsed.
      /// </summary>
      public event EventHandler Collapsed;

      /// <summary>
      /// Happens if a mouse button is pressed over the node.
      /// </summary>
      public event MouseEventHandler MouseDown;
      /// <summary>
      /// Happens if the mouse moves over the node.
      /// </summary>
      public event MouseEventHandler MouseMove;
      /// <summary>
      /// Happens if a mouse button is released over the node.
      /// </summary>
      public event MouseEventHandler MouseUp;

      #endregion

      #region IGetInternalIList Members

      System.Collections.IList IGetInternalIList.Get()
      {
         return (IList)base.Nodes;
      }

      #endregion
   }

   internal interface IGetInternalIList
   {
      IList Get();
   }

   /// <summary>
   /// A collection which is nearly similar to the TreeNodeCollection and can be used for the
   /// CEventTree- or the CEventNode-class' Nodes-property.
   /// </summary>
   public class NodeCollection : ICollection<EventNode>, IList<EventNode>
   {
      #region Private variables / Public properties

      /// <summary>
      /// The tree to which this collection belongs.
      /// </summary>
      public EventTree ParentTree
      {
         get { return _parentTree; }
      }
      private EventTree _parentTree;

      /// <summary>
      /// Gets the node which nodes are managed by this collection.
      /// </summary>
      public TreeNode ParentNode
      {
         get { return _parentNode; }
      }
      private TreeNode _parentNode;

      /// <summary>
      /// Specifies if the collection is used as a node collection in an other node.
      /// </summary>
      private bool _usedForNode = false;


      private IList _genericNodeCollection;

      #endregion

      #region Public constructor

      public NodeCollection(EventTree tree)
      {
         _parentTree = tree;
         _usedForNode = false;
         _genericNodeCollection = ((IGetInternalIList)tree).Get();
      }

      public NodeCollection(EventNode node)
      {
         _parentNode = node;
         _usedForNode = true;
         _genericNodeCollection = ((IGetInternalIList)node).Get();
      }

      #endregion

      #region ICollection<CEventNode> Members

      public void Add(EventNode item)
      {
         _genericNodeCollection.Add(item);
      }

      public EventNode Add(string text)
      {
         EventNode created = null;
         if (_usedForNode)
            created = new EventNode(text);
         else
            created = new EventNode(text);
         Add(created);

         return created;
      }

      public EventNode Add(string key, string text)
      {
         EventNode created = null;
         if (_usedForNode)
            created = new EventNode(text);
         else
            created = new EventNode(text);
         created.Name = key;

         Add(created);

         return created;
      }

      public EventNode Add(string key, string text, int imageIndex, int selectedImageIndex)
      {
         EventNode created = null;
         if (_usedForNode)
            created = new EventNode(text, imageIndex, selectedImageIndex);
         else
            created = new EventNode(text, imageIndex, selectedImageIndex);

         created.Name = key;

         Add(created);

         return created;
      }

      public void Clear()
      {
         _genericNodeCollection.Clear();
      }

      public bool Contains(EventNode item)
      {
         return _genericNodeCollection.Contains(item);
      }

      public void CopyTo(EventNode[] array, int arrayIndex)
      {
         _genericNodeCollection.CopyTo(array, arrayIndex);
      }

      public int Count
      {
         get { return _genericNodeCollection.Count; }
      }

      public bool IsReadOnly
      {
         get { return _genericNodeCollection.IsReadOnly; }
      }

      public bool Remove(EventNode item)
      {
         try
         {
            _genericNodeCollection.Remove(item);
         }
         catch (Exception)
         {
            return false;
         }
         return true;
      }

      #endregion

      #region IEnumerable<CEventNode> Members

      public IEnumerator<EventNode> GetEnumerator()
      {
         foreach (EventNode node in _genericNodeCollection)
         {
            yield return node;
         }
      }

      #endregion

      #region IEnumerable Members

      System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
      {
         return _genericNodeCollection.GetEnumerator();
      }

      #endregion

      #region IList<CEventNode> Members

      public int IndexOf(EventNode item)
      {
         return _genericNodeCollection.IndexOf(item);
      }

      public void Insert(int index, EventNode item)
      {
         _genericNodeCollection.Insert(index, item);
      }

      public void RemoveAt(int index)
      {
         _genericNodeCollection.RemoveAt(index);
      }

      public EventNode this[int index]
      {
         get { return (EventNode)_genericNodeCollection[index]; }
         set { _genericNodeCollection[index] = value; }
      }

      #endregion
   }

Schlagwörter: Multiselect TreeView, TreeNode mit Events

jaensen Themenstarter:in
2.760 Beiträge seit 2006
vor 14 Jahren

*push*
Ein Kollege hat gerade mal den Code des vorher hier geposteten Trees angeschaut und dabei festgestellt das ich den wohl spät Nachts geschrieben haben muss 😉
Deswegen oben nun eine verbesserte Version.

1.564 Beiträge seit 2007
vor 14 Jahren

Gute Arbeit!

Echt sinnvolles Control. 😉

Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+

Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.

jaensen Themenstarter:in
2.760 Beiträge seit 2006
vor 14 Jahren

Definitiv.
Ist aber in Teilen geklaut 😉