Windows Forms ComboBox DropDown Position - c#

Usually dropdown item start position aligned with ComboBox start position like the image above.
But i need to develop ComboBox Control which has the lengthy dropdown item gets aligned in middle.I mean Dropdown item left position should still more left positioned than ComboBox like the image below. Any help would be greatly appreciated.

Here is an extended ComboBox which have 2 new useful features to let you set the position and size of drop-down:
DropDownAlignment: You can set it to Left, then the drop-down will appear in it's normal position and it's left is aligned with left of control. If you set it to Middle, the middle of drop-down will be aligned with control and if you set it to Right, the right of drop-down will be aligned with right of control.
AutoWidthDropDown: If you set it to true to, then DropdownWidth will be set to the width of the longest item. If you set it to false it uses Width as DropDownWidth.
Here is appearance of drop-down after setting AutoWidthDropDown to true and DropDownAlignment to Left, Middle and Right:
Implementation - ComboBox with DropDown Position and AutoWith DropDown
You can handle WM_CTLCOLORLISTBOX message, the lparam is the handle of drop-down and then you can set position of drop-down using SetWindowPos.
Also you can calculate width of longest item and set as DropDownWidth if AutoWidthDropDown is true.
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Linq;
public class MyComboBox : ComboBox
{
private const UInt32 WM_CTLCOLORLISTBOX = 0x0134;
private const int SWP_NOSIZE = 0x1;
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter,
int X, int Y, int cx, int cy, uint uFlags);
public enum DropDownAlignments { Left = 0, Middle, Right }
public bool AutoWidthDropDown { get; set; }
public DropDownAlignments DropDownAlignment { get; set; }
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_CTLCOLORLISTBOX) {
var bottomLeft = this.PointToScreen(new Point(0, Height));
var x = bottomLeft.X;
if (DropDownAlignment == MyComboBox.DropDownAlignments.Middle)
x -= (DropDownWidth - Width) / 2;
else if (DropDownAlignment == DropDownAlignments.Right)
x -= (DropDownWidth - Width);
var y = bottomLeft.Y;
SetWindowPos(m.LParam, IntPtr.Zero, x, y, 0, 0, SWP_NOSIZE);
}
base.WndProc(ref m);
}
protected override void OnDropDown(EventArgs e)
{
if (AutoWidthDropDown)
DropDownWidth = Items.Cast<Object>().Select(x => GetItemText(x))
.Max(x => TextRenderer.MeasureText(x, Font,
Size.Empty, TextFormatFlags.Default).Width);
else
DropDownWidth = this.Width;
base.OnDropDown(e);
}
}

Related

How to align Combobox values on center, without spaces? [duplicate]

