22

I am trying to use the SpellCheck class C# provides (in PresentationFramework.dll). But, I am experiencing problems when trying to bind the spelling to my textbox:

SpellCheck.SetIsEnabled(txtWhatever, true);

The problem is that my txtWhatever is of type System.Windows.Forms and the parameter this function is looking for is System.Windows.Controls, and simple converting failed. I also tried to make my TextBox of this type, but... couldn't. Does anyone know how to use this SpellCheck object? (MSDN wasn't that helpful...)

Thanks

Muad'Dib
  • 28,542
  • 5
  • 55
  • 68
Assaf
  • 788
  • 2
  • 9
  • 23

7 Answers7

58

You have to use a WPF TextBox to make spell checking work. You can embed one in a Windows Forms form with the ElementHost control. It works pretty similar to a UserControl. Here's a control that you can drop straight from the toolbox. To get started, you need Project + Add Reference and select WindowsFormsIntegration, System.Design and the WPF assemblies PresentationCore, PresentationFramework and WindowsBase.

Add a new class to your project and paste the code shown below. Compile. Drop the SpellBox control from the top of the toolbox onto a form. It supports the TextChanged event and the Multiline and WordWrap properties. There's a nagging problem with the Font, there is no easy way to map a WF Font to the WPF font properties. The easiest workaround for that is to set the form's Font to "Segoe UI", the default for WPF.

using System;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms.Integration;
using System.Windows.Forms.Design;

[Designer(typeof(ControlDesigner))]
//[DesignerSerializer("System.Windows.Forms.Design.ControlCodeDomSerializer, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.ComponentModel.Design.Serialization.CodeDomSerializer, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
class SpellBox : ElementHost {
    public SpellBox() {
        box = new TextBox();
        base.Child = box;
        box.TextChanged += (s, e) => OnTextChanged(EventArgs.Empty);
        box.SpellCheck.IsEnabled = true;
        box.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
        this.Size = new System.Drawing.Size(100, 20);
    }
    public override string Text {
        get { return box.Text; }
        set { box.Text = value; }
    }
    [DefaultValue(false)]
    public bool Multiline {
        get { return box.AcceptsReturn; }
        set { box.AcceptsReturn = value; }
    }
    [DefaultValue(false)]
    public bool WordWrap {
        get { return box.TextWrapping != TextWrapping.NoWrap; }
        set { box.TextWrapping = value ? TextWrapping.Wrap : TextWrapping.NoWrap; }
    }
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public new System.Windows.UIElement Child {
        get { return base.Child; }
        set { /* Do nothing to solve a problem with the serializer !! */ }
    }
    private TextBox box;
}

By popular demand, a VB.NET version of this code that avoids the lambda:

Imports System
Imports System.ComponentModel
Imports System.ComponentModel.Design.Serialization
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Forms.Integration
Imports System.Windows.Forms.Design

<Designer(GetType(ControlDesigner))> _
Class SpellBox
    Inherits ElementHost

    Public Sub New()
        box = New TextBox()
        MyBase.Child = box
        AddHandler box.TextChanged, AddressOf box_TextChanged
        box.SpellCheck.IsEnabled = True
        box.VerticalScrollBarVisibility = ScrollBarVisibility.Auto
        Me.Size = New System.Drawing.Size(100, 20)
    End Sub

    Private Sub box_TextChanged(ByVal sender As Object, ByVal e As EventArgs)
        OnTextChanged(EventArgs.Empty)
    End Sub

    Public Overrides Property Text() As String
        Get
            Return box.Text
        End Get
        Set(ByVal value As String)
            box.Text = value
        End Set
    End Property

    <DefaultValue(False)> _
    Public Property MultiLine() As Boolean
        Get
            Return box.AcceptsReturn
        End Get
        Set(ByVal value As Boolean)
            box.AcceptsReturn = value
        End Set
    End Property

    <DefaultValue(False)> _
    Public Property WordWrap() As Boolean
        Get
            Return box.TextWrapping <> TextWrapping.NoWrap
        End Get
        Set(ByVal value As Boolean)
            If value Then
                box.TextWrapping = TextWrapping.Wrap
            Else
                box.TextWrapping = TextWrapping.NoWrap
            End If
        End Set
    End Property

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
    Public Shadows Property Child() As System.Windows.UIElement
        Get
            Return MyBase.Child
        End Get
        Set(ByVal value As System.Windows.UIElement)
            '' Do nothing to solve a problem with the serializer !!
        End Set
    End Property
    Private box As TextBox
