Skip to content
This repository has been archived by the owner on Oct 17, 2024. It is now read-only.

Commit

Permalink
custom textbox
Browse files Browse the repository at this point in the history
  • Loading branch information
byCrookie committed Sep 9, 2022
1 parent 1a1ee01 commit 4f9f0b1
Show file tree
Hide file tree
Showing 7 changed files with 313 additions and 56 deletions.
138 changes: 131 additions & 7 deletions src/TypeCode.Wpf/Controls/ComboBox/ComboBoxStyle.xaml
Original file line number Diff line number Diff line change
@@ -1,14 +1,138 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="clr-namespace:TypeCode.Wpf.Helper.Converters"
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
xmlns:behaviours="clr-namespace:TypeCode.Wpf.Helper.Behaviours"
xmlns:comboBox="clr-namespace:TypeCode.Wpf.Controls.ComboBox"
xmlns:triggers="clr-namespace:TypeCode.Wpf.Helper.Triggers"
xmlns:system="clr-namespace:System;assembly=System.Runtime">

<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource {x:Type ComboBox}}">
<Setter Property="ItemContainerStyle">
<converters:BooleanToVisibilityConverterSelf x:Key="BooleanToVisibilityConverter" True="Visible" False="Collapsed" />
<converters:BooleanToVisibilityConverterSelf x:Key="InvertedBooleanToVisibilityConverter" True="Collapsed"
False="Visible" />
<converters:ObjectToStringConverter x:Key="ObjectToStringConverter"/>
<system:Boolean x:Key="False">False</system:Boolean>

<Style TargetType="{x:Type comboBox:CustomComboBox}">

<Setter Property="Template">
<Setter.Value>
<Style TargetType="ComboBoxItem" BasedOn="{StaticResource {x:Type ComboBoxItem}}">
<Setter Property="Background" Value="{StaticResource Primary}"></Setter>
</Style>
<ControlTemplate TargetType="{x:Type comboBox:CustomComboBox}">

<Grid Validation.ErrorTemplate="{x:Null}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>

<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>

<Border
VerticalAlignment="Center"
Grid.Row="0"
Grid.Column="0">

<Border.Style>
<Style TargetType="{x:Type Border}">
<Setter Property="BorderThickness" Value="1"></Setter>
<Style.Triggers>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(Validation.HasError), UpdateSourceTrigger=PropertyChanged}"
Value="false">
<Setter Property="BorderBrush"
Value="{StaticResource Accent}" />
</DataTrigger>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(Validation.HasError), UpdateSourceTrigger=PropertyChanged}"
Value="true">
<Setter Property="BorderBrush"
Value="{StaticResource Error}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>

<TextBlock x:Name="InputTextBox"
Grid.Column="0"
Margin="4"
Validation.ErrorTemplate="{x:Null}"
Text="{Binding Path=SelectedComboBoxItem, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}">
</TextBlock>

</Border>

<TextBlock Grid.Row="1"
Grid.Column="0"
Padding="2"
Foreground="{StaticResource Error}"
Visibility="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(Validation.HasError), UpdateSourceTrigger=PropertyChanged, Converter={StaticResource BooleanToVisibilityConverter}}"
Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(Validation.Errors)/ErrorContent, UpdateSourceTrigger=PropertyChanged}" />

<Popup
Grid.Row="0"
Grid.Column="0"
StaysOpen="False"
Placement="Bottom"
PlacementTarget="{Binding ElementName=InputTextBox}"
Width="{Binding ActualWidth, ElementName=InputTextBox}"
IsOpen="{TemplateBinding IsComboBoxDropDownOpen}">


<ListBox
Name="ComboBoxListBox"
MaxWidth="{Binding ActualWidth, ElementName=InputTextBox}"
Margin="1"
MaxHeight="100"
BorderThickness="1"
Background="{StaticResource Primary}"
BorderBrush="{StaticResource Accent}"
ItemsSource="{Binding Path=ComboBoxItems, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource TemplatedParent}}"
behaviours:SourceChangedScrollTopBehaviour.ScrollToTop="True">

