Click here to Skip to main content
15,881,803 members
Articles / Desktop Programming / WPF

Replacing TreeView with ListBox

Rate me:
Please Sign up or sign in to vote.
4.83/5 (18 votes)
6 Oct 2011BSD4 min read 70.7K   5.2K   66  
TreeView is not good enough to support millions of nodes. Simulating using a ListBox might help.
using System;
using System.Security;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Threading;

namespace MefBasic.Controls
{

	public enum SearchMode
	{
		Instant,
		Regular
	}

	public enum SearchBoxState
	{
		Prompt,
		Input,
		Text,
		Search
	}

	[TemplatePart(Name = "PART_SearchText", Type = typeof(TextBoxBase))]
	[TemplatePart(Name = "PART_SearchButton", Type = typeof(ButtonBase))]
	[TemplatePart(Name = "PART_DropDownArrow", Type = typeof(ToggleButton))]
	public class SearchBox : Control, ICommandSource
	{
		#region Text Dependency Property

		public string Text
		{
			get { return (string)GetValue(TextProperty); }
			set { SetValue(TextProperty, value); }
		}

		public static readonly DependencyProperty TextProperty =
				DependencyProperty.Register("Text", typeof(string), typeof(SearchBox),
					new FrameworkPropertyMetadata(
						String.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
						OnTextChanged));

		#endregion

		#region Prompt Dependency Property

		public string Prompt
		{
			get { return (string)GetValue(PromptProperty); }
			set { SetValue(PromptProperty, value); }
		}

		public static readonly DependencyProperty PromptProperty =
				DependencyProperty.Register("Prompt", typeof(string), typeof(SearchBox), new UIPropertyMetadata(String.Empty));

		#endregion

		#region Command Dependency Property

		public ICommand Command
		{
			get { return (ICommand)GetValue(CommandProperty); }
			set { SetValue(CommandProperty, value); }
		}

		public static readonly DependencyProperty CommandProperty =
				DependencyProperty.Register("Command", typeof(ICommand), typeof(SearchBox), new UIPropertyMetadata(null));

		#endregion

		#region CommandParameter Dependency Property

		public object CommandParameter
		{
			get { return GetValue(CommandParameterProperty); }
			set { SetValue(CommandParameterProperty, value); }
		}

		public static readonly DependencyProperty CommandParameterProperty =
				DependencyProperty.Register("CommandParameter", typeof(object), typeof(SearchBox), new UIPropertyMetadata(null));

		#endregion

		#region CommandTarget Dependency Property

		public IInputElement CommandTarget
		{
			get { return (IInputElement)GetValue(CommandTargetProperty); }
			set { SetValue(CommandTargetProperty, value); }
		}

		public static readonly DependencyProperty CommandTargetProperty =
				DependencyProperty.Register("CommandTarget", typeof(IInputElement), typeof(SearchBox),
					new FrameworkPropertyMetadata(null));

		#endregion

		#region CommandDelay Dependency Property

		public Duration CommandDelay
		{
			get { return (Duration)GetValue(CommandDelayProperty); }
			set { SetValue(CommandDelayProperty, value); }
		}

		public static readonly DependencyProperty CommandDelayProperty =
				DependencyProperty.Register("CommandDelay", typeof(Duration), typeof(SearchBox),
					new FrameworkPropertyMetadata(new Duration(TimeSpan.FromMilliseconds(500d)),
										new PropertyChangedCallback(OnSearchCommandDelayChanged)));

		#endregion

		#region Mode Dependency Property

		public SearchMode Mode
		{
			get { return (SearchMode)GetValue(ModeProperty); }
			set { SetValue(ModeProperty, value); }
		}

		public static readonly DependencyProperty ModeProperty =
				DependencyProperty.Register("Mode", typeof(SearchMode), typeof(SearchBox),
					new UIPropertyMetadata(SearchMode.Instant));

		#endregion

		#region State Dependency Property

		public SearchBoxState State
		{
			get { return (SearchBoxState)GetValue(StateProperty); }
			set { SetValue(StateProperty, value); }
		}

		public static readonly DependencyProperty StateProperty =
				DependencyProperty.Register("State", typeof(SearchBoxState), typeof(SearchBox),
					new UIPropertyMetadata(SearchBoxState.Prompt));

		#endregion

		private readonly DispatcherTimer _delayTimer;

		static SearchBox()
		{
			DefaultStyleKeyProperty.OverrideMetadata(
				typeof(SearchBox), new FrameworkPropertyMetadata(typeof(SearchBox))
			);
		}

		public SearchBox()
		{
			_delayTimer = new DispatcherTimer(DispatcherPriority.Background, Dispatcher) {Interval = CommandDelay.TimeSpan};
		    _delayTimer.Tick += OnCommandDelayTimerTick;
		}

