Resolved Microsoft.Office.Interop.Word setting SpellChecking Window Size and Location

tim8w

Well-known member
Joined
Sep 8, 2020
Messages
131
Programming Experience
10+
I am using Microsoft.Office.Interop.Word to SpellCheck a TextEdit box in my C# application. Works great except that I would like to center the SpellCheck Dialog on the Parent Form that called it. Is there a way to do this?

Here is the C# code that calls it:

C#:
public void fSpellCheck(TextBox tBox, Label lLbl)
{
    int iErrorCount = 0;
    Microsoft.Office.Interop.Word.Application app = new Microsoft.Office.Interop.Word.Application();
    if (tBox.Text.Length > 0)
    {
        app.Visible=false;
        // Setting these variables is comparable to passing null to the function.
        // This is necessary because the C# null cannot be passed by reference.
        object template=Missing.Value;
        object newTemplate=Missing.Value;
        object documentType=Missing.Value;
        object visible=true;
        object optional = Missing.Value;
        
        _Document doc = app.Documents.Add(ref template, ref newTemplate, ref documentType, ref visible);
        doc.Words.First.InsertBefore (tBox.Text );
        Microsoft.Office.Interop.Word.ProofreadingErrors we = doc.SpellingErrors;
        iErrorCount = we.Count;

        doc.CheckSpelling( ref optional, ref optional, ref optional, ref optional,
            ref optional, ref optional, ref optional,
            ref optional, ref optional, ref optional, ref optional, ref optional);
    
        if (iErrorCount == 0)
            lLbl.Text = "Spelling OK. No errors corrected ";
        else if (iErrorCount == 1)
            lLbl.Text = "Spelling OK. 1 error corrected ";
        else
            lLbl.Text = "Spelling OK. " + iErrorCount + " errors corrected ";
        object first=0;
        object last=doc.Characters.Count -1;             
            
        tBox.Text = doc.Range(ref first, ref last).Text;                 
    }
    else
        lLbl.Text = "Textbox is empty";

    object saveChanges = false;
    object originalFormat = Missing.Value;
    object routeDocument = Missing.Value;
    app.Quit( ref saveChanges, ref originalFormat, ref routeDocument);
}
 
You would need to use Window Functions - Win32 apps - FindWindow, GetWindowRect (to calculate center) and SetWindowPos. Since this opens as a dialog you also need to run this as a background task before that call.
 
You would need to use Window Functions - Win32 apps - FindWindow, GetWindowRect (to calculate center) and SetWindowPos. Since this opens as a dialog you also need to run this as a background task before that call.
Never done a Background Task. Can you show me the basics? I think I understand how to do the FindWind, GetWindowRect and SetWindowPos, part...
 
Perhaps I am missing something, but why are you using Microsoft Word to do the spell checking for your app. Shouldn't you be using the Win32 Spell Checking APIs instead?

 
Time to start learning about P/Invoke. :)

And yes, I have used the API many many years ago when I used to write code in C++. If you don't want to learn how to P/Invoke, the alternative is to write a wrapper in C++/CLI which exposes a .NET class in an assembly, and then reference that assembly.
 
Last edited:
Got it working. Thanks for the help!

C#:
int XParentCenter = _rectParent.Left + (_rectParent.Right - _rectParent.Left) / 2;
int YParentCenter = _rectParent.Top + (_rectParent.Bottom - _rectParent.Top) / 2;
RECT WindowRect = new RECT();

spellingWindow = FindWindow(null, "Spelling: English (United States)");
while (spellingWindow == IntPtr.Zero)
{
    spellingWindow = FindWindow(null, "Spelling: English (United States)");
}
GetWindowRect(spellingWindow, ref WindowRect);
MoveWindow(spellingWindow, XParentCenter - (WindowRect.Right - WindowRect.Left) / 2, YParentCenter - (WindowRect.Bottom - WindowRect.Top) / 2, (WindowRect.Right - WindowRect.Left), (WindowRect.Bottom - WindowRect.Top), true);
SetForegroundWindow(spellingWindow);
 
I started with Task.Run(Center); before the CheckSpelling call:
C#:
private async void Center()
{
    await Task.Delay(50);
    var h = Native.FindWindow(null, "Spelling: English (United States)");
    if (h != IntPtr.Zero)
    {
        Native.GetWindowRect(h, out Native.RECT r);
        var x = Location.X + (Width + r.Left - r.Right) / 2;
        var y = Location.Y + (Height + r.Top - r.Bottom) / 2;
        Native.SetWindowPos(h, IntPtr.Zero, x, y, 0, 0, Native.SWP_NOSIZE | Native.SWP_NOZORDER);
    }
}
Just a short Task.Delay was necessary, the dialog took a lot longer to display, but this worked anyway. Benefit of SetWindowPos is that you don't have to set window size.
You can use a loop too, but without the initial Task.Delay it will do 3000 calls to FindWindow before the dialog appears. The loop can be simplified like this:
C#:
IntPtr h;
do
{
    h = Native.FindWindow(null, "Spelling: English (United States)");
} while (h == IntPtr.Zero);
If using a loop there should also be an exit strategy if anything goes wrong.
 
I started with Task.Run(Center); before the CheckSpelling call:
C#:
private async void Center()
{
    await Task.Delay(50);
    var h = Native.FindWindow(null, "Spelling: English (United States)");
    if (h != IntPtr.Zero)
    {
        Native.GetWindowRect(h, out Native.RECT r);
        var x = Location.X + (Width + r.Left - r.Right) / 2;
        var y = Location.Y + (Height + r.Top - r.Bottom) / 2;
        Native.SetWindowPos(h, IntPtr.Zero, x, y, 0, 0, Native.SWP_NOSIZE | Native.SWP_NOZORDER);
    }
}
Just a short Task.Delay was necessary, the dialog took a lot longer to display, but this worked anyway. Benefit of SetWindowPos is that you don't have to set window size.
You can use a loop too, but without the initial Task.Delay it will do 3000 calls to FindWindow before the dialog appears. The loop can be simplified like this:
C#:
IntPtr h;
do
{
    h = Native.FindWindow(null, "Spelling: English (United States)");
} while (h == IntPtr.Zero);
If using a loop there should also be an exit strategy if anything goes wrong.
I get the error on the Task.Run(Center) line:

"The call is ambiguous between the following methods or properties: 'System.Threading.Tasks.Task.Run(System.Func <System.Threading.Tasks.Task>)' and 'System.Threading.Tasks.Task.Run(System.Action)'"
 
Don't know why, it is a void method so it's an Action, you could try Task.Run((Action)Center); (I get "Cast is redundant" for this)
 
Back
Top Bottom