Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 62 additions & 57 deletions MahApps.Metro/Controls/Helper/TextBoxHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ public class TextBoxHelper

private static readonly DependencyProperty IsSpellCheckContextMenuEnabledProperty = DependencyProperty.RegisterAttached("IsSpellCheckContextMenuEnabled", typeof(bool), typeof(TextBoxHelper), new FrameworkPropertyMetadata(false, UseSpellCheckContextMenuChanged));

private static readonly Func<NumericUpDown, int> NumericUpDownTextLength = control => control.Value.HasValue ? 1 : 0;
private static readonly Func<PasswordBox, int> PasswordBoxTextLength = control => control.Password.Length;
private static readonly Func<TextBox, int> TextBoxTextLength = control => control.Text.Length;

private static readonly Func<NumericUpDown, Action> NumericUpDownFocus = control => control.SelectAll;
private static readonly Func<PasswordBox, Action> PasswordBoxFocus = control => control.SelectAll;
private static readonly Func<TextBox, Action> TextBoxFocus = control => control.SelectAll;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xxMUROxx do we really need this static stuff? cause every func is only used once...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@punker76 we could also inline this fields. But what do you think of both generic methods? Should simplify further development if we add UseFloatingWatermark for other controls.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But what do you think of both generic methods? Should simplify further development if we add UseFloatingWatermark for other controls.

👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@punker76 done

/// <summary>
/// Indicates if a TextBox or RichTextBox should use SpellCheck context menu
/// </summary>
Expand Down Expand Up @@ -188,12 +196,6 @@ public static void SetUseFloatingWatermark(DependencyObject obj, bool value)
obj.SetValue(UseFloatingWatermarkProperty, value);
}

private static void SetTextLength(DependencyObject obj, int value)
{
obj.SetValue(TextLengthProperty, value);
obj.SetValue(HasTextProperty, value >= 1);
}

/// <summary>
/// Gets if the attached TextBox has text.
/// </summary>
Expand All @@ -207,7 +209,7 @@ public static void SetHasText(DependencyObject obj, bool value)
obj.SetValue(HasTextProperty, value);
}

