Click here to Skip to main content
15,888,984 members
Articles / Desktop Programming / WPF

Reflection Studio - Part 1 - Introduction: Architecture and Design

Rate me:
Please Sign up or sign in to vote.
4.83/5 (23 votes)
22 Sep 2010GPL36 min read 60K   6.9K   111  
Reflection Studio is a "developer" application for assembly, database, performance, and code generation, written in C# under WPF 4.0.
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace ReflectionStudio.Controls
{
	/// <summary>
	/// TreeViewExtended provide populate on demand and item selection with the right mouse click
	/// </summary>
	public class TreeViewExtended : TreeView
	{
		/// <summary>
		/// Constructor
		/// </summary>
		static TreeViewExtended()
		{
			FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(typeof(TreeViewExtended),
																		new FrameworkPropertyMetadata(typeof(TreeViewExtended)));
		}

		/// <summary>
		/// OnApplyTemplate allow to attach to PreviewMouseRightButtonDown for selecting tree item before context menu display
		/// </summary>
		public override void OnApplyTemplate()
		{
			base.OnApplyTemplate();
			base.PreviewMouseRightButtonDown += new MouseButtonEventHandler(this.TreeViewExtended_PreviewMouseRightButtonDown);
		}

		#region --------------------DEPENDENCY PROPERTIES--------------------

		/// <summary>
		/// PopulateOnDemand DependencyProperty
		/// </summary>
		public static readonly DependencyProperty PopulateOnDemandProperty =
			   DependencyProperty.Register("PopulateOnDemand", typeof(bool), typeof(TreeViewExtended));

		/// <summary>
		/// PopulateOnDemand property
		/// </summary>
		public bool PopulateOnDemand
		{
			get { return (bool)GetValue(PopulateOnDemandProperty); }
			set { SetValue(PopulateOnDemandProperty, value); }
		}

		#endregion

		#region --------------------EVENTS--------------------

		/// <summary>
		/// Allow to select an item before the context menu pop's up	
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void TreeViewExtended_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
		{
			TreeView control = sender as TreeView;

			IInputElement clickedItem = control.InputHitTest(e.GetPosition(control));
			
			while ((clickedItem != null) && !(clickedItem is TreeViewItem))
			{
				FrameworkElement frameworkkItem = (FrameworkElement)clickedItem;
				clickedItem = (IInputElement)(frameworkkItem.Parent ?? frameworkkItem.TemplatedParent);
			}
			
			if (clickedItem != null)
				((TreeViewItem)clickedItem).IsSelected = true;
		}
		#endregion

		#region --------------------TREEVIEWITEM POPULATE EVENT--------------------

		/// <summary>
		/// ItemNeedPopulateEvent event
		/// </summary>
		public static readonly RoutedEvent ItemNeedPopulateEvent = EventManager.RegisterRoutedEvent("ItemNeedPopulateEvent",
																RoutingStrategy.Bubble,
																typeof(ItemNeedPopulateEventHandler), typeof(TreeViewExtended));

		/// <summary>
		/// ItemNeedPopulateEventHandler delegate signature
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public delegate void ItemNeedPopulateEventHandler(object sender, System.Windows.RoutedEventArgs e);

		/// <summary>
		/// ItemNeedPopulateEventHandler
		/// </summary>
		public event ItemNeedPopulateEventHandler OnItemNeedPopulate
		{
			add { AddHandler(ItemNeedPopulateEvent, value); }
			remove { RemoveHandler(ItemNeedPopulateEvent, value); }
		}

		/// <summary>
		/// shorcut for raising ItemNeedPopulateEvent
		/// </summary>
		/// <param name="item"></param>
		protected void RaiseItemNeedPopulate( TreeViewItem item )
		{
			RoutedEventArgs args = new RoutedEventArgs();
			args.RoutedEvent = ItemNeedPopulateEvent;
			args.Source = item;
			RaiseEvent(args);
		}

		#endregion

		#region --------------------TREEVIEWITEM MANAGEMENT--------------------

		/// <summary>
		/// Internal management of the treeview item expansion. remove the dummy node if needed and fire the ItemNeedPopulateEvent
		/// event when the PopulateOnDemand property is set
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		void node_Expanded(object sender, RoutedEventArgs e)
		{
			TreeViewItem opened = (TreeViewItem)sender;

			if (opened.Items[0] is TreeViewItemDummy && PopulateOnDemand)
			{
				opened.Items.Clear();
				RaiseItemNeedPopulate(opened);
			}
		}

		/// <summary>
		/// Add a new treeview item to the specified parent. Manage dummy node and PopulateOnDemand
		/// </summary>
		/// <param name="parent"></param>
		/// <param name="label"></param>
		/// <param name="tag"></param>
		/// <param name="needDummy"></param>
		/// <returns></returns>
		public TreeViewItem AddItem(TreeViewItem parent, string label, object tag, bool needDummy = true)
		{
			TreeViewItem node = new TreeViewItem();
			node.Header = label;
			node.Tag = tag;

			if (PopulateOnDemand && needDummy)
			{
				node.Expanded += new RoutedEventHandler(node_Expanded);
				node.Items.Add(new TreeViewItemDummy());
			}

			if (parent != null)
				parent.Items.Add(node);
			else
				this.Items.Add(node);

			return node;
		}

		/// <summary>
		/// Add a dummy treeview item to the specified parent. Manage dummy node and PopulateOnDemand
		/// </summary>
		/// <param name="parent"></param>
		/// <param name="needDummy"></param>
		/// <returns></returns>
		public void AddDummyItem(TreeViewItem parent, bool needDummy = true)
		{
			if (PopulateOnDemand && needDummy)
			{
				parent.Expanded += new RoutedEventHandler(node_Expanded);
				parent.Items.Add(new TreeViewItemDummy());
			}
		}

		/// <summary>
		/// Return the parent of a treeview item which contains the given Type in his Tag property
		/// </summary>
		/// <param name="item"></param>
		/// <param name="searchedType"></param>
		/// <returns></returns>
		public TreeViewItem FindParentNode(object item, Type searchedType)
		{
			DependencyObject parent = (DependencyObject)item;

			while (parent != null && !(parent is TreeView))
			{
				if( ((TreeViewItem)parent).Tag != null )
				{
					if( ((TreeViewItem)parent).Tag.GetType() == searchedType )
						return (TreeViewItem)parent;
				}
				parent = ItemsControl.ItemsControlFromItemContainer(parent);
			}
			return (TreeViewItem)parent;
		}

		/// <summary>
		/// Return the parent of a treeview item
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		public TreeViewItem ParentNode(TreeViewItem item)
		{
			DependencyObject parent = (DependencyObject)item;
			return (TreeViewItem)ItemsControl.ItemsControlFromItemContainer(parent);
		}

		#endregion
	}

	/// <summary>
	/// TreeViewItemDummy
	/// </summary>
	internal class TreeViewItemDummy : TreeViewItem
	{
		/// <summary>
		/// Constructor, init the header with dummy string
		/// </summary>
		public TreeViewItemDummy()
		{
			Header = "Dummy";
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Architect
France France
WPF and MVVM fan, I practice C # in all its forms from the beginning of the NET Framework without mentioning C ++ / MFC and other software packages such as databases, ASP, WCF, Web & Windows services, Application, and now Core and UWP.
In my wasted hours, I am guilty of having fathered C.B.R. and its cousins C.B.R. for WinRT and UWP on the Windows store.
But apart from that, I am a great handyman ... the house, a rocket stove to heat the jacuzzi and the last one: a wood oven for pizza, bread, and everything that goes inside

https://guillaumewaser.wordpress.com/
https://fouretcompagnie.wordpress.com/

Comments and Discussions