vbAccelerator - Contents of code file: TextBoxIconCS_TextBoxMarginCustomise.csusing System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace vbAccelerator.Components.Controls
{
/// <summary>
/// A class for adding right and left margins to a TextBox
/// or the embedded TextBox within a DropDown ComboBox.
/// The margin can display an icon, a control or can
/// be drawn using a custom routine implemented by the
/// user.
/// </summary>
public class TextBoxMarginCustomise : NativeWindow
{
#region UnmanagedMethods
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[DllImport("user32", CharSet=CharSet.Auto)]
private extern static int SendMessage(
IntPtr hwnd,
int wMsg,
int wParam,
int lParam);
[DllImport("user32", CharSet=CharSet.Auto)]
private extern static IntPtr FindWindowEx(
IntPtr hwndParent,
IntPtr hwndChildAfter,
[MarshalAs(UnmanagedType.LPTStr)]
string lpszClass,
[MarshalAs(UnmanagedType.LPTStr)]
string lpszWindow);
[DllImport("user32", CharSet=CharSet.Auto)]
private extern static int GetWindowLong(
IntPtr hWnd,
int dwStyle);
[DllImport("user32")]
private extern static IntPtr GetDC(
IntPtr hwnd);
[DllImport("user32")]
private extern static int ReleaseDC (
IntPtr hwnd,
IntPtr hdc);
[DllImport("user32")]
private extern static int GetClientRect (
IntPtr hwnd,
ref RECT rc);
[DllImport("user32")]
private extern static int GetWindowRect (
IntPtr hwnd,
ref RECT rc);
private const int EC_LEFTMARGIN = 0x1;
private const int EC_RIGHTMARGIN = 0x2;
private const int EC_USEFONTINFO = 0xFFFF;
private const int EM_SETMARGINS = 0xD3;
private const int EM_GETMARGINS = 0xD4;
private const int WM_PAINT = 0xF;
private const int WM_SETFOCUS = 0x7;
private const int WM_KILLFOCUS = 0x8;
private const int WM_SETFONT = 0x30;
private const int WM_MOUSEMOVE = 0x200;
private const int WM_LBUTTONDOWN = 0x201;
private const int WM_RBUTTONDOWN = 0x204;
private const int WM_MBUTTONDOWN = 0x207;
private const int WM_LBUTTONUP = 0x202;
private const int WM_RBUTTONUP = 0x205;
private const int WM_MBUTTONUP = 0x208;
private const int WM_LBUTTONDBLCLK = 0x203;
private const int WM_RBUTTONDBLCLK = 0x206;
private const int WM_MBUTTONDBLCLK = 0x209;
private const int WM_KEYDOWN = 0x0100;
private const int WM_KEYUP = 0x0101;
private const int WM_CHAR = 0x0102;
private const int GWL_EXSTYLE = (-20);
private const int WS_EX_RIGHT = 0x00001000;
private const int WS_EX_LEFT = 0x00000000;
private const int WS_EX_RTLREADING = 0x00002000;
private const int WS_EX_LTRREADING = 0x00000000;
private const int WS_EX_LEFTSCROLLBAR = 0x00004000;
private const int WS_EX_RIGHTSCROLLBAR = 0x00000000;
#endregion
#region Member Variables
private System.Windows.Forms.ImageList imageList = null;
private int icon = -1;
private Control control = null;
private ITextBoxMarginCustomisePainter customPainter = null;
private int customWidth = 0;
#endregion
/// <summary>
/// Gets whether a Window is <c>RightToLeft.Yes</c> from
/// its <c>Handle</c>.
/// </summary>
/// <param name="handle">Handle of window to check.</param>
/// <returns><c>True</c> if Window is RightToLeft, <c>False</c>
otherwise.</returns>
private static bool IsRightToLeft(IntPtr handle)
{
int style = GetWindowLong(handle, GWL_EXSTYLE);
return (
((style & WS_EX_RIGHT) == WS_EX_RIGHT) ||
((style & WS_EX_RTLREADING) == WS_EX_RTLREADING) ||
((style & WS_EX_LEFTSCROLLBAR) == WS_EX_LEFTSCROLLBAR));
}
/// <summary>
/// Gets the far margin of a TextBox control or the
/// TextBox contained within a ComboBox.
/// </summary>
/// <param name="ctl">The Control to get the far margin
/// for</param>
/// <returns>Far margin, in pixels.</returns>
public static int FarMargin(Control ctl)
{
IntPtr handle = ctl.Handle;
if
(typeof(System.Windows.Forms.ComboBox).IsAssignableFrom(ctl.GetType())
)
{
handle = ComboEdithWnd(handle);
}
return FarMargin(handle);
}
private static int FarMargin(IntPtr handle)
{
int farMargin = SendMessage(handle, EM_GETMARGINS, 0, 0);
if (IsRightToLeft(handle))
{
farMargin = farMargin & 0xFFFF;
}
else
{
farMargin = (farMargin / 0x10000);
}
return farMargin;
}
/// <summary>
/// Sets the far margin of a TextBox control or the
/// TextBox contained within a ComboBox.
/// </summary>
/// <param name="ctl">The Control to set the far margin
/// for</param>
/// <param name="margin">New far margin in pixels, or -1
/// to use the default far margin.</param>
public static void FarMargin(Control ctl, int margin)
{
IntPtr handle = ctl.Handle;
if
(typeof(System.Windows.Forms.ComboBox).IsAssignableFrom(ctl.GetType())
)
{
handle = ComboEdithWnd(handle);
}
FarMargin(handle, margin);
}
private static void FarMargin(IntPtr handle, int margin)
{
int message = IsRightToLeft(handle) ? EC_LEFTMARGIN : EC_RIGHTMARGIN;
if (message == EC_LEFTMARGIN)
{
margin = margin & 0xFFFF;
}
else
{
margin = margin * 0x10000;
}
SendMessage(handle, EM_SETMARGINS, message, margin);
}
/// <summary>
/// Gets the near margin of a TextBox control or the
/// TextBox contained within a ComboBox.
/// </summary>
/// <param name="ctl">The Control to get the near margin
/// for</param>
/// <returns>Near margin, in pixels.</returns>
public static int NearMargin(Control ctl)
{
IntPtr handle = ctl.Handle;
if
(typeof(System.Windows.Forms.ComboBox).IsAssignableFrom(ctl.GetType())
)
{
handle = ComboEdithWnd(handle);
}
return NearMargin(handle);
}
private static int NearMargin(IntPtr handle)
{
int nearMargin = SendMessage(handle, EM_GETMARGINS, 0, 0);
if (IsRightToLeft(handle))
{
nearMargin = nearMargin / 0x10000;
}
else
{
nearMargin = nearMargin & 0xFFFF;
}
return nearMargin;
}
/// <summary>
/// Sets the near margin of a TextBox control or the
/// TextBox contained within a ComboBox.
/// </summary>
/// <param name="ctl">The Control to set the near margin
/// for</param>
/// <param name="margin">New near margin in pixels, or -1
/// to use the default near margin.</param>
public static void NearMargin(Control ctl, int margin)
{
IntPtr handle = ctl.Handle;
if
(typeof(System.Windows.Forms.ComboBox).IsAssignableFrom(ctl.GetType())
)
{
handle = ComboEdithWnd(handle);
}
NearMargin(handle, margin);
}
private static void NearMargin(IntPtr handle, int margin)
{
int message = IsRightToLeft(handle) ? EC_RIGHTMARGIN : EC_LEFTMARGIN;
if (message == EC_LEFTMARGIN)
{
margin = margin & 0xFFFF;
}
else
{
margin = margin * 0x10000;
}
SendMessage(handle, EM_SETMARGINS, message, margin);
}
/// <summary>
/// Gets the handle of the TextBox contained within a
/// ComboBox control.
/// </summary>
/// <param name="handle">The ComboBox window handle.</param>
/// <returns>The handle of the contained text box.</returns>
public static IntPtr ComboEdithWnd(IntPtr handle)
{
handle = FindWindowEx(handle, IntPtr.Zero, "EDIT", "\0");
return handle;
}
/// <summary>
/// Attaches an instance of this class to a ComboBox control.
/// The control must have the "DropDown" style so it has a
/// TextBox.
/// </summary>
/// <param name="comboBox">ComboBox with DropDown style to
/// attach to.</param>
/// <remarks>Use the <see
cref="System.Windows.Forms.NativeWindow.AssignHandle"/> method to attach
/// this class to an arbitrary TextBox control using its
/// handle.</remarks>
public void Attach(System.Windows.Forms.ComboBox comboBox)
{
if (!this.Handle.Equals(IntPtr.Zero))
{
this.ReleaseHandle();
}
IntPtr handle = ComboEdithWnd(comboBox.Handle);
this.AssignHandle(handle);
setMargin();
}
/// <summary>
/// Attaches an instance of this class to a TextBox control.
/// </summary>
/// <param name="textBox">TextBox to attach to.</param>
/// <remarks>Use the <see
cref="System.Windows.Forms.NativeWindow.AssignHandle"/> method to attach
/// this class to an arbitrary TextBox control using its
/// handle.</remarks>
public void Attach(System.Windows.Forms.TextBox textBox)
{
if (!this.Handle.Equals(IntPtr.Zero))
{
this.ReleaseHandle();
}
this.AssignHandle(textBox.Handle);
setMargin();
}
/// <summary>
/// Gets/sets the ImageList used to draw icons in the control.
/// </summary>
public System.Windows.Forms.ImageList ImageList
{
get
{
return this.imageList;
}
set
{
this.imageList = value;
setMargin();
}
}
/// <summary>
/// Gets/sets the 0-based icon index to draw in the control.
/// Values < 0 have special meanings. -1 erases the
/// icon, and < -1 draws a colour sample box.
/// </summary>
public int Icon
{
get
{
return this.icon;
}
set
{
this.icon = value;
setMargin();
}
}
/// <summary>
/// Gets/sets the control to be displayed in the near margin.
/// The <see cref="ImageList"/> property must be <c>null</c> if you
/// want to display a control.
/// </summary>
public System.Windows.Forms.Control Control
{
get
{
return this.control;
}
set
{
this.control = value;
setMargin();
}
}
/// <summary>
/// Gets/sets a class which implements the <see
cref="ITextBoxMarginCustomisePainter "/>
/// interface to perform customised painting in the margin.
/// The <see cref="ImageList"/> and <see cref="Control"/> properties must
/// be <c>null</c> if you want to use this technique.
/// </summary>
public ITextBoxMarginCustomisePainter CustomPainter
{
get
{
return this.customPainter;
}
set
{
this.customPainter = value;
setMargin();
}
}
/// <summary>
/// Sets the near margin to accommodate the specified control.
/// </summary>
private void setMargin()
{
if (!this.Handle.Equals(IntPtr.Zero))
{
if (this.control != null)
{
NearMargin(this.Handle, this.control.Width + 4);
moveControl();
}
else if (this.imageList != null)
{
NearMargin(this.Handle, this.imageList.ImageSize.Width + 4);
}
else if (this.customPainter != null)
{
this.customWidth = this.customPainter.GetMarginWidth();
NearMargin(this.Handle, this.customWidth);
}
}
}
/// <summary>
/// Moves the contained control to the appropriate
/// position
/// </summary>
private void moveControl()
{
if (this.control != null)
{
Point currentLocation = this.control.Location;
RECT rcWindow = new RECT();
GetWindowRect(this.Handle, ref rcWindow);
Point moveTo = new Point(rcWindow.left + 2,
rcWindow.top + ((rcWindow.bottom - rcWindow.top) -
this.control.Height) / 2);
if (IsRightToLeft(this.Handle))
{
moveTo.X = rcWindow.right - this.control.Width - 2;
}
moveTo = this.control.Parent.PointToClient(moveTo);
if (!currentLocation.Equals(moveTo))
{
this.control.Location = moveTo;
this.control.BringToFront();
}
}
}
/// <summary>
/// Calls the base WndProc and performs WM_PAINT
/// processing to draw the icon if necessary.
/// </summary>
/// <param name="m">Windows Message</param>
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (this.control == null)
{
switch (m.Msg)
{
case WM_SETFONT:
setMargin();
break;
case WM_PAINT:
RePaint();
break;
case WM_SETFOCUS:
case WM_KILLFOCUS:
RePaint();
break;
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
RePaint();
break;
case WM_LBUTTONUP:
case WM_RBUTTONUP:
case WM_MBUTTONUP:
RePaint();
break;
case WM_LBUTTONDBLCLK:
case WM_RBUTTONDBLCLK:
case WM_MBUTTONDBLCLK:
RePaint();
break;
case WM_KEYDOWN:
case WM_CHAR:
case WM_KEYUP:
RePaint();
break;
case WM_MOUSEMOVE:
if (!m.WParam.Equals(IntPtr.Zero))
{
RePaint();
}
break;
}
}
else
{
switch (m.Msg)
{
case WM_PAINT:
moveControl();
break;
}
}
}
/// <summary>
/// Paints the control if necessary:
/// </summary>
private void RePaint()
{
if ((this.ImageList != null) ||
(this.customPainter != null))
{
RECT rcClient = new RECT();
GetClientRect(this.Handle, ref rcClient);
bool rightToLeft = IsRightToLeft(this.Handle);
IntPtr handle = this.Handle;
IntPtr hdc = GetDC(handle);
Graphics gfx = Graphics.FromHdc(hdc);
if (this.customPainter == null)
{
int itemSize = this.imageList.ImageSize.Height;
Point pt = new Point(0, rcClient.top + (rcClient.bottom -
rcClient.top - itemSize) / 2);
if (rightToLeft)
{
pt.X = rcClient.right - itemSize - 2;
}
else
{
pt.X = 2;
}
if (this.icon > -1)
{
gfx.SetClip(new Rectangle(pt.X, pt.Y,
this.imageList.ImageSize.Width,
this.imageList.ImageSize.Height - 4));
this.imageList.Draw(gfx, pt, icon);
gfx.ResetClip();
}
else
{
gfx.FillRectangle(SystemBrushes.Window, pt.X, pt.Y,
this.imageList.ImageSize.Width,
this.imageList.ImageSize.Height + 1);
}
}
else
{
Rectangle rcDraw = new Rectangle(
new Point(0, 0), new Size(this.customWidth, rcClient.bottom -
rcClient.top));
this.customPainter.Draw(gfx, rcDraw, rightToLeft);
}
gfx.Dispose();
ReleaseDC(handle, hdc);
}
}
/// <summary>
/// Constructs a new instance of this class
/// </summary>
public TextBoxMarginCustomise()
{
// intentionally blank
}
}
/// <summary>
/// An interface which users of the <see cref="TextBoxMarginCustomise"/>
/// class can use to provide a customised painting routine for the
/// margin area. Create an instance of this class and install it
/// using the <see cref="TextBoxMarginCustomise.CustomPainter"/>
/// accessor.
/// </summary>
public interface ITextBoxMarginCustomisePainter
{
/// <summary>
/// Called to obtain the width of the margin.
/// </summary>
/// <returns>Width of the margin</returns>
int GetMarginWidth();
/// <summary>
/// Called whenever the margin area needs to
/// be repainted.
/// </summary>
/// <param name="gfx">Graphics object to paint on.</param>
/// <param name="rcDraw">Boundary of margin area.</param>
/// <param name="rightToLeft">Whether the control is right
/// to left or not</param>
void Draw(Graphics gfx, Rectangle rcDraw, bool rightToLeft);
}
}
|
|