How to open the Interaction.InputBox to center of the form? I know there is a code for the position of the InputBox
Interaction.InputBox("Question?", "Title", "Default Text", x,y);
I will be using this InputBox in different form of different sizes. Is there a way to open the InputBox in the center of the form? Or I have to position them individually on each form?
Is it possible also to reposition the OKbutton and Cancelbutton of InputBox?
Here is something simple to calculate the center of a form, the extra offset is for the size of the input box.
{
int x = this.Left + (this.Width / 2) - 200;
int y = this.Top + (this.Height / 2) - 100;
}
Pass these into the input box for x and y
If you want full customisation then creating your own form is the best way to go as indicated in Fabio's comment.
However, if you just want to approximately centre the box and you will be doing it many times then you could write your own extension method to show and position the input box for you:
public static class FormExtensions
{
public static string CentredInputBox(this Form form, string prompt, string title = "", string defaultResponse = "")
{
const int approxInputBoxWidth = 370;
const int approxInputBoxHeight = 158;
int left = form.Left + (form.Width / 2) - (approxInputBoxWidth / 2);
left = left < 0 ? 0 : left;
int top = form.Top + (form.Height / 2) - (approxInputBoxHeight / 2);
top = top < 0 ? 0 : top;
return Microsoft.VisualBasic.Interaction.InputBox(prompt, title, defaultResponse, left, top);
}
}
Usage from within a form:
this.CentredInputBox("MyPrompt", "MyTitle", "MyDefaultResponse");
It's not perfect because if the box is bigger than normal for some reason then it won't quite be in the centre, and I think its size is variable depending on how much text is in it. However, it shouldn't be far off in normal usage.
To center your InputBox, you can try using Win32 functions to deal with it. This code works for you:
[DllImport("user32")]
private static extern int SetWindowPos(IntPtr hwnd, IntPtr afterHwnd, int x, int y, int cx, int cy, int flag);
[DllImport("user32")]
private static extern IntPtr FindWindow(string className, string caption);
[DllImport("user32")]
private static extern int GetWindowRect(IntPtr hwnd, out RECT rect);
//RECT structure
public struct RECT {
public int left, top, right, bottom;
}
public void ShowCenteredInputBox(string prompt, string title, string defaultReponse){
BeginInvoke((Action)(() => {
while (true) {
IntPtr hwnd = FindWindow(null, title + "\n\n\n");//this is just a trick to identify your InputBox from other window with the same caption
if (hwnd != IntPtr.Zero) {
RECT rect;
GetWindowRect(hwnd, out rect);
int w = rect.right - rect.left;
int h = rect.bottom - rect.top;
int x = Left + (Width - w) / 2;
int y = Top + (Height - h) / 2;
SetWindowPos(hwnd, IntPtr.Zero, x, y, w, h, 0x40);//SWP_SHOWWINDOW = 0x40
break;
}
};
}));
Microsoft.VisualBasic.Interaction.InputBox(prompt, title + "\n\n\n", defaultResponse,0,0);
}
Of course you can also change the position of the buttons, label and TextBox on your InputBox but it's very nasty and tricky, we can say that it's not simple. The recommended solution for you is to create new standard form in System.Windows.Forms.Form, add controls to it and use the method ShowDialog() to show your form.. Of course it requires more code to do but it allows you to fully customize the look and feel and its behaviors.
You can set the InputBox's starting position. There's a property for that
InputBox ib = new InputBox();
ib.StartPosition = FormStartPosition.CenterParent;
Where as FormStartPosition is an enum, from which you can select your desired position!
You can sipmly use -1 for x and y:
Interaction.InputBox("Question?", "Title", "Default Text", -1,-1);
Related
I need a control panel, which is docked to the right side of my primary screen, here's how I've done that:
var PrimaryScreen = Screen.PrimaryScreen;
var WorkingArea = PrimaryScreen.WorkingArea;
this.Width = WorkingArea.Width / 6;
this.Height = WorkingArea.Height;
int X = WorkingArea.Width - this.Width;
int Y = WorkingArea.Location.Y;
this.Location = new Point(X, Y);
This works exactly, how I want it too, with one slight problem. I would need to simultaneously resize the primary screen workingarea, so other maximized forms / apps won't overlap my panel. Also, my panel should be always visible.
I had found a post here, exactly concerning this topic:
Reserve screen area in Windows 7
This is how it was suggested there:
public class WorkArea
{
[System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "SystemParametersInfoA")]
private static extern Int32 SystemParametersInfo(Int32 uAction, Int32 uParam, IntPtr lpvParam, Int32 fuWinIni);
private const Int32 SPI_SETWORKAREA = 47;
public WorkArea(Int32 Left, Int32 Right, Int32 Top, Int32 Bottom)
{
_WorkArea.Left = Left;
_WorkArea.Top = Top;
_WorkArea.Bottom = Bottom;
_WorkArea.Right = Right;
}
public struct RECT
{
public Int32 Left;
public Int32 Right;
public Int32 Top;
public Int32 Bottom;
}
private RECT _WorkArea;
public void SetWorkingArea()
{
IntPtr ptr = IntPtr.Zero;
ptr = Marshal.AllocHGlobal(Marshal.SizeOf(_WorkArea));
Marshal.StructureToPtr(_WorkArea, ptr, false);
int i = SystemParametersInfo(SPI_SETWORKAREA, 0, ptr, 0);
}
}
But the post is over 10 years old, and the solution sadly didn't work for me.
So my question would be: What is the best way to accomplish that? I am open to any suggestions, since I am still learning ;)
I'm trying to do screen captures by capturing a specific window and in order to accurately figure out the size of the window to capture I want to use DwmGetWindowAttribute(). When I call this function with PInvoke on Windows 10 the Rect structure is always empty even though the result value is 0 (success). The Window handle passed in is valid as well because there is fallback code that calls GetWindowRect() which works (albeit with border problems).
I'm a bit at a loss. I used this same code a while back (perhaps on Windows 8.1?) and the same code seemed to be working but now no matter what I do the call to the function always returns an empty structure.
Here's the relevant code.
Definitions:
[DllImport("dwmapi.dll")]
static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, out Rect pvAttribute, int cbAttribute);
[Flags]
public enum DwmWindowAttribute : uint
{
DWMWA_NCRENDERING_ENABLED = 1,
DWMWA_NCRENDERING_POLICY,
DWMWA_TRANSITIONS_FORCEDISABLED,
DWMWA_ALLOW_NCPAINT,
DWMWA_CAPTION_BUTTON_BOUNDS,
DWMWA_NONCLIENT_RTL_LAYOUT,
DWMWA_FORCE_ICONIC_REPRESENTATION,
DWMWA_FLIP3D_POLICY,
DWMWA_EXTENDED_FRAME_BOUNDS,
DWMWA_HAS_ICONIC_BITMAP,
DWMWA_DISALLOW_PEEK,
DWMWA_EXCLUDED_FROM_PEEK,
DWMWA_CLOAK,
DWMWA_CLOAKED,
DWMWA_FREEZE_REPRESENTATION,
DWMWA_LAST
}
[Serializable, StructLayout(LayoutKind.Sequential)]
public struct Rect
{
public int Left;
public int Top;
public int Right;
public int Bottom;
public Rectangle ToRectangle()
{
return Rectangle.FromLTRB(Left, Top, Right, Bottom);
}
}
Code to do the capture:
public static Rectangle GetWindowRectangle(IntPtr handle)
{
Rectangle rected = Rectangle.Empty;
Rect rect = new Rect();
if (Environment.OSVersion.Version.Major < 6)
{
GetWindowRect(handle, out rect);
rected = rect.ToRectangle();
}
else
{
int size = Marshal.SizeOf(typeof(Rect));
int res = DwmGetWindowAttribute(handle, (int)DwmWindowAttribute.DWMWA_EXTENDED_FRAME_BOUNDS, out rect, size);
Debug.WriteLine(res.ToString("x") + " " + size + " " + handle + " " + (int) DwmWindowAttribute.DWMWA_EXTENDED_FRAME_BOUNDS);
// allow returning of desktop and aero windows
if (rected.Width == 0)
{
GetWindowRect(handle, out rect);
rected = rect.ToRectangle();
Debug.WriteLine("Using GetWindowRect");
}
}
Debug.WriteLine(rected.ToString());
return rected;
}
It feels like something simple is missing here. Any ideas?
Based on the Rick Strahl original code as well as Hans Passant correction I created a more compact version of GetWindowsRectangle. I tested it on Windows 10, here's the code in case it helps someone in the future:
public static Rectangle GetWindowRectangle(IntPtr handle)
{
Rect rect = new Rect();
if (Environment.OSVersion.Version.Major >= 6)
{
int size = Marshal.SizeOf(typeof(Rect));
DwmGetWindowAttribute(handle, (int)DwmWindowAttribute.DWMWA_EXTENDED_FRAME_BOUNDS, out rect, size);
}
else if (Environment.OSVersion.Version.Major < 6 || rect.ToRectangle().Width == 0)
{
GetWindowRect(handle, out rect);
}
return rect.ToRectangle();
}
Use GetWindowRect instead of DwmGetWindowAttribute to receive RECT of the window.
[DllImport("user32.dll", SetLastError = true)]
public static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
Mouse stimulation using SendInput works perfectly on MainDisplay. However when I use SendInput for extended screen (e.g. Second screen placed to the left of the main display in my case. Issues is replicable irrespective of the extended display any place around main display but with different resolution then main display):
If I use SendInput on extended screen, the mouse position has offset in both X and Y position, ever so slightly ranging from 40 to 80 points in x and 10 to 20 points in Y based on if X (width) and Y(height) of extended screen is different to main display width/height)
Thanks in advance for any support as to why difference on extended screen
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetCursorPos(ref Win32Point pt);
[DllImport("user32.dll")]
internal static extern bool SetCursorPos(int X, int Y);
[StructLayout(LayoutKind.Sequential)]
internal struct Win32Point
{
public Int32 X;
public Int32 Y;
};
internal enum SendInputEventType : int
{
InputMouse,
InputKeyboard
}
[DllImport("user32.dll", SetLastError = true)]
private static extern uint SendInput(uint nInputs, ref Input pInputs, int cbSize);
public struct Input
{
public uint InputType;
public MouseInput MI;
}
public struct MouseInput
{
public int Dx;
public int Dy;
public uint MouseData;
public uint DwFlags;
public uint Time;
public IntPtr DwExtraInfo;
}
public enum MouseEventInfo
{
mouseEventfMove = 0x0001,
mouseEventfLeftdown = 0x0002,
mouseEventfLeftup = 0x0004,
mouseEventfRightdown = 0x0008,
mouseEventfRightup = 0x0010,
mouseEventfWheel = 0x0800,
mouseEventfAbsolute = 0x8000,
wheelDelta = 0x0078
}
static int CalculateAbsoluteCoordinateX(int x, System.Drawing.Rectangle currentBounds)
{
return ((currentBounds.X + x) * 65536) / (currentBounds.Width);
}
static int CalculateAbsoluteCoordinateY(int y, System.Drawing.Rectangle currentBounds)
{
return (((currentBounds.Y + y) * 65536) / currentBounds.Height);
}
// for me screen at index 0 (screen no 1) is main display. Screen id 2
//placed to the left of the main display as per resolution screen i.e.at
//index 1 (Screen.AllScreens[1]) is extended display and Bound.X is a -ve value
public static int ScreenId = 2;
public static System.Drawing.Rectangle CurrentBounds
{
get
{
return SysForms.Screen.AllScreens[ScreenId - 1].Bounds;
}
}
public static void ClickLeftMouseButton(int x, int y)
{
Input mouseInput = new Input();
mouseInput.InputType = SendInputEventType.InputMouse;
mouseInput.MI.Dx = CalculateAbsoluteCoordinateX(x, CurrentBounds);
mouseInput.MI.Dy = CalculateAbsoluteCoordinateY(y, CurrentBounds);
mouseInput.MI.MouseData = 0;
mouseInput.MI.DwFlags = MouseEventInfo.mouseEventfMove | MouseEventInfo.mouseEventfAbsolute;
SendInput(1, ref mouseInput, Marshal.SizeOf(new INPUT()));
mouseInput.MI.DwFlags = MouseEventInfo.mouseEventfLeftdown;
SendInput(1, ref mouseInput, Marshal.SizeOf(new INPUT()));
mouseInput.MI.DwFlags = MouseEventFlags.mouseEventfLeftup;
SendInput(1, ref mouseInput, Marshal.SizeOf(new INPUT()));
}
//Below is code of the WPF MainWindow for testing. Two buttons with click event.
// For main display with screenid as 1 both setcursor position and sendinput
//work perfectly, as I get the MousePosition, but when I apply this to
//extended screen (currently with two screen, main display is screen 1 in my
//case and screen 2 is extended screen, they put the mouse at two different positions.
//I have my doubts the way I am using the extended screen Bounds.X, but
//haven't will able to fix the issue
int x = 600;
int y = 300;
private void btnSend_Click(object sender, RoutedEventArgs e)
{
SetCursorPos(SysForms.Screen.AllScreens[ScreenId - 1].Bounds.X + x, SysForms.Screen.AllScreens[screenId - 1].Bounds.Y + y);
}
private void btnSend1_Click(object sender, RoutedEventArgs e)
{
ClickLeftMouseButton(x, y);
}
Found the issue. While using SendInput, the conversion of x,y in absolute value must be done in relation to Main/Primary screen.
Thus the changes:
static int CalculateAbsoluteCoordinateX(int x, System.Drawing.Rectangle currentBounds)
{
return ((currentBounds.X + x) * 65536) / (SystemParameters.PrimaryScreenWidth);
}
static int CalculateAbsoluteCoordinateY(int y, System.Drawing.Rectangle currentBounds)
{
return (((currentBounds.Y + y) * 65536) / SystemParameters.PrimaryScreenHeight);
}
Let me start by saying that I am no stranger to using winapi calls to manipulate other windows, but this is the first time I have seen a window that has two identical control IDs. It seems that the color dialog hasn't changed much between windows versions and I can confirm that this behavior exists on all color dialogs from Windows Vista through to Windows 10 (possibly exists in win xp and lower as well but I can't be bothered to check).
What I am attempting to do is use winapi calls to localize the text in a color dialog control in C#. The best way I have found to do this is to use GetDlgItem() to get a handle to the control I wish to change and then use SetWindowText() to actually change the text. This works great for all controls on the color dialog except for the 'Basic colors:' and 'Custom colors:' labels, which both have a control ID of 0xFFFF (decimal value: 65535).
I use an app called WinID to do this type of work (I find it much easier than using Spy++) and you can see from the screenshots below that the ID of the two text labels do in-fact register as the same ID.
NOTE: I have
tested this using Spy++ and of course I get the same values as shown below:
I would like to know 2 things:
How is it possible for 2 controls to have the same control id?
Is there a 'better way' to get a handle to a control from an external dialog/app using winapi calls? Please keep in mind that using something like FindWindowEx(nColorDialogHandle, IntPtr.Zero, "Static", "&custom colors:"); works, but is not useful to me because I must be able to find the handle without relying on the text in English since this must also work on color dialogs from a non-English version of Windows.
Below is some sample code to demonstrate how I am currently able to change the text on a color dialog. I am happy with the code except that I am unable to get a direct handle to the 'Custom colors:' label since using GetDlgItem() with the control id of 0xFFFF seems to return a handle to the first instance of the control with that ID (in this case it always returns a handle to the 'Basic colors:' label). The only way I am able to get the 'Custom colors:' handle is by using an indirect method of looping through all controls on the color dialog until I find one with text that has not already been changed. This works fine but I would like to know if there is a more direct way to get this handle without looping through controls:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// Open the color dialog before the form actually loads
ColorDialogEx oColorDialog = new ColorDialogEx(this.CreateGraphics());
oColorDialog.FullOpen = true;
oColorDialog.ShowDialog();
}
}
public class ColorDialogEx : ColorDialog
{
private const Int32 WM_INITDIALOG = 0x0110; // Windows Message Constant
private Graphics oGraphics;
private const uint GW_HWNDLAST = 1;
private const uint GW_HWNDPREV = 3;
private string sColorPickerText = "1-Color Picker";
private string sBasicColorsText = "2-Basic colors:";
private string sDefineCustomColorsButtonText = "3-Define Custom Colors >>";
private string sOKButtonText = "4-OK";
private string sCancelButtonText = "5-Cancel";
private string sAddToCustomColorsButtonText = "6-Add to Custom Colors";
private string sColorText = "7-Color";
private string sSolidText = "|8-Solid";
private string sHueText = "9-Hue:";
private string sSatText = "10-Sat:";
private string sLumText = "11-Lum:";
private string sRedText = "12-Red:";
private string sGreenText = "13-Green:";
private string sBlueText = "14-Blue:";
private string sCustomColorsText = "15-Custom colors:";
// WinAPI definitions
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool SetWindowText(IntPtr hWnd, string text);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
[DllImport("user32.dll")]
public static extern long GetWindowRect(int hWnd, ref Rectangle lpRect);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetTitleBarInfo(IntPtr hwnd, ref TITLEBARINFO pti);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll")]
private static extern IntPtr GetDlgItem(IntPtr hDlg, int nIDDlgItem);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int GetClassName(IntPtr hWnd, System.Text.StringBuilder lpClassName, int nMaxCount);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[StructLayout(LayoutKind.Sequential)]
struct TITLEBARINFO
{
public const int CCHILDREN_TITLEBAR = 5;
public uint cbSize;
public RECT rcTitleBar;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = CCHILDREN_TITLEBAR + 1)]
public uint[] rgstate;
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left, Top, Right, Bottom;
public RECT(int left, int top, int right, int bottom)
{
Left = left;
Top = top;
Right = right;
Bottom = bottom;
}
public RECT(System.Drawing.Rectangle r) : this(r.Left, r.Top, r.Right, r.Bottom) { }
public int X
{
get { return Left; }
set { Right -= (Left - value); Left = value; }
}
public int Y
{
get { return Top; }
set { Bottom -= (Top - value); Top = value; }
}
public int Height
{
get { return Bottom - Top; }
set { Bottom = value + Top; }
}
public int Width
{
get { return Right - Left; }
set { Right = value + Left; }
}
public System.Drawing.Point Location
{
get { return new System.Drawing.Point(Left, Top); }
set { X = value.X; Y = value.Y; }
}
public System.Drawing.Size Size
{
get { return new System.Drawing.Size(Width, Height); }
set { Width = value.Width; Height = value.Height; }
}
public static implicit operator System.Drawing.Rectangle(RECT r)
{
return new System.Drawing.Rectangle(r.Left, r.Top, r.Width, r.Height);
}
public static implicit operator RECT(System.Drawing.Rectangle r)
{
return new RECT(r);
}
public static bool operator ==(RECT r1, RECT r2)
{
return r1.Equals(r2);
}
public static bool operator !=(RECT r1, RECT r2)
{
return !r1.Equals(r2);
}
public bool Equals(RECT r)
{
return r.Left == Left && r.Top == Top && r.Right == Right && r.Bottom == Bottom;
}
public override bool Equals(object obj)
{
if (obj is RECT)
return Equals((RECT)obj);
else if (obj is System.Drawing.Rectangle)
return Equals(new RECT((System.Drawing.Rectangle)obj));
return false;
}
public override int GetHashCode()
{
return ((System.Drawing.Rectangle)this).GetHashCode();
}
public override string ToString()
{
return string.Format(System.Globalization.CultureInfo.CurrentCulture, "{{Left={0},Top={1},Right={2},Bottom={3}}}", Left, Top, Right, Bottom);
}
}
public ColorDialogEx(Graphics g)
{
oGraphics = g;
}
protected override IntPtr HookProc(IntPtr nColorDialogHandle, int msg, IntPtr wparam, IntPtr lparam)
{
IntPtr returnValue = base.HookProc(nColorDialogHandle, msg, wparam, lparam);
if (msg == WM_INITDIALOG)
{
IntPtr[] oStaticHandleArray = new IntPtr[9];
// Change the window title
SetWindowText(nColorDialogHandle, sColorPickerText);
// Get titlebar info for calculations later
TITLEBARINFO oTITLEBARINFO = new TITLEBARINFO();
oTITLEBARINFO.cbSize = (uint)System.Runtime.InteropServices.Marshal.SizeOf(oTITLEBARINFO);
GetTitleBarInfo(nColorDialogHandle, ref oTITLEBARINFO);
// Change the text of the "Basic colors:" label
oStaticHandleArray[0] = GetDlgItem(nColorDialogHandle, 0xFFFF);
SetWindowText(oStaticHandleArray[0], sBasicColorsText);
// Change the text of the "Define Custom Colors >>" button
SetWindowText(GetDlgItem(nColorDialogHandle, 0x2CF), sDefineCustomColorsButtonText);
// Save the "OK" button size and new width
Rectangle oOKButtonRect = new Rectangle();
int nOKButtonWidth = (int)oGraphics.MeasureString(sOKButtonText, new Font("Microsoft Sans Serif", 8, FontStyle.Regular)).Width + 20; // +20 accounts for extra +10 padding on either side
// Find the "OK" Button
IntPtr nChildHandle = GetDlgItem(nColorDialogHandle, 0x1);
if (nChildHandle.ToInt32() > 0)
{
// The "OK" button was found
// Now save the current size and position
GetWindowRect(nChildHandle.ToInt32(), ref oOKButtonRect);
// We have to subtract oOKButtonRect.X value from oOKButtonRect.Width to obtain the "real" button width (same thing with subtracting Y value from Height)
oOKButtonRect.Width = oOKButtonRect.Width - oOKButtonRect.X;
oOKButtonRect.Height = oOKButtonRect.Height - oOKButtonRect.Y;
// Resize the "OK" button so that the new text fits properly
// NOTE: I cannot be sure 100% if it is correct to use the titlebar to find the position of the button or not but the math works out in all of my tests
MoveWindow(nChildHandle, oOKButtonRect.X - oTITLEBARINFO.rcTitleBar.X, oOKButtonRect.Y - oTITLEBARINFO.rcTitleBar.Y - oTITLEBARINFO.rcTitleBar.Height, nOKButtonWidth, oOKButtonRect.Height, true);
// Finally, change the button text
SetWindowText(nChildHandle, sOKButtonText);
}
// Find the "Cancel" Button
nChildHandle = GetDlgItem(nColorDialogHandle, 0x2);
if (nChildHandle.ToInt32() > 0)
{
// The "Cancel" button was found
// Now get the current size and position
Rectangle oCancelButtonRect = new Rectangle();
GetWindowRect(nChildHandle.ToInt32(), ref oCancelButtonRect);
// We have to subtract oCancelButtonRect.X value from oCancelButtonRect.Width to obtain the "real" button width (same thing with subtracting Y value from Height)
oCancelButtonRect.Width = oCancelButtonRect.Width - oCancelButtonRect.X;
oCancelButtonRect.Height = oCancelButtonRect.Height - oCancelButtonRect.Y;
// Resize the "Cancel" button so that the new text fits properly
// NOTE: I cannot be sure 100% if it correct to use the titlebar to find the position of the button or not but the math works out in all of my tests
MoveWindow(nChildHandle, oOKButtonRect.X + nOKButtonWidth - oTITLEBARINFO.rcTitleBar.X + 6, oCancelButtonRect.Y - oTITLEBARINFO.rcTitleBar.Y - oTITLEBARINFO.rcTitleBar.Height, (int)oGraphics.MeasureString(sCancelButtonText, new Font("Microsoft Sans Serif", 8, FontStyle.Regular)).Width + 20, oCancelButtonRect.Height, true);
// Finally, change the button text
SetWindowText(nChildHandle, sCancelButtonText);
}
// Change the text of the "Add to Custom Colors" button
SetWindowText(GetDlgItem(nColorDialogHandle, 0x2C8), sAddToCustomColorsButtonText);
// Change the text of the "Color" label text
oStaticHandleArray[1] = GetDlgItem(nColorDialogHandle, 0x2DA);
SetWindowText(oStaticHandleArray[1], sColorText);
// Change the text of the "Solid" label text
oStaticHandleArray[2] = GetDlgItem(nColorDialogHandle, 0x2DB);
SetWindowText(oStaticHandleArray[2], sSolidText);
// Change the text of the "Hue:" label
oStaticHandleArray[3] = GetDlgItem(nColorDialogHandle, 0x2D3);
SetWindowText(oStaticHandleArray[3], sHueText);
// Change the text of the "Sat:" label
oStaticHandleArray[4] = GetDlgItem(nColorDialogHandle, 0x2D4);
SetWindowText(oStaticHandleArray[4], sSatText);
// Change the text of the "Lum:" label
oStaticHandleArray[5] = GetDlgItem(nColorDialogHandle, 0x2D5);
SetWindowText(oStaticHandleArray[5], sLumText);
// Change the text of the "Red:" label
oStaticHandleArray[6] = GetDlgItem(nColorDialogHandle, 0x2D6);
SetWindowText(oStaticHandleArray[6], sRedText);
// Change the text of the "Green:" label
oStaticHandleArray[7] = GetDlgItem(nColorDialogHandle, 0x2D7);
SetWindowText(oStaticHandleArray[7], sGreenText);
// Change the text of the "Blue:" label
oStaticHandleArray[8] = GetDlgItem(nColorDialogHandle, 0x2D8);
SetWindowText(oStaticHandleArray[8], sBlueText);
// Change the text of the "Custom Colors:" label
SetCustomColorsText(nColorDialogHandle, oStaticHandleArray);
}
return returnValue;
}
private static string GetClassName(IntPtr nHandle)
{
// Create the stringbuilder object that is used to get the window class name from the GetClassName win api function
System.Text.StringBuilder sClassName = new System.Text.StringBuilder(100);
GetClassName(nHandle, sClassName, sClassName.Capacity);
return sClassName.ToString();
}
private static string GetWindowText(IntPtr nHandle)
{
// Create the stringbuilder object that is used to get the window text from the GetWindowText win api function
System.Text.StringBuilder sWindowText = new System.Text.StringBuilder(100);
GetWindowText(nHandle, sWindowText, sWindowText.Capacity);
return sWindowText.ToString();
}
private void SetCustomColorsText(IntPtr nHandle, IntPtr[] oStaticHandleArray)
{
// Find the last control based on the handle to the main window
IntPtr nWorkingHandle = GetWindow(FindWindowEx(nHandle, IntPtr.Zero, null, null), GW_HWNDLAST);
bool bFound = false;
do
{
// Look only for "Static" controls that we have not already changed
if (GetClassName(nWorkingHandle) == "Static" && oStaticHandleArray.Contains(nWorkingHandle) == false)
{
// Found a "Static" control
// Check to see if it is the one we are looking for
string sControlText = GetWindowText(nWorkingHandle);
if (sControlText != "")
{
// Found the "Custom Colors:" label
// Change the text of the "Custom Colors:" label
SetWindowText(nWorkingHandle, sCustomColorsText);
bFound = true;
}
}
// Working backwards we look for the previous control
nWorkingHandle = GetWindow(nWorkingHandle, GW_HWNDPREV);
// Jump out of the loop when the working handle doesn't find anymore controls
if (nWorkingHandle == IntPtr.Zero)
break;
} while (bFound == false);
}
}
}
This dialog is already localized. You can look at it with Visual Studio. Copy c:\windows\system32\en-US\comdlg32.dll.mui to, say, c:\temp\test.dll. Replace "en-US" with your local language tag. In VS use File > Open > File and pick test.dll. You'll see the resources in the MUI file, open the Dialog node and double-click the one named "CHOOSECOLOR". The resource editor opens, you can pick an item in the dialog template and look at its properties in the Property window.
Hopefully it is obvious why the STATIC control has the default IDSTATIC id (65535), there is no need for Windows to do anything to change its properties so no need to find it back. And not for you either, your user will have his own copy of the MUI file that contains the dialog with strings in his native language.
Do note that a machine usually only has MUI files for a single language. If you need to do this to, say, create screenshots for documentation then start here.
Why do I get a heigh and Width of 0 with the below:
static void Main(string[] args)
{
Process notePad = new Process();
notePad.StartInfo.FileName = "notepad.exe";
notePad.Start();
IntPtr handle = notePad.Handle;
RECT windowRect = new RECT();
GetWindowRect(handle, ref windowRect);
int width = windowRect.Right - windowRect.Left;
int height = windowRect.Bottom - windowRect.Top;
Console.WriteLine("Height: " + height + ", Width: " + width);
Console.ReadLine();
}
Here is my definition of GetWindowRect:
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
This is my definition for RECT:
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left; // x position of upper-left corner
public int Top; // y position of upper-left corner
public int Right; // x position of lower-right corner
public int Bottom; // y position of lower-right corner
}
Thanks all for any help.
You are passing a process handle to a function, GetWindowRect, that expects a window handle. Naturally, this fails. You should send Notepad.MainWindowHandle instead.
You may be querying the size before notepad has fully started up. Try this:
notePad.Start();
notePad.WaitForInputIdle(); // Waits for notepad to finish startup
IntPtr handle = notePad.Handle;
I like using pinvoke.net to sanity check all my PInvokes. GetWindowRect is described well at: http://pinvoke.net/default.aspx/user32/GetWindowRect.html