static void OnIsMonitoringChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
private static void OnIsMonitoringChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TextBox)
{
Expand Down Expand Up @@ -250,44 +252,67 @@ static void OnIsMonitoringChanged(DependencyObject d, DependencyPropertyChangedE
{
var numericUpDown = d as NumericUpDown;
numericUpDown.SelectAllOnFocus = (bool)e.NewValue;
if ((bool)e.NewValue)
{
numericUpDown.ValueChanged += OnNumericUpDownValueChaged;
numericUpDown.GotFocus += NumericUpDownGotFocus;
}
else
{
numericUpDown.ValueChanged += OnNumericUpDownValueChaged;
numericUpDown.GotFocus += NumericUpDownGotFocus;
}
}
}

static void TextChanged(object sender, TextChangedEventArgs e)
private static void OnNumericUpDownValueChaged(object sender, RoutedEventArgs e)
{
var txtBox = sender as TextBox;
if (txtBox == null)
return;
SetTextLength(txtBox, txtBox.Text.Length);
SetTextLength(sender as NumericUpDown, NumericUpDownTextLength);
}

static void PasswordChanged(object sender, RoutedEventArgs e)
private static void SetTextLength<TDependencyObject>(TDependencyObject sender, Func<TDependencyObject, int> funcTextLength) where TDependencyObject : DependencyObject
{
var passBox = sender as PasswordBox;
if (passBox == null)
return;
SetTextLength(passBox, passBox.Password.Length);
if (sender != null)
{
var value = funcTextLength(sender);
sender.SetValue(TextLengthProperty, value);
sender.SetValue(HasTextProperty, value >= 1);
}
}

static void TextBoxGotFocus(object sender, RoutedEventArgs e)
private static void TextChanged(object sender, RoutedEventArgs e)
{
var txtBox = sender as TextBox;
if (txtBox == null)
return;
if (GetSelectAllOnFocus(txtBox))
{
txtBox.Dispatcher.BeginInvoke((Action)(txtBox.SelectAll));
}
SetTextLength(sender as TextBox, TextBoxTextLength);
}

static void PasswordGotFocus(object sender, RoutedEventArgs e)
private static void PasswordChanged(object sender, RoutedEventArgs e)
{
var passBox = sender as PasswordBox;
if (passBox == null)
return;
if (GetSelectAllOnFocus(passBox))
SetTextLength(sender as PasswordBox, PasswordBoxTextLength);
}

private static void TextBoxGotFocus(object sender, RoutedEventArgs e)
{
ControlGotFocus(sender as TextBox, TextBoxFocus);
}

private static void NumericUpDownGotFocus(object sender, RoutedEventArgs e)
{
ControlGotFocus(sender as NumericUpDown, NumericUpDownFocus);
}

private static void PasswordGotFocus(object sender, RoutedEventArgs e)
{
ControlGotFocus(sender as PasswordBox, PasswordBoxFocus);
}

private static void ControlGotFocus<TDependencyObject>(TDependencyObject sender, Func<TDependencyObject, Action> funcSelectAll) where TDependencyObject : DependencyObject
{
if (sender != null)
{
passBox.Dispatcher.BeginInvoke((Action)(passBox.SelectAll));
if (GetSelectAllOnFocus(sender))
{
sender.Dispatcher.BeginInvoke(funcSelectAll, sender);
}
}
}

Expand Down Expand Up @@ -335,8 +360,6 @@ public static void SetIsClearTextButtonBehaviorEnabled(Button obj, bool value)
obj.SetValue(IsClearTextButtonBehaviorEnabledProperty, value);
}



public static ICommand GetButtonCommand(DependencyObject d)
{
return (ICommand)d.GetValue(ButtonCommandProperty);
Expand Down Expand Up @@ -444,22 +467,22 @@ private static void ButtonCommandOrClearTextChanged(DependencyObject d, Dependen
if (textbox != null)
{
// only one loaded event
textbox.Loaded -= TextBoxLoaded;
textbox.Loaded += TextBoxLoaded;
textbox.Loaded -= TextChanged;
textbox.Loaded += TextChanged;
if (textbox.IsLoaded)
{
TextBoxLoaded(textbox, new RoutedEventArgs());
TextChanged(textbox, new RoutedEventArgs());
}
}
var passbox = d as PasswordBox;
if (passbox != null)
{
// only one loaded event
passbox.Loaded -= PassBoxLoaded;
passbox.Loaded += PassBoxLoaded;
passbox.Loaded -= PasswordChanged;
passbox.Loaded += PasswordChanged;
if (passbox.IsLoaded)
{
PassBoxLoaded(passbox, new RoutedEventArgs());
PasswordChanged(passbox, new RoutedEventArgs());
}
}
var combobox = d as ComboBox;
Expand All @@ -483,23 +506,5 @@ static void ComboBoxLoaded(object sender, RoutedEventArgs e)
comboBox.SetValue(HasTextProperty, !string.IsNullOrWhiteSpace(comboBox.Text) || comboBox.SelectedItem != null);
}
}

static void PassBoxLoaded(object sender, RoutedEventArgs e)
{
var passbox = sender as PasswordBox;
if (passbox != null)
{
passbox.SetValue(HasTextProperty, !string.IsNullOrWhiteSpace(passbox.Password));
}
}

static void TextBoxLoaded(object sender, RoutedEventArgs e)
{
var textbox = sender as TextBox;
if (textbox != null)
{
textbox.SetValue(HasTextProperty, !string.IsNullOrWhiteSpace(textbox.Text));
}
}
}
}
44 changes: 37 additions & 7 deletions MahApps.Metro/Controls/NumericUpDown.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace MahApps.Metro.Controls
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
Expand Down Expand Up @@ -157,7 +158,9 @@ private static void InterceptManualEnterChangedCallback(DependencyObject depende
typeof(bool),
typeof(NumericUpDown),
new FrameworkPropertyMetadata(true, OnHasDecimalsChanged));