I want to align my text in combo box so that it will show in the center of combobox tell me how to do this also you can see there is a default border around a combo box when it is in focus how can i remove that border also
Kindly solve my two problems
Thanks
This article will help you: http://blog.michaelgillson.org/2010/05/18/left-right-center-where-do-you-align/
The trick is to set the DrawMode-Property of the ComboBox to OwnerDrawFixed as well as subscribe to its event DrawItem.
Your event should contain the following code:
// Allow Combo Box to center aligned
private void cbxDesign_DrawItem(object sender, DrawItemEventArgs e)
{
// By using Sender, one method could handle multiple ComboBoxes
ComboBox cbx = sender as ComboBox;
if (cbx != null)
{
// Always draw the background
e.DrawBackground();
// Drawing one of the items?
if (e.Index >= 0)
{
// Set the string alignment. Choices are Center, Near and Far
StringFormat sf = new StringFormat();
sf.LineAlignment = StringAlignment.Center;
sf.Alignment = StringAlignment.Center;
// Set the Brush to ComboBox ForeColor to maintain any ComboBox color settings
// Assumes Brush is solid
Brush brush = new SolidBrush(cbx.ForeColor);
// If drawing highlighted selection, change brush
if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
brush = SystemBrushes.HighlightText;
// Draw the string
e.Graphics.DrawString(cbx.Items[e.Index].ToString(), cbx.Font, brush, e.Bounds, sf);
}
}
}
To right align the items you can simply replace StringAlignment.Center with StringAlignment.Far.
This isn't supported for ComboBox. The exact reasons are lost in the fog of time, ComboBox has been around since the early nineties, but surely has something to do with the awkwardness of getting the text in the textbox portion to line up with the text in the dropdown. Custom drawing with DrawItem cannot solve it either, that only affects the appearance of the dropdown items.
As a possible workaround, you could perhaps do something outlandish like padding the item strings with spaces so they look centered. You'll need TextRenderer.MeasureText() to figure out how many spaces to add for each item.
The "border" you are talking about is not a border, it is the focus rectangle. You can't get rid of that either, Windows refuses to let you create a UI that won't show the control with the focus. Users that prefer the keyboard over the mouse care about that. No workaround for that one.
Set RightToLeft property to true.
It does NOT reverse the sequence of characters. It only right-justifies.
The post is a bit old but it may be still worth to say:
both requirements are possible for Windows Forms ComboBox:
Text align center (text area and the dropdown)
For the text area, find the Edit control and set the ES_CENTER style for the control.
For the dropdown items or the selected item in drop-down mode, to align text to center, just make the control owner-drawn and draw the text at center.
Get rid of focus rectangle
Make the control owner-drawn and just don't draw focus rectangle.
Example
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class MyComboBox : ComboBox
{
public MyComboBox()
{
DrawMode = DrawMode.OwnerDrawFixed;
}
[DllImport("user32.dll")]
static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
const int GWL_STYLE = -16;
const int ES_LEFT = 0x0000;
const int ES_CENTER = 0x0001;
const int ES_RIGHT = 0x0002;
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
public int Width { get { return Right - Left; } }
public int Height { get { return Bottom - Top; } }
}
[DllImport("user32.dll")]
public static extern bool GetComboBoxInfo(IntPtr hWnd, ref COMBOBOXINFO pcbi);
[StructLayout(LayoutKind.Sequential)]
public struct COMBOBOXINFO
{
public int cbSize;
public RECT rcItem;
public RECT rcButton;
public int stateButton;
public IntPtr hwndCombo;
public IntPtr hwndEdit;
public IntPtr hwndList;
}
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
SetupEdit();
}
private int buttonWidth = SystemInformation.HorizontalScrollBarArrowWidth;
private void SetupEdit()
{
var info = new COMBOBOXINFO();
info.cbSize = Marshal.SizeOf(info);
GetComboBoxInfo(this.Handle, ref info);
var style = GetWindowLong(info.hwndEdit, GWL_STYLE);
style |= 1;
SetWindowLong(info.hwndEdit, GWL_STYLE, style);
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
base.OnDrawItem(e);
e.DrawBackground();
var txt = "";
if (e.Index >= 0)
txt = GetItemText(Items[e.Index]);
TextRenderer.DrawText(e.Graphics, txt, Font, e.Bounds,
ForeColor, TextFormatFlags.Left | TextFormatFlags.HorizontalCenter);
}
}
you can do something like this by adding space before Display member in your Query
for example :
combobox1.DataSource = Query(Select col1 , (' '+col2) as Col2 from tableName)
combobox1.DisplayMember = "Col2";
combobox1.ValueMember = "col1";

Using winapi to change text on color dialog but 2 static controls have the same control id

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.

InputBox Position

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);

Why doesn't TextRenderer.MeasureText work properly?