		public override void OnApplyTemplate()
		{
			base.OnApplyTemplate();
			var searchText = GetTemplateChild("PART_SearchText") as TextBoxBase;
			if (searchText != null) {
				searchText.IsKeyboardFocusWithinChanged += OnTextBoxIsKeyboardFocusedChanged;
				searchText.KeyDown += OnTextBoxKeyDown;
			}
			var searchButton = GetTemplateChild("PART_SearchButton") as ButtonBase;
			if (searchButton != null) {
				searchButton.Click += OnSearchButtonClick;
			}
			var dropDownArrow = GetTemplateChild("PART_DropDownArrow") as ToggleButton;
			if (dropDownArrow != null) {
				dropDownArrow.Click += OnDropDownArrowClick;
			}
		}

		private void OnTextBoxIsKeyboardFocusedChanged(object sender, DependencyPropertyChangedEventArgs e)
		{
			var focused = (bool)(e.NewValue ?? false);
			if (focused) {
				OnTextBoxGotFocus(sender);
			}
			else {
				OnTextBoxLostFocus();
			}
		}

		private void OnTextBoxLostFocus()
		{
			if (State == SearchBoxState.Input)
				State = SearchBoxState.Prompt;
		}

		private void OnTextBoxGotFocus(object sender)
		{
			if (!String.IsNullOrEmpty(Text)) {
				var searchText = (TextBoxBase)sender;
				searchText.SelectAll();

				State = SearchBoxState.Text;
			}
			else {
				State = SearchBoxState.Input;
			}
		}

		private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			var searchBox = (SearchBox)d;
			var text = (string)e.NewValue;

			searchBox.State = String.IsNullOrEmpty(text) ? SearchBoxState.Input : SearchBoxState.Text;

			if (searchBox.Mode == SearchMode.Instant) {
				searchBox._delayTimer.Stop();
				searchBox._delayTimer.Start();
			}
		}

		private void OnTextBoxKeyDown(object sender, KeyEventArgs e)
		{
			if (e.Key == Key.Escape && Mode == SearchMode.Instant) {
				ClearText(SearchBoxState.Input);
			}
			else if ((e.Key == Key.Return || e.Key == Key.Enter) && Mode == SearchMode.Regular) {
				if (State == SearchBoxState.Search)
					ClearText(SearchBoxState.Input);
				else
					ExecuteCommand();
			}
		}

		private void ClearText(SearchBoxState state)
		{
			if (!String.IsNullOrEmpty(Text))
				Text = String.Empty;
			State = state;
		}

		protected override void OnContextMenuOpening(ContextMenuEventArgs e)
		{
			if (ContextMenu != null)
				ContextMenu.IsOpen = false;
			e.Handled = true;
		}

		private void OnDropDownArrowClick(object sender, RoutedEventArgs e)
		{
			if (ContextMenu == null || !ContextMenu.HasItems)
				return;

			var dropDownArrow = (ToggleButton)sender;

			if (dropDownArrow.IsChecked ?? false) {
				ContextMenu.PlacementTarget = dropDownArrow;
				ContextMenu.Placement = PlacementMode.Bottom;
				ContextMenuService.SetPlacement(dropDownArrow, PlacementMode.Bottom);
			}
		}

		private void OnSearchButtonClick(object sender, RoutedEventArgs e)
		{
			if (!String.IsNullOrEmpty(Text)) {
				if (Mode == SearchMode.Instant || State == SearchBoxState.Search)
					ClearText(SearchBoxState.Prompt);
				else
					ExecuteCommand();
			}
		}

		private static void OnSearchCommandDelayChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			var searchBox = (SearchBox)d;
			if (searchBox != null) {
				searchBox._delayTimer.Interval = ((Duration)e.NewValue).TimeSpan;
				searchBox._delayTimer.Stop();
			}
		}

		private void OnCommandDelayTimerTick(object sender, EventArgs e)
		{
			_delayTimer.Stop();
			ExecuteCommand();
		}

		private void ExecuteCommand()
		{
			ExecuteCommand(this);
			if (Mode == SearchMode.Regular)
				State = SearchBoxState.Search;
		}

		[SecurityCritical]
		private static void ExecuteCommand(ICommandSource commandSource)
		{
			var command = commandSource.Command;
			if (command != null) {
				var commandParameter = commandSource.CommandParameter;
				var commandTarget = commandSource.CommandTarget;
				var routedCommand = command as RoutedCommand;
				if (routedCommand != null) {
					if (commandTarget == null) {
						commandTarget = commandSource as IInputElement;
					}
					if (routedCommand.CanExecute(commandParameter, commandTarget)) {
						routedCommand.Execute(commandParameter, commandTarget);
					}
				}
				else if (command.CanExecute(commandParameter)) {
					command.Execute(commandParameter);
				}
			}
		}
	}
}

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 BSD License


Written By
Software Developer (Senior) KAZ Software Limited
Bangladesh Bangladesh
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions