4

Looking for hints, tips and search terms for changing the text on a win32 window from C#.

More specifically, I'm trying to change the text on the print dialog from "Print" to "OK", as I am using the dialog to create a print ticket and not do any printing.

How can I find the dialog's window handle? Once I've got it, how would I go about finding the button in the child windows of the form? Once I've found that, how would I change the text on the button? And how can I do all this before the dialog is shown?

There's a similar question here, but it points to a CodeProject article that is waaay more complex than needed and is taking me a bit longer to parse through than I'd like to spend on this. TIA.

  • 1
    Can you refine your question a bit? I'm confused on what parts are changing to what (the acceptance button in the Print Dialog on my computer already says "OK"). – Jon Seigel Apr 19 '10 at 15:45
  • +1 to Jon's comment - I get the same under Windows Vista Business – Jon Cage Apr 19 '10 at 15:49
  • @jon (and jon... JonJon) Its always "Print" when used via System.Windows.Controls.PrintDialog. Nevertheless, it matters not to the core of the question. Button says X, I want to make it say Y, how do pls. –  Apr 19 '10 at 15:59

1 Answers1

9

You should use Spy++ to take a look at the dialog. The class name is important and the control ID of the button. If it is a native Windows dialog then the class name should be "#32770". In which case you'll have a lot of use for my post in this thread. Here is another in C#. You change the button text by P/Invoking SetWindowText() on the button handle.


using System;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;

class SetDialogButton : IDisposable {
    private Timer mTimer = new Timer();
    private int mCtlId;
    private string mText;

    public SetDialogButton(int ctlId, string txt) {
        mCtlId = ctlId;
        mText = txt;
        mTimer.Interval = 50;
        mTimer.Enabled = true;
        mTimer.Tick += (o, e) => findDialog();
    }

    private void findDialog() {
        // Enumerate windows to find the message box
        EnumThreadWndProc callback = new EnumThreadWndProc(checkWindow);
        if (!EnumThreadWindows(GetCurrentThreadId(), callback, IntPtr.Zero)) mTimer.Enabled = false;
    }
    private bool checkWindow(IntPtr hWnd, IntPtr lp) {
        // Checks if <hWnd> is a dialog
        StringBuilder sb = new StringBuilder(260);
        GetClassName(hWnd, sb, sb.Capacity);
        if (sb.ToString() != "#32770") return true;
        // Got it, get the STATIC control that displays the text
        IntPtr hCtl = GetDlgItem(hWnd, mCtlId);
        SetWindowText(hCtl, mText);
        // Done
        return true;
    }
    public void Dispose() {
        mTimer.Enabled = false;
    }

    // P/Invoke declarations
    private const int WM_SETFONT = 0x30;
    private const int WM_GETFONT = 0x31;
    private delegate bool EnumThreadWndProc(IntPtr hWnd, IntPtr lp);
    [DllImport("user32.dll")]
    private static extern bool EnumThreadWindows(int tid, EnumThreadWndProc callback, IntPtr lp);
    [DllImport("kernel32.dll")]
    private static extern int GetCurrentThreadId();
    [DllImport("user32.dll")]
    private static extern int GetClassName(IntPtr hWnd, StringBuilder buffer, int buflen);
    [DllImport("user32.dll")]
    private static extern IntPtr GetDlgItem(IntPtr hWnd, int item);
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern bool SetWindowText(IntPtr hWnd, string txt);
}

Usage:

        using (new SetDialogButton(1, "Okay")) {
            printDialog1.ShowDialog();
        }
Community
  • 1
  • 1
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • 1
    @Hans Well, DAMNIT. I managed to leverage your code to locate the dialog, then I was able to enumerate the child windows and change all the text in those. Except that the ONE button I needed to change did not. See: http://i43.tinypic.com/6xvr7m.png Any ideas? –  Apr 19 '10 at 18:09
  • Hmmm, stepped through my code and the button window SAYS its text is OK and it SAYS it changes but when the dialog appears its still "Print"... –  Apr 19 '10 at 18:20
  • Bummer, you seem to be doing it right from the screen shot. I can only guess that you didn't find the right button. – Hans Passant Apr 19 '10 at 18:34
  • I see it is the PrintDialog class. Perhaps significant is that the Print button is the very first control, control ID 1. You ought to use GetDlgItem() to get its handle. – Hans Passant Apr 19 '10 at 18:47
  • I've tried `SetDlgItemText(hWnd, 1, "OMFG");` and GetDlgItem/SetWindowText but neither work. –  Apr 19 '10 at 19:49
  • Again, I grab the first dialog item and the text on it is OK. Its almost as if the "Print" is placed on the button *after* I change it. –  Apr 19 '10 at 19:52
  • @Will: I could repro your problem. No idea what's causing the problem, but another approach using a Timer to run the code worked. Code added to post. – Hans Passant Apr 19 '10 at 20:52
  • I just copied/pasted this code for exactly the same purpose (replace "Print" button with "OK" button) and it worked out of the box. Nice work! – Evren Kuzucuoglu May 26 '11 at 15:09
  • I used this code and the legend on the PrintDialog Print button changed to Okay - which is brilliant. But is there any way to stop the Timer redraw from making the button text flicker? – Brian THOMAS Feb 15 '18 at 10:32
  • I would like to add/change the button image as well. Is it possible? – Andrew Paes Jan 29 '20 at 20:46