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);
}
}
}
}
}