<b:Interaction.Triggers>
<b:EventTrigger EventName="SelectionChanged">
<triggers:SetPropertyActionTrigger
PropertyName="SelectedComboBoxItem"
TargetObject="{Binding RelativeSource={RelativeSource AncestorType={x:Type comboBox:CustomComboBox}}}"
PropertyValue="{Binding ElementName=ComboBoxListBox, Path=SelectedItem}">
</triggers:SetPropertyActionTrigger>
</b:EventTrigger>
<b:EventTrigger EventName="SelectionChanged">
<triggers:SetPropertyActionTrigger
PropertyName="IsComboBoxDropDownOpen"
TargetObject="{Binding RelativeSource={RelativeSource AncestorType={x:Type comboBox:CustomComboBox}}}"
PropertyValue="{StaticResource False}">
</triggers:SetPropertyActionTrigger>
</b:EventTrigger>
</b:Interaction.Triggers>

<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource ObjectToStringConverter}}" ToolTip="{Binding}"
TextTrimming="CharacterEllipsis" />
</DataTemplate>
</ListBox.ItemTemplate>

<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsVirtualizing="True"
VirtualizationMode="Recycling">
</VirtualizingStackPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>

</ListBox>

</Popup>

</Grid>

</ControlTemplate>
</Setter.Value>
</Setter>

</Style>

</ResourceDictionary>
56 changes: 56 additions & 0 deletions src/TypeCode.Wpf/Controls/ComboBox/CustomComboBox.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using System.Collections;
using System.Windows;
using System.Windows.Controls;

namespace TypeCode.Wpf.Controls.ComboBox;

public class CustomComboBox : Control
{
public CustomComboBox()
{
PreviewMouseLeftButtonUp += (_, _) => IsComboBoxDropDownOpen = true;
MouseLeave += (_, _) => IsComboBoxDropDownOpen = false;
}

public static readonly DependencyProperty IsComboBoxDropDownOpenProperty =
DependencyProperty.Register(
name: nameof(IsComboBoxDropDownOpen),
propertyType: typeof(bool),
ownerType: typeof(CustomComboBox),
typeMetadata: new FrameworkPropertyMetadata(false)
);

public bool IsComboBoxDropDownOpen
{
get => (bool)GetValue(IsComboBoxDropDownOpenProperty);
private set => SetValue(IsComboBoxDropDownOpenProperty, value);
}

public static readonly DependencyProperty ComboBoxItemsProperty =
DependencyProperty.Register(
name: nameof(ComboBoxItems),
propertyType: typeof(IEnumerable),
ownerType: typeof(CustomComboBox),
typeMetadata: new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)
);

public IEnumerable ComboBoxItems
{
get => (IEnumerable)GetValue(ComboBoxItemsProperty);
set => SetValue(ComboBoxItemsProperty, value);
}

public static readonly DependencyProperty SelectedComboBoxItemProperty =
DependencyProperty.Register(
name: nameof(SelectedComboBoxItem),
propertyType: typeof(object),
ownerType: typeof(CustomComboBox),
typeMetadata: new FrameworkPropertyMetadata(null)
);

public object SelectedComboBoxItem
{
get => GetValue(SelectedComboBoxItemProperty);
set => SetValue(SelectedComboBoxItemProperty, value);
}
}
17 changes: 17 additions & 0 deletions src/TypeCode.Wpf/Helper/Converters/ObjectToStringConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.Globalization;
using System.Windows.Data;

namespace TypeCode.Wpf.Helper.Converters;

public class ObjectToStringConverter : IValueConverter
{
public virtual object? Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value.ToString();
}

public virtual object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new Exception();
}
}
48 changes: 48 additions & 0 deletions src/TypeCode.Wpf/Helper/Triggers/PropertySetAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System.Reflection;
using System.Windows;
using Microsoft.Xaml.Behaviors;

namespace TypeCode.Wpf.Helper.Triggers;

