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

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

Related

How to add Caret to a Form control?

On the goal to learn how to create C# Form components. I decided to create a label which the text can be selected with caret showing the current selection position. Using the code below i successfully added the text with custom color and font. But the caret is not showing. I used CreateCaret and ShowCaret of user32 to create and display the caret.
class MyCustomLabel : Control
{
string[] lines;
public MyCustomLabel ()
{
lines = new string[] {
"public class MyApp {",
" System.Console.WriteLine(\"Hello,World\");",
"}"};
CreateCaret(Handle, IntPtr.Zero, 10, Height);
ShowCaret(Handle);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
StringFormat fmt = StringFormat.GenericTypographic;
fmt.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces;
for (int i = 0; i < lines.Length; ++i)
e.Graphics.DrawString(lines[i], Font, new SolidBrush(this.ForeColor),
0, Font.Height * i, StringFormat.GenericTypographic);
}
[DllImport("user32")]
private extern static bool ShowCaret(IntPtr hwnd);
[DllImport("user32")]
private extern static int CreateCaret(IntPtr hwnd, IntPtr hBitmap, int width, int height);
}
At first place i want to have the Caret displayed and it will be appreciate if there is an example with selection.

Windows Forms ComboBox DropDown Position

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

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.

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.

Disable WinForms ProgressBar animation

Is there a possbility to disable animation of the progress bar?
I need it for some pocess which is paused and not running at the moment. An average user would think the process is running if the progress bar is being blinking.
The advice to create own progress bar control is not what I'm looking for.
You can use the Vista progress bar's paused state, like this:
// Assuming a Form1 with 3 ProgressBar controls
private void Form1_Load(object sender, EventArgs e)
{
SendMessage(progressBar2.Handle,
0x400 + 16, //WM_USER + PBM_SETSTATE
0x0003, //PBST_PAUSED
0);
SendMessage(progressBar3.Handle,
0x400 + 16, //WM_USER + PBM_SETSTATE
0x0002, //PBST_ERROR
0);
}
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
static extern uint SendMessage(IntPtr hWnd,
uint Msg,
uint wParam,
uint lParam);
My workaround was to use a Panel Control instead of ProgressBar. I changed BackColor, BorderStyle (to Fixed3D) and I manipulate its Width to display needed level of progress. I assumed that 100% of progress is equal to Width of the Form.
The standard means of communicating to a user that an action is either paused or can't be accurately measured is to use the marquee display style.
progressBar1.Style = ProgressBarStyle.Marquee;
This style ignores the Maximum and Value properties and displays a progress bar "segment" that continually moves across the progress bar and loops around (it doesn't fill the progress bar, it moves what looks like a section of the bar all the way across the control and around to the beginning again.)
What you will want to do is set the style on this control specifically to override the theme changes. This article gives you a bit of information.
You could override the OnPaint() of the progressbar. You don't actually need to rewrite the whole thing, you just have to inherit the progressbar and override OnPaint like this:
public class my_progress_bar : ProgressBar {
public Brush brush;
public my_progress_bar() {
this.SetStyle(ControlStyles.UserPaint, true);
brush = Brushes.ForestGreen;
}
protected override void OnPaint(PaintEventArgs e)
{
//base.OnPaint(e);
Rectangle rectangle = e.ClipRectangle;
rectangle.Width = (int)(rectangle.Width * ((double)Value / Maximum)) - 4;
ProgressBarRenderer.DrawHorizontalBar(e.Graphics, e.ClipRectangle);
rectangle.Height = Height - 4;
e.Graphics.FillRectangle(brush, 2, 2, rectangle.Width, rectangle.Height);
}
}
Paste this as to code. This will rewrite the progress bar, it's also customizable to color.
public class CProgressBar : ProgressBar
{
public Color MyColor {
get { return _color; }
set {
_color = value;
MyBrush = new SolidBrush(_color);
Invalidate();
}
}
private Color _color = Color.Green;
public CProgressBar()
{
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);
}
public int Value {
get { return _value; }
set {
_value = value;
Invalidate();
}
}
private int _value;
private SolidBrush MyBrush = new SolidBrush(_color);
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.FillRectangle(MyBrush, new Rectangle(0, 0, Width * (_value / Maximum), Height));
}
}

Categories

Resources