I want to measure the height of the text given a certain width of available canvas. The text that I pass in is really long and I know will wrap. To that end, I call the following:
using System.Windows.Forms;
...
string text = "Really really long text that is sure to wrap...";
Font font = new Font("Arial", 14);
Size canvas = new Size(1100, 850);
Size size = TextRenderer.MeasureText(text, font, canvas);
No matter what I pass in for canvas, it always returns 14 for size.Height.
Am I missing something simple?
Please, use the TextFormatFlags measure parameter as shown below:
Size size = TextRenderer.MeasureText(text, font, canvas, TextFormatFlags.WordBreak);
DimitryG's solution seems to work great, but only when there is no word big enough to fill more than an entire row. If such word exists, the width will be bigger than the proposed width. There is the flag TextFormatFlags.EndEllipsis for this case, however I didn't manage to combine the flags in a way so the output is correct (if I use TextFormatFlags.WordEllipsis | TextFormatFlags.WordBreak the width is correct, but the height is not updated when Word Ellipsis takes place, which means that the big word will be trimmed, but the height will be the same as if it's not trimmed). I also tried the flag TextFormatFlags.EndEllipsis but with no results.
So until someone makes this clear, I propose using a TextBox for word wrap, and then multiply the number of lines in the TextBox with the Font's height.
Code:
int MeasureMultilineTextHeigh(string text, Font font, int proposedWidth)
{
// Exception handling.
TextBox textBox = new TextBox()
{
Multiline = true,
BorderStyle = BorderStyle.None,
Width = proposedWidth,
Font = font,
Text = text,
};
int lineCount = textBox.GetLineFromCharIndex(int.MaxValue) + 1;
int fontHeight = TextRenderer.MeasureText("X", font).Height;
return lineCount * fontHeight;
}
However this approach has one problem: If, and only if Multiline property of a TextBox is set to true, every Font will have its own left and right padding. See this stackoverflow.com question and this social.msdn.microsoft.com question for more details. So this means that in some situations the returned value might be bigger than expected. To solve this problem you can use the SetPadding function to remove the padding (you can find the method as an answer in the first question), code:
private const int EM_SETRECT = 0xB3;
[DllImport(#"User32.dll", EntryPoint = #"SendMessage", CharSet = CharSet.Auto)]
private static extern int SendMessageRefRect(IntPtr hWnd, uint msg, int wParam, ref RECT rect);
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public readonly int Left;
public readonly int Top;
public readonly int Right;
public readonly int Bottom;
private RECT(int left, int top, int right, int bottom)
{
Left = left;
Top = top;
Right = right;
Bottom = bottom;
}
public RECT(Rectangle r) : this(r.Left, r.Top, r.Right, r.Bottom) { }
}
public void SetPadding(TextBox textBox, Padding padding)
{
var rect = new Rectangle(padding.Left, padding.Top, textBox.ClientSize.Width - padding.Left - padding.Right, textBox.ClientSize.Height - padding.Top - padding.Bottom);
RECT rc = new RECT(rect);
SendMessageRefRect(textBox.Handle, EM_SETRECT, 0, ref rc);
}
int MeasureMultilineTextHeigh(string text, Font font, int proposedWidth)
{
// Exception handling.
TextBox textBox = new TextBox()
{
Multiline = true,
BorderStyle = BorderStyle.None,
Width = proposedWidth,
Font = font,
};
SetPadding(textBox, Padding.Empty);
textBox.Text = text;
int lineCount = textBox.GetLineFromCharIndex(int.MaxValue) + 1;
int fontHeight = TextRenderer.MeasureText("X", font).Height;
return lineCount * fontHeight;
}
Needed using statements:
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Runtime.InteropServices;
I hope this helps. Sorry for my English, if I made any mistake.

How to get the position of the tracker of a .NET TrackBar?

I am building a Windows Forms applicaton in C# and have a TrackBar on my Form. How can I compute the (pixel) position of the tip of the tracker? I would like to draw a line from there to another point on my form.
Additionally I also would like to compute the lowest and highest possible x position of the tip.
The native Windows control that is wrapped by TrackBar has an awkward restriction, you cannot get the positions for the first and last tic marks. You can however get the display rectangles for the channel and the slider. This class returns them:
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Runtime.InteropServices;
class MyTrackBar : TrackBar {
public Rectangle Slider {
get {
RECT rc = new RECT();
SendMessageRect(this.Handle, TBM_GETTHUMBRECT, IntPtr.Zero, ref rc);
return new Rectangle(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top);
}
}
public Rectangle Channel {
get {
RECT rc = new RECT();
SendMessageRect(this.Handle, TBM_GETCHANNELRECT, IntPtr.Zero, ref rc);
return new Rectangle(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top);
}
}
private const int TBM_GETCHANNELRECT = 0x400 + 26;
private const int TBM_GETTHUMBRECT = 0x400 + 25;
private struct RECT { public int left, top, right, bottom; }
[DllImport("user32.dll", EntryPoint = "SendMessageW")]
private static extern IntPtr SendMessageRect(IntPtr hWnd, int msg, IntPtr wp, ref RECT lp);
}
Here is a sample usage in a form, it draws a line from the first tick mark to the slider pointer:
private void myTrackBar1_ValueChanged(object sender, EventArgs e) {
this.Invalidate();
}
protected override void OnPaint(PaintEventArgs e) {
var chan = this.RectangleToClient(myTrackBar1.RectangleToScreen(myTrackBar1.Channel));
var slider = this.RectangleToClient(myTrackBar1.RectangleToScreen(myTrackBar1.Slider));
e.Graphics.DrawLine(Pens.Black, chan.Left + slider.Width / 2, myTrackBar1.Bottom + 5,
slider.Left + slider.Width / 2, myTrackBar1.Bottom + 5);
}

Categories

Resources