public class SetPropertyActionTrigger : TriggerAction<FrameworkElement>
{
public string PropertyName
{
get => (string)GetValue(PropertyNameProperty);
set => SetValue(PropertyNameProperty, value);
}

public static readonly DependencyProperty PropertyNameProperty
= DependencyProperty.Register(nameof(PropertyName), typeof(string),
typeof(SetPropertyActionTrigger));

public object PropertyValue
{
get => GetValue(PropertyValueProperty);
set => SetValue(PropertyValueProperty, value);
}

public static readonly DependencyProperty PropertyValueProperty
= DependencyProperty.Register(nameof(PropertyValue), typeof(object),
typeof(SetPropertyActionTrigger));

public object TargetObject
{
get => GetValue(TargetObjectProperty);
set => SetValue(TargetObjectProperty, value);
}

public static readonly DependencyProperty TargetObjectProperty
= DependencyProperty.Register(nameof(TargetObject), typeof(object),
typeof(SetPropertyActionTrigger));

protected override void Invoke(object parameter)
{
var propertyInfo = TargetObject.GetType().GetProperty(
PropertyName,
BindingFlags.Instance | BindingFlags.Public
| BindingFlags.NonPublic | BindingFlags.InvokeMethod);

propertyInfo?.SetValue(TargetObject, PropertyValue);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
xmlns:outputBox="clr-namespace:TypeCode.Wpf.Components.OutputBox"
xmlns:textBox="clr-namespace:TypeCode.Wpf.Controls.TextBox"
xmlns:encodingConversion="clr-namespace:TypeCode.Wpf.Pages.EncodingConversion"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
xmlns:comboBox="clr-namespace:TypeCode.Wpf.Controls.ComboBox"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
d:DataContext="{d:DesignInstance encodingConversion:EncodingConversionViewModel}">
Expand All @@ -26,7 +26,7 @@
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>

<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
Expand All @@ -53,41 +53,30 @@
</TextBox.InputBindings>
</textBox:CustomTextBox>

<ComboBox Grid.Column="0"
Grid.Row="1"
ItemsSource="{Binding EncodingsFrom, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
SelectedValuePath="Key"
ToolTip="Encoding Source"
HorizontalAlignment="Left"
Width="300"
MaxWidth="300"
SelectedValue="{Binding EncodingFrom, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Margin="0 5 0 0">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Background="Transparent" Text="{Binding Value}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

<ComboBox Grid.Column="0"
Grid.Row="2"
ItemsSource="{Binding EncodingsTo, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
SelectedValuePath="Key"
ToolTip="Encoding Destination"
HorizontalAlignment="Left"
Width="300"
MaxWidth="300"
SelectedValue="{Binding EncodingTo, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Margin="0 5 0 0">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Background="Transparent" Text="{Binding Value}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<comboBox:CustomComboBox Grid.Column="0"
Grid.Row="1"
ComboBoxItems="{Binding EncodingsFrom, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
ToolTip="Encoding Source"
HorizontalAlignment="Left"
Width="300"
MaxWidth="300"
SelectedComboBoxItem="{Binding EncodingFrom, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Margin="0 5 0 0">
</comboBox:CustomComboBox>

<comboBox:CustomComboBox Grid.Column="0"
Grid.Row="2"
ComboBoxItems="{Binding EncodingsTo, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
ToolTip="Encoding Destination"
HorizontalAlignment="Left"
Width="300"
MaxWidth="300"
SelectedComboBoxItem="{Binding EncodingTo, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Margin="0 5 0 0">
</comboBox:CustomComboBox>

<Button Margin="5,0,0,0" VerticalAlignment="Top" Grid.Row="0" Grid.Column="1" Command="{Binding ConvertCommand}"
<Button Margin="5,0,0,0" VerticalAlignment="Top" Grid.Row="0" Grid.Column="1"
Command="{Binding ConvertCommand}"
Content="Convert" />
</Grid>

Expand Down
Loading

0 comments on commit 4f9f0b1

Please sign in to comment.