Skip to content

Commit 5ff1406

Browse files
committed
Merge branch 'spellCheck' of https://github.com/Keboo/MaterialDesignInXamlToolkit into Keboo-spellCheck
2 parents 18524dd + 58f2169 commit 5ff1406

8 files changed

+174
-7
lines changed

MainDemo.Wpf/TextFields.xaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
MinWidth="280"
9191
AcceptsReturn="True"
9292
VerticalScrollBarVisibility="Auto"
93+
SpellCheck.IsEnabled="True"
9394
materialDesign:HintAssist.Hint="Multiline text"
9495
Height="80">Multiline. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. The quick brown fox jumps over the lazy dog. War and peace. Keep going. Go on. For how long? Not long. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</TextBox>
9596
<materialDesign:PackIcon Grid.Row="2" Grid.Column="0" Kind="Phone" Foreground="{Binding ElementName=PhoneTextBox, Path=BorderBrush}" />

MaterialDesignThemes.Wpf/MaterialDesignThemes.Wpf.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,9 @@
329329
<Compile Include="SnackbarMessageEventArgs.cs" />
330330
<Compile Include="SnackbarMessageQueue.cs" />
331331
<Compile Include="SnackbarMessageQueueItem.cs" />
332+
<Compile Include="SpellingIgnoreAllMenuItem.cs" />
333+
<Compile Include="SpellingNoSuggestionsMenuItem.cs" />
334+
<Compile Include="SpellingSuggestionMenuItem.cs" />
332335
<Compile Include="Transitions\CircleWipe.cs" />
333336
<Compile Include="IHintProxy.cs" />
334337
<Compile Include="Transitions\IndexedItemOffsetMultiplierExtension.cs" />
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System.Windows.Controls;
2+
using System.Windows.Documents;
3+
4+
namespace MaterialDesignThemes.Wpf
5+
{
6+
public class SpellingIgnoreAllMenuItem : MenuItem
7+
{
8+
public SpellingIgnoreAllMenuItem()
9+
{
10+
Command = EditingCommands.IgnoreSpellingError;
11+
}
12+
}
13+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
using System.Windows.Controls;
2+
3+
namespace MaterialDesignThemes.Wpf
4+
{
5+
public class SpellingNoSuggestionsMenuItem : MenuItem
6+
{ }
7+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System.Windows.Controls;
2+
using System.Windows.Documents;
3+
4+
namespace MaterialDesignThemes.Wpf
5+
{
6+
public class SpellingSuggestionMenuItem : MenuItem
7+
{
8+
public string Suggestion { get; }
9+
public SpellingSuggestionMenuItem(string spellingSuggestion)
10+
{
11+
Suggestion = spellingSuggestion;
12+
Command = EditingCommands.CorrectSpellingError;
13+
CommandParameter = spellingSuggestion;
14+
}
15+
}
16+
}

MaterialDesignThemes.Wpf/TextFieldAssist.cs

Lines changed: 119 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
using System;
1+
using System.Linq;
22
using System.Windows;
33
using System.Windows.Controls;
44
using System.Windows.Controls.Primitives;
5-
using System.Windows.Media;
65

76
namespace MaterialDesignThemes.Wpf
87
{
@@ -64,9 +63,122 @@ public static void SetDecorationVisibility(DependencyObject element, Visibility
6463
/// <returns></returns>
6564
public static Visibility GetDecorationVisibility(DependencyObject element)
6665
{
67-
return (Visibility) element.GetValue(DecorationVisibilityProperty);
66+
return (Visibility)element.GetValue(DecorationVisibilityProperty);
6867
}
69-
68+
69+
/// <summary>
70+
/// Automatially inserts spelling suggestions into the text box context menu.
71+
/// </summary>
72+
public static readonly DependencyProperty IncludeSpellingSuggestionsProperty = DependencyProperty.RegisterAttached(
73+
"IncludeSpellingSuggestions", typeof(bool), typeof(TextFieldAssist), new PropertyMetadata(default(bool), IncludeSpellingSuggestionsChanged));
74+
75+
public static void SetIncludeSpellingSuggestions(TextBoxBase element, bool value)
76+
{
77+
element.SetValue(IncludeSpellingSuggestionsProperty, value);
78+
}
79+
80+
public static bool GetIncludeSpellingSuggestions(TextBoxBase element)
81+
{
82+
return (bool)element.GetValue(IncludeSpellingSuggestionsProperty);
83+
}
84+
85+
private static void IncludeSpellingSuggestionsChanged(DependencyObject element, DependencyPropertyChangedEventArgs e)
86+
{
87+
var textBox = element as TextBoxBase;
88+
if (textBox != null)
89+
{
90+
if ((bool)e.NewValue)
91+
{
92+
textBox.ContextMenuOpening += TextBoxOnContextMenuOpening;
93+
textBox.ContextMenuClosing += TextBoxOnContextMenuClosing;
94+
}
95+
else
96+
{
97+
textBox.ContextMenuOpening -= TextBoxOnContextMenuOpening;
98+
textBox.ContextMenuClosing -= TextBoxOnContextMenuClosing;
99+
}
100+
}
101+
}
102+
103+
private static void TextBoxOnContextMenuOpening(object sender, ContextMenuEventArgs e)
104+
{
105+
var textBoxBase = sender as TextBoxBase;
106+
107+
ContextMenu contextMenu = textBoxBase?.ContextMenu;
108+
if (contextMenu == null) return;
109+
110+
RemoveSpellingSuggestions(contextMenu);
111+
112+
if (!SpellCheck.GetIsEnabled(textBoxBase)) return;
113+
114+
SpellingError spellingError = GetSpellingError(textBoxBase);
115+
if (spellingError != null)
116+
{
117+
int insertionIndex = 0;
118+
bool hasSuggestion = false;
119+
foreach (string suggestion in spellingError.Suggestions)
120+
{
121+
hasSuggestion = true;
122+
var menuItem = new SpellingSuggestionMenuItem(suggestion)
123+
{
124+
CommandTarget = textBoxBase
125+
};
126+
contextMenu.Items.Insert(insertionIndex++, menuItem);
127+
}
128+
if (!hasSuggestion)
129+
{
130+
contextMenu.Items.Insert(insertionIndex++, new SpellingNoSuggestionsMenuItem());
131+
}
132+
133+
contextMenu.Items.Insert(insertionIndex++, new Separator { Tag = typeof(SpellingSuggestionMenuItem) });
134+
135+
contextMenu.Items.Insert(insertionIndex++, new SpellingIgnoreAllMenuItem
136+
{
137+
CommandTarget = textBoxBase
138+
});
139+
140+
contextMenu.Items.Insert(insertionIndex, new Separator { Tag = typeof(SpellingSuggestionMenuItem) });
141+
}
142+
}
143+
144+
private static SpellingError GetSpellingError(TextBoxBase textBoxBase)
145+
{
146+
var textBox = textBoxBase as TextBox;
147+
if (textBox != null)
148+
{
149+
return textBox.GetSpellingError(textBox.CaretIndex);
150+
}
151+
var richTextBox = textBoxBase as RichTextBox;
152+
if (richTextBox != null)
153+
{
154+
return richTextBox.GetSpellingError(richTextBox.CaretPosition);
155+
}
156+
return null;
157+
}
158+
159+
private static void TextBoxOnContextMenuClosing(object sender, ContextMenuEventArgs e)
160+
{
161+
var contextMenu = (sender as TextBoxBase)?.ContextMenu;
162+
if (contextMenu != null)
163+
{
164+
RemoveSpellingSuggestions(contextMenu);
165+
}
166+
}
167+
168+
private static void RemoveSpellingSuggestions(ContextMenu menu)
169+
{
170+
foreach (object item in (from item in menu.Items.OfType<object>()
171+
let separator = item as Separator
172+
where item is SpellingSuggestionMenuItem ||
173+
item is SpellingIgnoreAllMenuItem ||
174+
item is SpellingNoSuggestionsMenuItem ||
175+
ReferenceEquals(separator?.Tag, typeof(SpellingSuggestionMenuItem))
176+
select item).ToList())
177+
{
178+
menu.Items.Remove(item);
179+
}
180+
}
181+
70182
#region Methods
71183

72184
/// <summary>
@@ -84,7 +196,7 @@ private static void ApplyTextBoxViewMargin(Control textBox, Thickness margin)
84196
var frameworkElement = (textBox.Template.FindName("PART_ContentHost", textBox) as ScrollViewer)?.Content as FrameworkElement;
85197
if (frameworkElement != null)
86198
{
87-
frameworkElement.Margin = margin;
199+
frameworkElement.Margin = margin;
88200
}
89201
}
90202

@@ -105,12 +217,12 @@ private static void TextBoxViewMarginPropertyChangedCallback(
105217

106218
if (box.IsLoaded)
107219
{
108-
ApplyTextBoxViewMargin(box, (Thickness) dependencyPropertyChangedEventArgs.NewValue);
220+
ApplyTextBoxViewMargin(box, (Thickness)dependencyPropertyChangedEventArgs.NewValue);
109221
}
110222

111223
box.Loaded += (sender, args) =>
112224
{
113-
var textBox = (Control) sender;
225+
var textBox = (Control)sender;
114226
ApplyTextBoxViewMargin(textBox, GetTextBoxViewMargin(textBox));
115227
};
116228
}

MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.Menu.xaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,4 +274,18 @@
274274
</Setter.Value>
275275
</Setter>
276276
</Style>
277+
278+
<Style TargetType="{x:Type wpf:SpellingSuggestionMenuItem}" BasedOn="{StaticResource MaterialDesignMenuItem}">
279+
<Setter Property="FontWeight" Value="Bold" />
280+
<Setter Property="Header" Value="{Binding RelativeSource={RelativeSource Self}, Path=Suggestion}" />
281+
</Style>
282+
283+
<Style TargetType="{x:Type wpf:SpellingIgnoreAllMenuItem}" BasedOn="{StaticResource MaterialDesignMenuItem}">
284+
<Setter Property="Header" Value="Ignore All" />
285+
</Style>
286+
287+
<Style TargetType="{x:Type wpf:SpellingNoSuggestionsMenuItem}" BasedOn="{StaticResource MaterialDesignMenuItem}">
288+
<Setter Property="Header" Value="(no spelling suggestions)" />
289+
<Setter Property="IsEnabled" Value="False" />
290+
</Style>
277291
</ResourceDictionary>

MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.TextBox.xaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
2525
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
2626
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource MaterialDesignValidationErrorTemplate}"/>
27+
<Setter Property="wpf:TextFieldAssist.IncludeSpellingSuggestions" Value="{Binding RelativeSource={RelativeSource Self}, Path=(SpellCheck.IsEnabled)}" />
2728
<!-- cludge the default context menu -->
2829
<Setter Property="ContextMenu">
2930
<Setter.Value>

0 commit comments

Comments
 (0)