Permalink
Browse files

Adopt SpellCheck.IsEnabled changes from MaterialDesignInXamlToolkit @…

…ButchersBoy #650

  Removes the extra hidden classes and enables flexible style changes, it's more WPF now
  • Loading branch information...
punker76 committed Apr 19, 2017
1 parent c70f39e commit 79744525ee288f18e2d4086b245a431b2c764d48
@@ -34,7 +34,7 @@
Margin="{StaticResource ColumnMargin}">
<Label Content="TextBox" Style="{DynamicResource DescriptionHeaderStyle}" />
<TextBox Margin="{StaticResource ControlMargin}"
Controls:TextBoxHelper.IsSpellCheckContextMenuEnabled="True"
SpellCheck.IsEnabled="True"
Text="Enabled">
<TextBox.ContextMenu>
<ContextMenu>
@@ -44,10 +44,10 @@
</TextBox>
<TextBox Margin="{StaticResource ControlMargin}"
Controls:TextBoxHelper.ClearTextButton="True"
Controls:TextBoxHelper.IsSpellCheckContextMenuEnabled="True"
Controls:TextBoxHelper.IsWaitingForData="True"
Controls:TextBoxHelper.UseFloatingWatermark="True"
Controls:TextBoxHelper.Watermark="Watermark"
SpellCheck.IsEnabled="True"
ToolTip="Default alignment">
<TextBox.ContextMenu>
<ContextMenu>
@@ -143,8 +143,8 @@
<Label Content="RichTextBox" Style="{DynamicResource DescriptionHeaderStyle}" />
<RichTextBox Margin="{StaticResource ControlMargin}"
Padding="5"
Controls:TextBoxHelper.IsSpellCheckContextMenuEnabled="True"
IsDocumentEnabled="True">
IsDocumentEnabled="True"
SpellCheck.IsEnabled="True">
<FlowDocument>
<Paragraph>
<Hyperlink NavigateUri="https://github.com/MahApps/MahApps.Metro">
@@ -15,20 +15,20 @@

namespace MahApps.Metro.Controls
{
public interface ISpellCheckMenuItem
internal enum SpellingResourceKeyId
{
SuggestionMenuItemStyle,
IgnoreAllMenuItemStyle,
NoSuggestionsMenuItemStyle,
SeparatorStyle,
}

public class SpellCheckMenuItem : MenuItem, ISpellCheckMenuItem
{
public SpellCheckMenuItem()
{
this.SetResourceReference(FrameworkElement.StyleProperty, "MetroMenuItem");
}
}

public class SpellCheckSeparator : Separator, ISpellCheckMenuItem
public static class Spelling
{
public static ResourceKey SuggestionMenuItemStyleKey { get; } = new ComponentResourceKey(typeof(Spelling), SpellingResourceKeyId.SuggestionMenuItemStyle);
public static ResourceKey IgnoreAllMenuItemStyleKey { get; } = new ComponentResourceKey(typeof(Spelling), SpellingResourceKeyId.IgnoreAllMenuItemStyle);
public static ResourceKey NoSuggestionsMenuItemStyleKey { get; } = new ComponentResourceKey(typeof(Spelling), SpellingResourceKeyId.NoSuggestionsMenuItemStyle);
public static ResourceKey SeparatorStyleKey { get; } = new ComponentResourceKey(typeof(Spelling), SpellingResourceKeyId.SeparatorStyle);
}

/// <summary>
@@ -69,7 +69,7 @@ public class TextBoxHelper

public static readonly DependencyProperty HasTextProperty = DependencyProperty.RegisterAttached("HasText", typeof (bool), typeof (TextBoxHelper), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsRender));

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

/// <summary>
/// This property can be used to retrieve the watermark using the <see cref="DisplayAttribute"/> of bound property.
@@ -252,7 +252,7 @@ private static Type ResolveBinding(Type type, string[] paths)
}
#endif

private static void UseSpellCheckContextMenuChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
private static void IsSpellCheckContextMenuEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var tb = d as TextBoxBase;
if (null == tb)
@@ -262,16 +262,15 @@ private static void UseSpellCheckContextMenuChanged(DependencyObject d, Dependen

if (e.OldValue != e.NewValue)
{
tb.SetValue(SpellCheck.IsEnabledProperty, (bool)e.NewValue);
if ((bool)e.NewValue)
{
tb.SetValue(SpellCheck.IsEnabledProperty, true);
tb.ContextMenuOpening += TextBoxBaseContextMenuOpening;
tb.LostFocus += TextBoxBaseLostFocus;
tb.ContextMenuClosing += TextBoxBaseContextMenuClosing;
}
else
{
tb.SetValue(SpellCheck.IsEnabledProperty, false);
tb.ContextMenuOpening -= TextBoxBaseContextMenuOpening;
tb.LostFocus -= TextBoxBaseLostFocus;
tb.ContextMenuClosing -= TextBoxBaseContextMenuClosing;
@@ -281,70 +280,99 @@ private static void UseSpellCheckContextMenuChanged(DependencyObject d, Dependen

private static void TextBoxBaseLostFocus(object sender, RoutedEventArgs e)
{
RemoveSpellCheckMenuItems((FrameworkElement)sender);
RemoveSpellCheckMenuItems(((FrameworkElement)sender).ContextMenu);
}

private static void TextBoxBaseContextMenuClosing(object sender, ContextMenuEventArgs e)
{
RemoveSpellCheckMenuItems((FrameworkElement)sender);
RemoveSpellCheckMenuItems(((FrameworkElement)sender).ContextMenu);
}

private static void TextBoxBaseContextMenuOpening(object sender, ContextMenuEventArgs e)
{
var tbBase = (TextBoxBase)sender;
var textBox = tbBase as TextBox;
var richTextBox = tbBase as RichTextBox;
var contextMenu = tbBase.ContextMenu;
if (contextMenu == null)
{
return;
}

RemoveSpellCheckMenuItems((FrameworkElement)sender);
RemoveSpellCheckMenuItems(contextMenu);

// the default item comes normally through the styles, so I think we don't need to do this
/*if (tbBase.ContextMenu == null)
if (!SpellCheck.GetIsEnabled(tbBase))
{
tbBase.ContextMenu = GetDefaultTextBoxBaseContextMenu();
}*/
return;
}

var cmdIndex = 0;
var textBox = tbBase as TextBox;
var richTextBox = tbBase as RichTextBox;
var spellingError = textBox != null
? textBox.GetSpellingError(textBox.CaretIndex)
: richTextBox?.GetSpellingError(richTextBox.CaretPosition);
if (spellingError != null) {
if (spellingError != null)
{
var spellingSuggestionStyle = contextMenu.TryFindResource(Spelling.SuggestionMenuItemStyleKey) as Style;
var suggestions = spellingError.Suggestions.ToList();
if (suggestions.Any()) {
foreach (var suggestion in suggestions) {
var mi = new SpellCheckMenuItem();
mi.Header = suggestion;
mi.FontWeight = FontWeights.Bold;
mi.Command = EditingCommands.CorrectSpellingError;
mi.CommandParameter = suggestion;
mi.CommandTarget = tbBase;
tbBase.ContextMenu.Items.Insert(cmdIndex, mi);
cmdIndex++;
if (suggestions.Any())
{
foreach (var suggestion in suggestions)
{
var mi = new MenuItem
{
Command = EditingCommands.CorrectSpellingError,
CommandParameter = suggestion,
CommandTarget = tbBase,
Style = spellingSuggestionStyle,
Tag = typeof(Spelling)
};
contextMenu.Items.Insert(cmdIndex++, mi);
}
// add a separator
tbBase.ContextMenu.Items.Insert(cmdIndex, new SpellCheckSeparator());
cmdIndex++;
}
var ignoreAllMI = new SpellCheckMenuItem();
ignoreAllMI.Header = "Ignore All";
ignoreAllMI.Command = EditingCommands.IgnoreSpellingError;
ignoreAllMI.CommandTarget = tbBase;
tbBase.ContextMenu.Items.Insert(cmdIndex, ignoreAllMI);
cmdIndex++;
}
else
{
contextMenu.Items.Insert(cmdIndex++, new MenuItem
{
Style = contextMenu.TryFindResource(Spelling.NoSuggestionsMenuItemStyleKey) as Style,
Tag = typeof(Spelling)
});
}

// add a separator
contextMenu.Items.Insert(cmdIndex++, new Separator
{
Style = contextMenu.TryFindResource(Spelling.SeparatorStyleKey) as Style,
Tag = typeof(Spelling)
});

// ignore all
var ignoreAllMI = new MenuItem
{
Command = EditingCommands.IgnoreSpellingError,
CommandTarget = tbBase,
Style = contextMenu.TryFindResource(Spelling.IgnoreAllMenuItemStyleKey) as Style,
Tag = typeof(Spelling)
};
contextMenu.Items.Insert(cmdIndex++, ignoreAllMI);

// add another separator
tbBase.ContextMenu.Items.Insert(cmdIndex, new SpellCheckSeparator());
contextMenu.Items.Insert(cmdIndex, new Separator
{
Style = contextMenu.TryFindResource(Spelling.SeparatorStyleKey) as Style,
Tag = typeof(Spelling)
});
}
}

private static void RemoveSpellCheckMenuItems([CanBeNull] FrameworkElement tbBase)
private static void RemoveSpellCheckMenuItems([CanBeNull] ContextMenu contextMenu)
{
if (tbBase?.ContextMenu == null)
if (contextMenu != null)
{
return;
}
var spellCheckItems = tbBase.ContextMenu.Items.OfType<ISpellCheckMenuItem>().ToList();
foreach (var item in spellCheckItems)
{
tbBase.ContextMenu.Items.Remove(item);
var spellCheckItems = contextMenu.Items.OfType<FrameworkElement>().Where(item => ReferenceEquals(item.Tag, typeof(Spelling))).ToList();
foreach (var item in spellCheckItems)
{
contextMenu.Items.Remove(item);
}
}
}

@@ -1,4 +1,6 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:MahApps.Metro.Controls">

<MenuScrollingVisibilityConverter x:Key="MenuScrollingVisibilityConverter" />

@@ -14,7 +16,7 @@
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Separator}">
<Grid Margin="0 6 0 4" SnapsToDevicePixels="true">
<Grid Margin="0 6 0 4" SnapsToDevicePixels="True">
<Rectangle Height="1"
Margin="20 0 1 1"
Fill="{DynamicResource GrayBrush7}" />
@@ -139,6 +141,30 @@
</Style.Triggers>
</Style>

<Style x:Key="{x:Static controls:Spelling.SuggestionMenuItemStyleKey}"
BasedOn="{StaticResource MetroMenuItem}"
TargetType="{x:Type MenuItem}">
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Header" Value="{Binding RelativeSource={RelativeSource Self}, Path=CommandParameter}" />
</Style>

<Style x:Key="{x:Static controls:Spelling.IgnoreAllMenuItemStyleKey}"
BasedOn="{StaticResource MetroMenuItem}"
TargetType="{x:Type MenuItem}">
<Setter Property="Header" Value="Ignore All" />
</Style>

<Style x:Key="{x:Static controls:Spelling.NoSuggestionsMenuItemStyleKey}"
BasedOn="{StaticResource MetroMenuItem}"
TargetType="{x:Type MenuItem}">
<Setter Property="Header" Value="(no spelling suggestions)" />
<Setter Property="IsEnabled" Value="False" />
</Style>

<Style x:Key="{x:Static controls:Spelling.SeparatorStyleKey}"
BasedOn="{StaticResource {x:Static MenuItem.SeparatorStyleKey}}"
TargetType="{x:Type Separator}" />

<ContextMenu x:Key="TextBoxMetroContextMenu" Style="{StaticResource MetroContextMenu}">
<MenuItem Command="ApplicationCommands.Cut" Style="{DynamicResource MetroMenuItem}" />
<MenuItem Command="ApplicationCommands.Copy" Style="{DynamicResource MetroMenuItem}" />
@@ -22,6 +22,7 @@
<Setter Property="Controls:TextBoxHelper.ButtonFontSize" Value="{DynamicResource ClearTextButtonFontSize}" />
<Setter Property="Controls:TextBoxHelper.ButtonWidth" Value="22" />
<Setter Property="Controls:TextBoxHelper.IsMonitoring" Value="True" />
<Setter Property="Controls:TextBoxHelper.IsSpellCheckContextMenuEnabled" Value="{Binding RelativeSource={RelativeSource Self}, Path=(SpellCheck.IsEnabled)}" />
<Setter Property="FontFamily" Value="{DynamicResource ContentFontFamily}" />
<Setter Property="FontSize" Value="{DynamicResource ContentFontSize}" />
<Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
@@ -503,6 +504,7 @@
<Setter Property="ContextMenu" Value="{DynamicResource TextBoxMetroContextMenu}" />
<Setter Property="Controls:ControlsHelper.FocusBorderBrush" Value="{DynamicResource TextBoxFocusBorderBrush}" />
<Setter Property="Controls:ControlsHelper.MouseOverBorderBrush" Value="{DynamicResource TextBoxMouseOverBorderBrush}" />
<Setter Property="Controls:TextBoxHelper.IsSpellCheckContextMenuEnabled" Value="{Binding RelativeSource={RelativeSource Self}, Path=(SpellCheck.IsEnabled)}" />
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="FontFamily" Value="{DynamicResource ContentFontFamily}" />
<Setter Property="FontSize" Value="{DynamicResource ContentFontSize}" />

0 comments on commit 7974452

Please sign in to comment.