Search code examples
wpfspell-checking

WPF spell checking not loading a custom dictionary in a tab


If I globally enable spell checking in App.xaml...

<Application.Resources>
  <Style TargetType="TextBox">
    <Setter Property="SpellCheck.IsEnabled"
            Value="True" />
  </Style>
</Application.Resources>

...then I get red underlines and spell checking in all textboxes in the application, irrespective of where they are.

If I want to add a custom dictionary, then I have to use code similar to the one shown in this SO answer, and then call it as follows...

public MainWindow() {
  InitializeComponent();
  Loaded += (_, __) => Helpers.SetCustomDictionary(this);
}

(code for helper method shown lower down)

This works fine for textboxes that are shown when the window first loads, but if I have a tab control, and the default tab has a textbox, then the custom dictionary is not applied.

I tried calling Helpers.SetCustomDictionary(this) when the tab loaded, but that didn't work either. I can see that the method is called when the window loads, and my guess is that at that stage, the tab's contents haven't been created, so the method doesn't find them to set the custom dictionary.

The only thing I found that worked was calling it when the individual textbox itself was loaded. However, this is painful, as I have to do this for every single textbox individually.

Anyone know of a way to get the custom dictionary working for textboxes that are not visible when the window first loads?

Thanks

P.S. Here is the code for the helper method, which uses the FindAllChildren() method shown in the linked SO reply...

public static void SetCustomDictionary(DependencyObject parent) {
  Uri uri = new Uri("pack://application:,,,/CustomDictionary.lex");
  List<TextBox> textBoxes = new List<TextBox>();
  FindAllChildren(parent, ref textBoxes);
  foreach (TextBox tb in textBoxes) {
    if (tb.SpellCheck.IsEnabled && !tb.SpellCheck.CustomDictionaries.Contains(uri)) {
      tb.SpellCheck.CustomDictionaries.Add(uri);
    }
  }
}

Solution

  • Whilst Dean Kuga's answer looked promising, it didn't work for me. After some more searching, it seems that there is a bug that prevents the Loaded event from being fired in most cases. In a comment to the answer to the SO question where I saw this mentioned, Marcin Wisnicki linked to some code he wrote that works around the issue.

    As I only want this to work for textboxes, I simplified his code a little. In case it helps anyone, here is my simplified code...

    public partial class App {
      protected override void OnStartup(StartupEventArgs e) {
        base.OnStartup(e);
        EventManager.RegisterClassHandler(typeof(Window),
          FrameworkElement.SizeChangedEvent, new RoutedEventHandler(OnSizeChanged));
        EventManager.RegisterClassHandler(typeof(TextBox),
          FrameworkElement.LoadedEvent, new RoutedEventHandler(OnLoaded), true);
      }
    
      private static void OnSizeChanged(object sender, RoutedEventArgs e) {
        SetMyInitialised((Window)sender, true);
      }
    
      private static void OnLoaded(object sender, RoutedEventArgs e) {
        if (e.OriginalSource is TextBox) {
          TextBox tb = (TextBox)e.OriginalSource;
          Helpers.SetCustomDictionaryTextBox(tb);
        }
      }
    
      #region MyInitialised dependency property
    
      public static readonly DependencyProperty MyInitialisedProperty =
        DependencyProperty.RegisterAttached("MyInitialised",
          typeof(bool),
          typeof(App),
          new FrameworkPropertyMetadata(false,
            FrameworkPropertyMetadataOptions.Inherits,
            OnMyInitialisedChanged));
    
      private static void OnMyInitialisedChanged(DependencyObject dpo,
        DependencyPropertyChangedEventArgs ev) {
        if ((bool)ev.NewValue && dpo is FrameworkElement) {
          (dpo as FrameworkElement).Loaded += delegate { };
        }
      }
    
      public static void SetMyInitialised(UIElement element, bool value) {
        element.SetValue(MyInitialisedProperty, value);
      }
    
      public static bool GetMyInitialised(UIElement element) {
        return (bool)element.GetValue(MyInitialisedProperty);
      }
    
      #endregion MyInitialised
    }
    

    Being English, I changed "Initialized" to "Initialised", but other than that, the DP code is the same.

    Hope this helps someone.