Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
9 changes: 9 additions & 0 deletions MainDemo.Wpf/Domain/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,15 @@ private static IEnumerable<DemoItem> GenerateDemoItems(ISnackbarMessageQueue sna
DocumentationLink.StyleLink("Shadows"),
DocumentationLink.SpecsLink("https://material.io/design/environment/elevation.html", "Elevation")
});

yield return new DemoItem(
"Smart Hint",
typeof(SmartHint),
new[]
{
DocumentationLink.DemoPageLink<SmartHint>(),
DocumentationLink.StyleLink("SmartHint"),
});
}

private bool DemoItemsFilter(object obj)
Expand Down
80 changes: 80 additions & 0 deletions MainDemo.Wpf/Domain/SmartHintViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using MaterialDesignThemes.Wpf;

namespace MaterialDesignDemo.Domain;

internal class SmartHintViewModel : ViewModelBase
{
private FloatingHintHorizontalAlignment _selectedAlignment = FloatingHintHorizontalAlignment.Inherit;
private double _selectedFloatingScale = 0.75;
private bool _showClearButton = true;
private bool _showLeadingIcon = true;
private string _hintText = "Hint text";
private Point _selectedFloatingOffset = new (0, -16);

public IEnumerable<FloatingHintHorizontalAlignment> HorizontalAlignmentOptions { get; } = Enum.GetValues(typeof(FloatingHintHorizontalAlignment)).OfType<FloatingHintHorizontalAlignment>();
public IEnumerable<double> FloatingScaleOptions { get; } = new[] {0.25, 0.5, 0.75, 1.0};

public IEnumerable<Point> FloatingOffsetOptions { get; } = new[] { new Point(0, -16), new Point(0, 16), new Point(16, 16), new Point(-16, -16) };

public IEnumerable<string> ComboBoxOptions { get; } = new[] {"Option 1", "Option 2", "Option 3"};

public FloatingHintHorizontalAlignment SelectedAlignment
{
get => _selectedAlignment;
set
{
_selectedAlignment = value;
OnPropertyChanged();
}
}

public double SelectedFloatingScale
{
get => _selectedFloatingScale;
set
{
_selectedFloatingScale = value;
OnPropertyChanged();
}
}

public Point SelectedFloatingOffset
{
get => _selectedFloatingOffset;
set
{
_selectedFloatingOffset = value;
OnPropertyChanged();
}
}

public bool ShowClearButton
{
get => _showClearButton;
set
{
_showClearButton = value;
OnPropertyChanged();
}
}

public bool ShowLeadingIcon
{
get => _showLeadingIcon;
set
{
_showLeadingIcon = value;
OnPropertyChanged();
}
}

public string HintText
{
get => _hintText;
set
{
_hintText = value;
OnPropertyChanged();
}
}
}
491 changes: 491 additions & 0 deletions MainDemo.Wpf/SmartHint.xaml

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions MainDemo.Wpf/SmartHint.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using MaterialDesignDemo.Domain;

namespace MaterialDesignDemo;