private static readonly Regex RegexStringFormatHexadecimal = new Regex(@"^(?<complexHEX>.*{\d:X\d+}.*)?(?<simpleHEX>X\d+)?$", RegexOptions.Compiled);

private const double DefaultInterval = 1d;
private const int DefaultDelay = 500;
private const string ElementNumericDown = "PART_NumericDown";
Expand Down Expand Up @@ -855,6 +858,7 @@ private static void OnMaximumChanged(DependencyObject d, DependencyPropertyChang
var numericUpDown = (NumericUpDown)d;

numericUpDown.CoerceValue(ValueProperty);
numericUpDown.Value = (double?)CoerceValue(numericUpDown, numericUpDown.Value);
numericUpDown.OnMaximumChanged((double)e.OldValue, (double)e.NewValue);
numericUpDown.EnableDisableUpDown();
}
Expand All @@ -865,6 +869,7 @@ private static void OnMinimumChanged(DependencyObject d, DependencyPropertyChang

numericUpDown.CoerceValue(ValueProperty);
numericUpDown.CoerceValue(MaximumProperty);
numericUpDown.Value = (double?)CoerceValue(numericUpDown, numericUpDown.Value);
numericUpDown.OnMinimumChanged((double)e.OldValue, (double)e.NewValue);
numericUpDown.EnableDisableUpDown();
}
Expand All @@ -886,6 +891,7 @@ private static void OnStringFormatChanged(DependencyObject d, DependencyProperty
{
nud.InternalSetText(nud.Value);
}
nud.HasDecimals = !RegexStringFormatHexadecimal.IsMatch((string)e.NewValue);
}

private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
Expand Down Expand Up @@ -924,14 +930,9 @@ private void InternalSetText(double? newValue)
{
_valueTextBox.Text = newValue.Value.ToString(culture);
}
else if (!StringFormat.Contains("{"))
{
// then we may have a StringFormat of e.g. "N0"
_valueTextBox.Text = newValue.Value.ToString(StringFormat, culture);
}
else
{
_valueTextBox.Text = string.Format(culture, StringFormat, newValue.Value);
FormatValue(newValue, culture);
}

if ((bool)GetValue(TextBoxHelper.IsMonitoringProperty))
Expand All @@ -940,6 +941,35 @@ private void InternalSetText(double? newValue)
}
}

private void FormatValue(double? newValue, CultureInfo culture)
{
var match = RegexStringFormatHexadecimal.Match(StringFormat);
if (match.Success)
{
if (match.Groups["simpleHEX"].Success)
{
// HEX DOES SUPPORT INT ONLY.
_valueTextBox.Text = ((int)newValue.Value).ToString(match.Groups["simpleHEX"].Value, culture);
}
else if (match.Groups["complexHEX"].Success)
{
_valueTextBox.Text = string.Format(culture, match.Groups["complexHEX"].Value, (int)newValue.Value);
}
}
else
{
if (!StringFormat.Contains("{"))
{
// then we may have a StringFormat of e.g. "N0"
_valueTextBox.Text = newValue.Value.ToString(StringFormat, culture);
}
else
{
_valueTextBox.Text = string.Format(culture, StringFormat, newValue.Value);
}
}
}