End Class
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • 1
    I'm trying to implement your solution: What is the (s,e) part of the code means? Because I am getting a compiler error for it? – Assaf Oct 27 '10 at 07:14
  • It is a lambda expression, VS2008 required. Just delete it and let intellisense add a regular method instead. – Hans Passant Oct 27 '10 at 07:21
  • Erm, come to think of it, you have to have VS2008 if you use WPF? – Hans Passant Oct 27 '10 at 07:22
  • I'm using VS2005 and the WPF thing works - it is just not yet called WPF... and thanks for the help - the intellisense did its job. – Assaf Oct 27 '10 at 08:07
  • Thanks for posting this code. I converted it to VB.Net and it is working almost perfectly (still can't get the lambda/text changed event line to work at all, but I'm going to keep at it and get it eventually!). – Crag Nov 23 '10 at 19:56
  • Thank you for posting this sir, you have doubtlessly saved me many hours. :) – Tuxmentat May 02 '11 at 20:11
  • Thank You! I'm voting this up for sure, you saved me money and wasted hours. Great Post! – MDL Mar 06 '12 at 16:02
  • 1
    This isn't working for me and I have no idea why. The wrong word doesn't get underlined. At first the compiler claimed about an assembly reference not found but just adding `System.Xaml` referenced solved. But the spell still isn't working for me: http://prntscr.com/6rvwwk what am I missing? of course that `textBox` is your `SpellBox`. – Jack Apr 10 '15 at 04:32
  • 4
    Ok, I got it working. :) I set the language explicitly: `box.Language = System.Windows.Markup.XmlLanguage.GetLanguage("en-US");`. You also need to have the .NET language pack installed (but I had already). I'm not sure but this issue is probably due the fact the English isn't my OS' language. It working: http://prntscr.com/6rw50x – Jack Apr 10 '15 at 05:09
  • 1
    @Hans great example many thanks. Just a quick question are we able to add custom words to the dictionary for the example you gave? – TripVoltage Jun 09 '15 at 07:00
  • @HansPassant - This is exactly what I need. However, I can't seem to figure out how to implement the TextChanged event. Do I code it in the SpellCheck class or my form? – rrirower Dec 15 '17 at 19:51
  • 1
    @HansPassant- Figured it out. I needed to dynamically add the events to my form. Thanks for your work. – rrirower Dec 15 '17 at 20:53
  • 1
    @Jack, did you ever figure out how that works exactly? `en-GB` doesn't work for me: it accepts "color" but marks "colour" as wrong. I'm expecting the opposite behaviour, which is what MS Word does on my system. – Lorraine Oct 19 '18 at 11:37
  • @Wilson My experience with this wasn't good. I don't know the reason for this specific behavior, but from what I remember, it look up the words (the dictionary) from the files provided by the Windows/.NET framework but it support few languages. For example, installing the chinese pack language doesn't add Support for it. I think you're better off using some other implementation. I don't remember now the library I've used but I'm sure there are alot of good ones over internet. Pick one and anything ask here on SO. – Jack Oct 19 '18 at 15:08
0

Free .NET spell checker based around a WPF text box that can be used client or server side can be seen here. It will wrap the text box for you although you still need the assembly includes to Presentation framework etc.

Full disclosure...written by yours truly

Aran Mulholland
  • 23,555
  • 29
  • 141
  • 228
0

I needed to add a background colour to the textbox in winforms that reflected the colour selected in the designer:

public override System.Drawing.Color BackColor
{
    get
    {
        if (box == null) { return Color.White; }
        System.Windows.Media.Brush br = box.Background;
        byte a = ((System.Windows.Media.SolidColorBrush)(br)).Color.A;
        byte g = ((System.Windows.Media.SolidColorBrush)(br)).Color.G;
        byte r = ((System.Windows.Media.SolidColorBrush)(br)).Color.R;
        byte b = ((System.Windows.Media.SolidColorBrush)(br)).Color.B;
        return System.Drawing.Color.FromArgb((int)a, (int)r, (int)g, (int)b);
    }
    set 
    {
        box.Background = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromArgb(value.A, value.R, value.G, value.B));
    }
}
Rupert
  • 169
  • 1
  • 2
  • 4
0

Have you tried just setting the property on the actual TextBox your attempting to spellcheck. e.g.

txtWhatever.SpellCheck.IsEnabled = true;
George Johnston
  • 31,652
  • 27
  • 127
  • 172
  • Yes, i tried. System.Windows.Forms doesn't have this property probably. This is exactly my problem... – Assaf Oct 26 '10 at 14:49
0

You're trying to use a spell-check component designed for WPF on a WinForms application. They're incompatible.

If you want to use the .NET-provided spell check, you'll have to use WPF as your widget system.

If you want to stick with WinForms, you'll need a third-party spell check component.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
0

If you want to enable the TextChanged Event write the code like this.

using System;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms.Integration;
using System.Windows.Forms.Design;

[Designer(typeof(ControlDesigner))]
//[DesignerSerializer("System.Windows.Forms.Design.ControlCodeDomSerializer, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.ComponentModel.Design.Serialization.CodeDomSerializer, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
class SpellBox : ElementHost
{    
    public SpellBox()
    {
        box = new TextBox();
        base.Child = box;
        box.TextChanged += (s, e) => OnTextChanged(EventArgs.Empty);
        box.SpellCheck.IsEnabled = true;
        box.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
        box.TextChanged += new System.Windows.Controls.TextChangedEventHandler(SpellBox_TextChanged);
        this.Size = new System.Drawing.Size(100, 20);
    }

    [Browsable(true)]
    [Category("Action")]
    [Description("Invoked when Text Changes")]
    public new event EventHandler TextChanged;
    protected void SpellBox_TextChanged(object sender, EventArgs e)
    {        
        if (this.TextChanged!=null)
            this.TextChanged(this, e);
    }
    public override string Text
    {
        get { return box.Text; }
        set { box.Text = value; }
    }
    [DefaultValue(false)]
    public bool Multiline
    {
        get { return box.AcceptsReturn; }
        set { box.AcceptsReturn = value; }
    }
    [DefaultValue(false)]
    public bool WordWrap
    {
        get { return box.TextWrapping != TextWrapping.NoWrap; }
        set { box.TextWrapping = value ? TextWrapping.Wrap : TextWrapping.NoWrap; }
    }
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public new System.Windows.UIElement Child
    {
        get { return base.Child; }
        set { /* Do nothing to solve a problem with the serializer !! */ }
    }
    private TextBox box;
}
-1

what about getting a list of words in the english language and copying that to a text file. add the reference. then use streamreader class to analyze the list against textbox.text. any words not found in the text file could be set to be highlighted or displayed in a dialog box with options to replace or ignore. this is a shotgun suggestion with many missing steps and i am 2 months into programming but....its what im going to attempt anyway. i am making a notepad project (rexpad on idreamincode.com). hope this helped!

Adam
  • 1
  • 1