Skip to content
Permalink
Browse files

Create SearchBox component

This component replaces the search boxes in CodeExplorer and Add/Remove References dialog.
It provides a vast simplification over the preexisting use of overriding a TextBox with a
Style defined in the resources section.

Also this commit moves Behaviours into their own solution folder, though no namespace adjustments
are performed.
  • Loading branch information...
Vogel612 committed Apr 22, 2019
1 parent dd68164 commit 4999e44b94b817fb3c4e889b90f29eecdcb7dc64
@@ -0,0 +1,58 @@
using System.Windows;
using System.Windows.Controls.Primitives;

namespace Rubberduck.UI.Controls.Behavior
{
using ButtonBehavior = System.Windows.Interactivity.Behavior<ButtonBase>;

public class ResetValueBehavior : ButtonBehavior
{
private ButtonBase _associatedButton;

protected override void OnAttached()
{
_associatedButton = AssociatedObject;

_associatedButton.Click += AssociatedButtonClick;
}

protected override void OnDetaching()
{
_associatedButton.Click -= AssociatedButtonClick;
}

private void AssociatedButtonClick(object sender, RoutedEventArgs e)
{
Affects = Default;
}

public object Affects
{
get => GetValue(AffectsProperty);
set
{
var oldValue = Affects;
SetValue(AffectsProperty, value);
OnPropertyChanged(new DependencyPropertyChangedEventArgs(AffectsProperty, oldValue, value));
}
}

public object Default
{
get => GetValue(DefaultProperty);
set
{
var oldValue = Default;
SetValue(DefaultProperty, value);
OnPropertyChanged(new DependencyPropertyChangedEventArgs(DefaultProperty, oldValue, value));
}
}

// Using a DependencyProperty as the backing store for Affects.
// This enables animation, styling, binding, etc...
public static readonly DependencyProperty AffectsProperty =
DependencyProperty.Register("Affects", typeof(object), typeof(ResetValueBehavior), new UIPropertyMetadata());
public static readonly DependencyProperty DefaultProperty =
DependencyProperty.Register("Default", typeof(object), typeof(ResetValueBehavior), new UIPropertyMetadata(defaultValue: null));
}
}
@@ -1,6 +1,7 @@
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using Rubberduck.Settings;

// credit to http://stackoverflow.com/a/2752538
@@ -9,7 +10,7 @@ namespace Rubberduck.UI.Controls
/// <summary>
/// Interaction logic for NumberPicker.xaml
/// </summary>
public partial class NumberPicker : IDataErrorInfo
public partial class NumberPicker : UserControl, IDataErrorInfo
{
public static readonly DependencyProperty NumValueProperty =
DependencyProperty.Register(nameof(NumValue), typeof(int), typeof(NumberPicker), new UIPropertyMetadata(default(int), PropertyChangedCallback));
@@ -0,0 +1,68 @@
<UserControl x:Class="Rubberduck.UI.Controls.SearchBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:local="clr-namespace:Rubberduck.UI.Controls;assembly="
xmlns:ib="clr-namespace:Rubberduck.UI.Controls.Behavior;assembly="
xmlns:converters="clr-namespace:Rubberduck.UI.Converters;assembly="
mc:Ignorable="d"
MinWidth="50" MinHeight="20"
Height="20"
Name="Root"
d:DesignHeight="20" d:DesignWidth="400"
d:DataContext="{d:DesignInstance local:SearchBox, IsDesignTimeCreatable=True}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<TextBox Text="{Binding Path=Text, ElementName=Root, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
x:Name="ValueContainer"
Background="Transparent"
Panel.ZIndex="2"
VerticalContentAlignment="Center"
Width="Auto"
Grid.Row="0" Grid.Column="0"/>
<!-- this is the actual hint container, it's BELOW the displaying control -->
<TextBox Text="{Binding Path=Hint, ElementName=Root, Mode=OneWay}"
Background="{Binding Path=Background, ElementName=Root}"
Width="{Binding Path=ActualWidth, ElementName=ValueContainer}"
Height="{Binding Path=ActualHeight, ElementName=ValueContainer}"
Panel.ZIndex="1"
VerticalContentAlignment="Center"
Grid.Row="0" Grid.Column="0">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<!-- Setter needs to be within this type to be overwritable with a trigger -->
<Setter Property="Foreground" Value="Transparent" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Text, Source={x:Reference ValueContainer}}" Value="">
<Setter Property="Foreground" Value="{x:Static SystemColors.GrayTextBrush}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<Button Name="SearchButton" Grid.Column="1" Command="{Binding ClearSearchCommand}"
BorderBrush="{x:Static SystemColors.ControlLightBrush}"
Background="Transparent"
Width="20" Height="20" Padding="0" Margin="0,1"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<Button.Resources>
<converters:SearchImageSourceConverter x:Key="SearchToIcon" />
</Button.Resources>
<Image VerticalAlignment="Center" HorizontalAlignment="Center"
Width="16" Height="16"
Source="{Binding Text, ElementName=ValueContainer,
Converter={StaticResource SearchToIcon},
UpdateSourceTrigger=PropertyChanged}" />
<i:Interaction.Behaviors>
<local:FocusElementAfterClickBehavior FocusElement="{x:Reference ValueContainer}"/>
<ib:ResetValueBehavior Affects="{Binding Path=Text, ElementName=ValueContainer, Mode=OneWayToSource}"
Default="{x:Static sys:String.Empty}"/>
</i:Interaction.Behaviors>
</Button>
</Grid>
</UserControl>
@@ -0,0 +1,82 @@
using NLog;
using Rubberduck.UI.Command;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Rubberduck.UI.Controls
{
/// <summary>
/// Interaction logic for SearchBox.xaml
/// </summary>
public partial class SearchBox : UserControl
{
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(nameof(Text), typeof(string), typeof(SearchBox), new UIPropertyMetadata(default(string), PropertyChangedCallback));
public static readonly DependencyProperty HintProperty =
DependencyProperty.Register(nameof(Hint), typeof(string), typeof(SearchBox), new UIPropertyMetadata(default(string), PropertyChangedCallback));

private static void PropertyChangedCallback(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
if (source is SearchBox control)
{
var newValue = (string)e.NewValue;
switch (e.Property.Name)
{
case "Text":
control.Text = newValue;
break;
case "Hint":
control.Hint = newValue;
break;
}
}
}

public string Text
{
get => (string)GetValue(TextProperty);
set
{
var old = GetValue(TextProperty);
SetValue(TextProperty, value);
OnPropertyChanged(new DependencyPropertyChangedEventArgs(TextProperty, old, value));
}
}
public string Hint
{
get => (string)GetValue(HintProperty);
set
{
var old = GetValue(HintProperty);
SetValue(HintProperty, value);
OnPropertyChanged(new DependencyPropertyChangedEventArgs(HintProperty, old, value));
}
}

public ICommand ClearSearchCommand { get => new DelegateCommand(LogManager.GetCurrentClassLogger(), (arg) => Text = ""); }

public SearchBox()
{
// design instance!
Text = "";
Hint = "Search";
Width = 300;
Height = 25;
Background = SystemColors.WindowBrush;
// not so much design instance
InitializeComponent();
}
}
}

0 comments on commit 4999e44

Please sign in to comment.
You can’t perform that action at this time.