/// <summary>
/// Interaction logic for SmartHint.xaml
/// </summary>
public partial class SmartHint : UserControl
{
public SmartHint()
{
DataContext = new SmartHintViewModel();
InitializeComponent();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;

namespace MaterialDesignThemes.Wpf.Converters;

internal class FloatingHintTextBlockMarginConverter : IMultiValueConverter
{
public object? Convert(object?[]? values, Type targetType, object? parameter, CultureInfo culture)
{
if (values?.Length != 7 || values.Any(v => v == null)
|| values[0] is not FloatingHintHorizontalAlignment floatingAlignment
|| values[1] is not HorizontalAlignment restingAlignment
|| !double.TryParse(values[2]!.ToString(), out double desiredWidth)
|| !double.TryParse(values[3]!.ToString(), out double availableWidth)
|| !double.TryParse(values[4]!.ToString(), out double scale)
|| !double.TryParse(values[5]!.ToString(), out double lower)
|| !double.TryParse(values[6]!.ToString(), out double upper))
{
return Transform.Identity;
}

double scaleMultiplier = upper + (lower - upper) * scale;

HorizontalAlignment alignment = restingAlignment;
if (scale != 0)
{
switch (floatingAlignment)
{
case FloatingHintHorizontalAlignment.Inherit:
alignment = restingAlignment;
break;
case FloatingHintHorizontalAlignment.Left:
alignment = HorizontalAlignment.Left;
break;
case FloatingHintHorizontalAlignment.Center:
alignment = HorizontalAlignment.Center;
break;
case FloatingHintHorizontalAlignment.Right:
alignment = HorizontalAlignment.Right;
break;
case FloatingHintHorizontalAlignment.Stretch:
alignment = HorizontalAlignment.Stretch;
break;
default:
throw new ArgumentOutOfRangeException();
}
}
switch (alignment)
{
case HorizontalAlignment.Right:
return FloatRight();
case HorizontalAlignment.Center:
return FloatCenter();
default:
return FloatLeft();
}

Thickness FloatLeft()
{
if (restingAlignment == HorizontalAlignment.Center)
{
// Animate from center to left
double offset = Math.Max(0, (availableWidth - desiredWidth) / 2);
return new Thickness(offset - offset * scale, 0, 0, 0);
}
if (restingAlignment == HorizontalAlignment.Right)
{
// Animate from right to left
double offset = Math.Max(0, availableWidth - desiredWidth);
return new Thickness(offset - offset * scale, 0, 0, 0);
}
return new Thickness(0);
}

Thickness FloatCenter()
{
if (restingAlignment == HorizontalAlignment.Left || restingAlignment == HorizontalAlignment.Stretch)
{
// Animate from left to center
double offset = Math.Max(0, (availableWidth - desiredWidth * scaleMultiplier) / 2);
return new Thickness(offset * scale, 0, 0, 0);
}
if (restingAlignment == HorizontalAlignment.Right)
{
// Animate from right to center
double startOffset = Math.Max(0, availableWidth - desiredWidth);
double endOffset = Math.Max(0, (availableWidth - desiredWidth) / 2);
double endOffsetDelta = startOffset - endOffset;
return new Thickness(endOffset + endOffsetDelta * (1 - scale), 0, 0, 0);
}
return new Thickness(Math.Max(0, availableWidth - desiredWidth * scaleMultiplier) / 2, 0, 0, 0);
}

Thickness FloatRight()
{
if (restingAlignment == HorizontalAlignment.Left || restingAlignment == HorizontalAlignment.Stretch)
{
// Animate from left to right
double offset = Math.Max(0, availableWidth - desiredWidth * scaleMultiplier);
return new Thickness(offset * scale, 0, 0, 0);
}
if (restingAlignment == HorizontalAlignment.Center)
{
// Animate from center to right
double startOffset = Math.Max(0, (availableWidth - desiredWidth) / 2);
double endOffsetDelta = Math.Max(0, availableWidth - desiredWidth * scaleMultiplier) - startOffset;
return new Thickness(startOffset + endOffsetDelta * scale, 0, 0, 0);
}
return new Thickness(Math.Max(0, availableWidth - desiredWidth * scaleMultiplier), 0, 0, 0);
}
}

public object?[]? ConvertBack(object? value, Type[] targetTypes, object? parameter, CultureInfo culture)
=> throw new NotImplementedException();
}
10 changes: 10 additions & 0 deletions MaterialDesignThemes.Wpf/FloatingHintHorizontalAlignment.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace MaterialDesignThemes.Wpf;

public enum FloatingHintHorizontalAlignment
{
Inherit,
Left,
Center,
Right,
Stretch
}
11 changes: 11 additions & 0 deletions MaterialDesignThemes.Wpf/HintAssist.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,17 @@ public static void SetHintOpacity(DependencyObject element, double value)
=> element.SetValue(HintOpacityProperty, value);
#endregion

#region AttachedProperty : FloatingHintHorizontalAlignment
public static readonly DependencyProperty FloatingHintHorizontalAlignmentProperty
= DependencyProperty.RegisterAttached("FloatingHintHorizontalAlignment", typeof(FloatingHintHorizontalAlignment), typeof(HintAssist),
new FrameworkPropertyMetadata(FloatingHintHorizontalAlignment.Inherit, FrameworkPropertyMetadataOptions.Inherits));

public static void SetFloatingHintHorizontalAlignment(DependencyObject element, FloatingHintHorizontalAlignment value)
=> element.SetValue(FloatingHintHorizontalAlignmentProperty, value);
public static FloatingHintHorizontalAlignment GetFloatingHintHorizontalAlignment(DependencyObject element)
=> (FloatingHintHorizontalAlignment) element.GetValue(FloatingHintHorizontalAlignmentProperty);
#endregion

#region AttachedProperty : HintFontFamilyProperty
public static readonly DependencyProperty FontFamilyProperty
= DependencyProperty.RegisterAttached("FontFamily", typeof(FontFamily), typeof(HintAssist),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@
Grid.Column="0"
Padding="{TemplateBinding Padding}">
<Grid x:Name="InnerRoot"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
HorizontalAlignment="Stretch"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
UseLayoutRounding="{TemplateBinding UseLayoutRounding}">
Expand All @@ -353,7 +353,7 @@
<TextBox x:Name="PART_EditableTextBox"
Grid.Column="1"
MinWidth="10"
HorizontalAlignment="Stretch"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
HorizontalContentAlignment="Stretch"
CaretBrush="{TemplateBinding Foreground}"
IsReadOnly="{TemplateBinding IsReadOnly}"
Expand All @@ -372,7 +372,8 @@
HintProxy="{Binding RelativeSource={RelativeSource TemplatedParent}, Converter={x:Static converters:HintProxyFabricConverter.Instance}}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
UseFloating="{TemplateBinding wpf:HintAssist.IsFloating}"
UseLayoutRounding="{TemplateBinding UseLayoutRounding}">
UseLayoutRounding="{TemplateBinding UseLayoutRounding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}">
<wpf:SmartHint.Hint>
<Border x:Name="HintBackgroundBorder"
Background="{TemplateBinding wpf:HintAssist.Background}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,14 +131,15 @@
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding wpf:TextFieldAssist.TextFieldCornerRadius}"
SnapsToDevicePixels="True">
<Grid HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid x:Name="grid"
MinWidth="1"
VerticalAlignment="Center">
VerticalAlignment="Center"
HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
Expand All @@ -155,6 +156,7 @@
wpf:ScrollViewerAssist.IgnorePadding="True"
Cursor="{TemplateBinding Cursor, Converter={StaticResource IBeamCursorConverter}}"
Focusable="false"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
HorizontalScrollBarVisibility="Hidden"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
UseLayoutRounding="{TemplateBinding UseLayoutRounding}"
Expand All @@ -168,7 +170,8 @@
FontSize="{TemplateBinding FontSize}"
HintOpacity="{TemplateBinding wpf:HintAssist.HintOpacity}"
HintProxy="{Binding RelativeSource={RelativeSource TemplatedParent}, Converter={x:Static converters:HintProxyFabricConverter.Instance}}"
UseFloating="{TemplateBinding wpf:HintAssist.IsFloating}">
UseFloating="{TemplateBinding wpf:HintAssist.IsFloating}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}">
<wpf:SmartHint.Hint>
<Border x:Name="HintBackgroundBorder"
Background="{TemplateBinding wpf:HintAssist.Background}"
Expand Down Expand Up @@ -595,15 +598,16 @@
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding wpf:TextFieldAssist.TextFieldCornerRadius}"
SnapsToDevicePixels="True">
<Grid HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid x:Name="grid"
MinWidth="1"
VerticalAlignment="Center">
VerticalAlignment="Center"
HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
Expand All @@ -618,6 +622,7 @@
<Grid x:Name="ContentGrid"
Grid.Column="1"
Panel.ZIndex="1"
HorizontalAlignment="Stretch"
wpf:PasswordBoxAssist.IsPasswordRevealed="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(wpf:PasswordBoxAssist.IsPasswordRevealed)}">

<ScrollViewer x:Name="PART_ContentHost"
Expand All @@ -628,6 +633,7 @@
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
UseLayoutRounding="{TemplateBinding UseLayoutRounding}"
VerticalScrollBarVisibility="Hidden"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Visibility="{Binding ElementName=ContentGrid, Path=(wpf:PasswordBoxAssist.IsPasswordRevealed), Converter={StaticResource InverseBooleanToVisibilityConverter}}" />
<TextBox x:Name="RevealPasswordTextBox"
Grid.Column="0"
Expand All @@ -639,6 +645,7 @@
SelectionBrush="{TemplateBinding SelectionBrush}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Style="{StaticResource MaterialDesignRawTextBox}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(wpf:PasswordBoxAssist.Password), UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
UseLayoutRounding="{TemplateBinding UseLayoutRounding}"
Visibility="{Binding ElementName=ContentGrid, Path=(wpf:PasswordBoxAssist.IsPasswordRevealed), Converter={StaticResource BooleanToVisibilityConverter}}">
Expand All @@ -657,7 +664,8 @@
FontSize="{TemplateBinding FontSize}"
HintOpacity="{TemplateBinding wpf:HintAssist.HintOpacity}"
HintProxy="{Binding RelativeSource={RelativeSource TemplatedParent}, Converter={x:Static converters:HintProxyFabricConverter.Instance}}"
UseFloating="{TemplateBinding wpf:HintAssist.IsFloating}">
UseFloating="{TemplateBinding wpf:HintAssist.IsFloating}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}">
<wpf:SmartHint.Hint>
<Border x:Name="HintBackgroundBorder"
Background="{TemplateBinding wpf:HintAssist.Background}"
Expand Down
Loading