private ScrollViewer TryFindScrollViewer()
{
_valueTextBox.ApplyTemplate();
Expand Down
66 changes: 64 additions & 2 deletions MahApps.Metro/Themes/NumericUpDown.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
xmlns:converters="clr-namespace:MahApps.Metro.Converters">

<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/VS/Colors.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Shared.xaml" />
</ResourceDictionary.MergedDictionaries>

<converters:ThicknessToDoubleConverter x:Key="ThicknessToDoubleConverter" />
Expand Down Expand Up @@ -68,8 +68,31 @@
<ColumnDefinition x:Name="PART_NumericDownColumn"
Width="Auto" />
</Grid.ColumnDefinitions>

<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid x:Name="PART_FloatingMessageContainer"
Grid.Column="0"
Height="0"
IsHitTestVisible="False"
Margin="5,0"
Visibility="Visible">
<TextBlock x:Name="PART_FloatingMessage"
Text="{TemplateBinding Controls:TextBoxHelper.Watermark}"
FontSize="{DynamicResource FloatingWatermarkFontSize}"
Foreground="{TemplateBinding Foreground}"
Opacity="0.6"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
<TextBlock.RenderTransform>
<TranslateTransform />
</TextBlock.RenderTransform>
</TextBlock>
</Grid>
<TextBox x:Name="PART_TextBox"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xxMUROxx can you change the floating message part like in 1919458 ?
thx

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@punker76 do you mean removing PART_FloatingMessage

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xxMUROxx no, i mean changeing Grid -> ContentControl and the tirgger part

1919458#diff-e09aa3d83a4c174f3c72c7698ff3c40eR124

1919458#diff-e09aa3d83a4c174f3c72c7698ff3c40eR193

Grid.Row="1"
Grid.Column="0"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Controls:ControlsHelper.DisabledVisualElementVisibility="Collapsed"
Expand All @@ -94,6 +117,7 @@
Controls:TextBoxHelper.ButtonsAlignment="{TemplateBinding ButtonsAlignment}" />
<RepeatButton x:Name="PART_NumericUp"
Grid.Column="1"
Grid.RowSpan="2"
Margin="2,2,0,2"
Delay="{TemplateBinding Delay}"
Foreground="{TemplateBinding Foreground}"
Expand All @@ -109,6 +133,7 @@
</RepeatButton>
<RepeatButton x:Name="PART_NumericDown"
Grid.Column="2"
Grid.RowSpan="2"
Margin="0,2,2,2"
VerticalContentAlignment="Center"
Delay="{TemplateBinding Delay}"
Expand All @@ -134,6 +159,36 @@
Opacity="0" />
</Grid>
<ControlTemplate.Triggers>
<!--Sets the MiniMessage visibility (Watermark must not be "" and FloatWatermark must be true)-->
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=(Controls:TextBoxHelper.HasText)}"
Value="False">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource HideFloatingMessageStoryboard}" />
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource ShowFloatingMessageStoryboard}" />
</DataTrigger.ExitActions>
</DataTrigger>
<!--To override Watermark == ""-->
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=(Controls:TextBoxHelper.Watermark)}"
Value="">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource HideFloatingMessageStoryboard}" />
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource ShowFloatingMessageStoryboard}" />
</DataTrigger.ExitActions>
</DataTrigger>
<!--To override TextBoxHelper.UseFloatingWatermark == false-->
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=(Controls:TextBoxHelper.UseFloatingWatermark)}"
Value="False">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource HideFloatingMessageStoryboard}" />
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource ShowFloatingMessageStoryboard}" />
</DataTrigger.ExitActions>
</DataTrigger>
<Trigger Property="ButtonsAlignment"
Value="Left">
<Setter TargetName="PART_TextBox"
Expand Down Expand Up @@ -234,7 +289,14 @@
<Setter TargetName="Base"
Property="BorderBrush"
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(Controls:ControlsHelper.FocusBorderBrush)}" />
<Setter TargetName="PART_FloatingMessage"
Property="Foreground"
Value="{DynamicResource AccentColorBrush}" />
<Setter TargetName="PART_FloatingMessage"
Property="Opacity"
Value="1" />
</Trigger>


<Trigger Property="HideUpDownButtons"
Value="True">
Expand Down
Loading