继承control的自定义TextBox

  下面来介绍一下本人写的一个自定义的textbox,首先说一下写这个控件遇到的几个难点:第一、关联输入法;第二、画字符串和焦点线

  先随便上两张效果图吧:

  

  下面这三个类是来自于网络某位高人的,不记得名字了,拿来用,代码看了一下,给有些地方没注释的加上了注释。

  

 
/// <summary>
/// 提供Unmanaged方法处理Windows Message并接收输入法的输入信号。
/// </summary>
public class ImeWinMessageHandler
{
private Control _tarForm = null;
private IntPtr _tarHandle = IntPtr.Zero;
private IntPtr _hIMC = IntPtr.Zero; /// <summary>
/// 取得一个值。这个值会指示哪一个辅助按键(Modifier Key)(Shift、Ctrl和Alt)处于按下的状态。
/// </summary>
public static Keys ModifierKeys
{
get
{
Keys m_Result = Keys.None;
if (NativeMethods.GetKeyState(0x10) < 0)
m_Result |= Keys.Shift;
if (NativeMethods.GetKeyState(0x11) < 0)
m_Result |= Keys.Control;
if (NativeMethods.GetKeyState(0x12) < 0)
m_Result |= Keys.Alt;
return m_Result;
}
}
/// <summary>
/// 初始化OptimistSutdioDll.Win32APIs.ImeWinMessageHandler的执行个体。
/// </summary>
/// <param name="form">用于处理Windows Message的 System.Windows.Forms.Form 执行个体。</param>
public ImeWinMessageHandler(Control form)
{
_tarForm = form;
}
private Boolean _Enabled = false;
/// <summary>
/// 取得或设置Handler是否启用。
/// </summary>
public Boolean Enabled
{
get { return _Enabled; }
set
{
if (Enabled != value)
{
_Enabled = value;
if (value)
{
_tarHandle = _tarForm.Handle;
_hIMC = NativeMethods.ImmGetContext(_tarHandle);
}
else
{
NativeMethods.ImmReleaseContext(_tarHandle, _hIMC);
_tarHandle = IntPtr.Zero;
_hIMC = IntPtr.Zero;
}
}
}
}
/// <summary>
/// 将Windows Message资料送入处理器进行处理。
/// </summary>
/// <param name="m">要处理 System.Windows.Forms.Message。</param>
public void InputMessage(ref Message m)
{
bool m_handled = false;
WndProc(m.HWnd, m.Msg, m.WParam, m.LParam, ref m_handled);
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (Enabled)
{
if (msg == NativeContansts.WM_IME_SETCONTEXT && wParam.ToInt32() == 1)
NativeMethods.ImmAssociateContext(_tarHandle, _hIMC);
else if (msg == NativeContansts.WM_CHAR)
{
char m_Reult = (char)(new KeyEventArgs(((Keys)((int)(wParam))) | ImeWinMessageHandler.ModifierKeys).KeyData);
this.OnCharacterInputted(m_Reult);
}
}
return IntPtr.Zero;
}
private void OnCharacterInputted(char c)
{
if (this.CharacterInputted != null)
this.CharacterInputted(this, new CharacterInputtedEventArgs(c));
}
/// <summary>
/// 当Handler收到输入法输入字元进入应用程式时引发此事件。
/// </summary>
public event EventHandler<CharacterInputtedEventArgs> CharacterInputted;
}
/// <summary>
/// 提供 CharacterInputted的事件资料。
/// </summary>
public class CharacterInputtedEventArgs : EventArgs
{
/// <summary>
/// 取得输入法输入的字元。
/// </summary>
public Char Character { get; private set; }
internal CharacterInputtedEventArgs(char c)
{
this.Character = c;
}
}
internal static class NativeMethods
{
/// <summary>
/// 获取当前正在输入的窗口的输入法句柄
/// </summary>
/// <param name="hWnd">要获取输入法的窗口句柄</param>
/// <returns></returns>
[DllImport("Imm32.dll")]
public static extern IntPtr ImmGetContext(IntPtr hWnd);
/// <summary>
/// 将指定的窗口和指定的输入法联系起来
/// </summary>
/// <param name="hWnd">指定的窗口</param>
/// <param name="hIMC">指定的输入法</param>
/// <returns></returns>
[DllImport("Imm32.dll")]
public static extern IntPtr ImmAssociateContext(IntPtr hWnd, IntPtr hIMC);
/// <summary>
/// 释放输入法和解锁相关联的内存
/// </summary>
/// <param name="hWnd">指定的有输入法的窗口</param>
/// <param name="hIMC">关联的输入法</param>
/// <returns></returns>
[DllImport("Imm32.dll")]
public static extern Boolean ImmReleaseContext(IntPtr hWnd, IntPtr hIMC);
/// <summary>
/// 指定虚拟键的状态
/// </summary>
/// <param name="keyCode">GetKeyState(VK_SHIFT) > 0 没按下GetKeyState(VK_SHIFT)小于0被按下</param>
/// <returns></returns>
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
public static extern short GetKeyState(int keyCode);
}
internal static class NativeContansts
{
public const int WM_IME_SETCONTEXT = 0x0281;
public const int WM_IME_COMPOSITION = 0x010F;
public const int WM_CHAR = 0x0102;
public const int GCS_COMPSTR = 0x0008;
}

  这三个类帮助我们把输入法跟控件关联起来,其中调用的四个API我都有注释,应该不难理解!

  代码基本上该注释的地方都有注释,有不理解的大家可以回复,有时间给大家讲解!

  接下看控件代码:

  第一、如何调用输入法的三个类跟控件关联

  

ImeWinMessageHandler ImeHandler;
public TxtBox()
{
InitializeComponent();

ImeHandler = new ImeWinMessageHandler(this);
ImeHandler.CharacterInputted += new EventHandler<CharacterInputtedEventArgs>(ImeHandler_CharacterInputted);
ImeHandler.Enabled = true;

}
private void ImeHandler_CharacterInputted(object sender, CharacterInputtedEventArgs e)
{
if (e.Character == '\b' && this.Text.Length > 0)
{
if (this.Text != prompt)
{
this.Text = this.Text.Remove(this.Text.Length - 1);
}
if (this.Text.Length == 0)
{ this.Text = prompt; }
}
else
{
if (e.Character != (char)3)//双击会产生3的ASCII码
{
if (this.Text.Length >= maxLength && maxLength != 0)
{
//this.Text = this.Text.Substring(this.Text.Length - 16, 16);
this.Text = this.Text;
}//这里可以设置最多输入多少个字符
else
{ this.Text += e.Character; }
}
}
}

  第二、设置控件的属性

  

#region property
private int alpha;
[CategoryAttribute("A参数设置"), Description("设置控件的透明度(0-255)!")]
public int Alpha
{
get { return alpha; }
set
{
if (value >= 0 && value <= 255)
{
if (alpha == value) return;
alpha = value;
this.Invalidate();
}
else
{ MessageBox.Show("透明度应该是0到255的整数"); }
}
}
private Color alphaBackColor;
[CategoryAttribute("A参数设置"), Description("设置控件的透明颜色!")]
public Color AlphaBackColor
{
get { return alphaBackColor; }
set {
if (alphaBackColor == value) return;
alphaBackColor = value;
this.Invalidate();
}
}//没用背景色,原因是用背景色会出现运行后一直是一种颜色,没找到原因,故自己自定义颜色属性
private int maxLength;
[CategoryAttribute("A参数设置"), Description("设置控件可输入的最大字符数!")]
public int MaxLength
{
get { return maxLength; }
set {
if (maxLength == value) return;
maxLength = value;
}
}
private Char passwordChar;
[CategoryAttribute("A参数设置"), Description("设置控件密码输入显示字符!")]
public Char PasswordChar
{
get { return passwordChar; }
set {
if (passwordChar == value) return;
passwordChar = value;
}
}
private string prompt;
[CategoryAttribute("A参数设置"), Description("设置控件Text!")]
public string Prompt
{
get
{
return this.prompt;
}
set
{
if (prompt == value) return;
prompt = value;
this.Text = prompt;
this.Invalidate();
}
}
private float cornerRadius;
[CategoryAttribute("A参数设置"), Description("设置控件圆角半径大小!")]
public float CornerRadius
{
get { return cornerRadius; }
set {
if (cornerRadius == value) return;
cornerRadius = value;
this.Invalidate();
}
}
[Browsable(false)]
public new Color BackColor
{
get { return base.BackColor; }
set { base.BackColor = value; }
}
[Browsable(false)]
public new string Text
{
get { return base.Text; }
set { base.Text = value; }
}
#endregion

  第三、重写某些事件

  

protected override void OnClick(EventArgs e)
{
this.Focus();
isFocus = true;
if (this.Text == prompt)
{ this.Text = ""; }
this.Invalidate(validRect);//为了只在矩形内显示文字
base.OnClick(e);
}
protected override void OnCreateControl()
{
Thread threadInvalidate = new Thread(methodInvalidate);
threadInvalidate.IsBackground = true;
threadInvalidate.Start();
base.OnCreateControl();
}
protected override void OnLostFocus(EventArgs e)
{
isFocus = false;
if (this.Text == "")
{ this.Text = prompt; }
this.Invalidate();
base.OnLostFocus(e);
}

private void methodInvalidate()
{
try
{
while (true)
{
if (isFocus)
{
this.Invalidate(validRect);//为了只在矩形内显示文字
if (isTwinkle)
{ isTwinkle = false; }
else
{ isTwinkle = true; }
}
Thread.Sleep(500);//每隔500ms就更新一下isTwinkle的布尔值,画焦点线
}
}
catch
{ }
}

  第四、重写onpaint,重点

  

protected override void OnPaint(PaintEventArgs e)
{
//---------------显示的区域------------------------
GraphicsPath gp = new GraphicsPath();
if (cornerRadius == 0)
{
//gp.AddArc(new Rectangle(0, 0, this.Height, this.Height), 90.0f, 180.0f);
//gp.AddArc(new Rectangle(this.Width - this.Height, 0, this.Height, this.Height), -90.0f, 180.0f);
//gp.CloseFigure();
gp.AddRectangle(new Rectangle(0,0,this.Width,this.Height));
}
else
{
gp = GetRoundedCorner(cornerRadius, new Rectangle(0, 0, this.Width, this.Height));
}
//-------------------------------------------------

//--------------画笔属性---------------------------
Graphics g = e.Graphics;
g.SmoothingMode = SmoothingMode.HighQuality;
g.CompositingQuality = CompositingQuality.HighQuality;
g.PageUnit = GraphicsUnit.Pixel;
//-------------------------------------------------

Brush b = new SolidBrush(Color.FromArgb(alpha, AlphaBackColor));//用指定的透明度和颜色定义brush
Pen p=new Pen(b);//用指定的brush定义画笔
g.FillPath(b,gp);//用指定的brush填出要显示的区域

//SizeF sf = g.MeasureString(this.Text, this.Font);//获取字符串的宽度高度
Size s = TextRenderer.MeasureText(this.Text, this.Font);
string tempText = this.Text;//把文本信息赋值给临时变量,画密码符号时用到并且要获取控件的文本信息用Text属性获取也不会变成了密码符号
//---------------------更改文本信息为密码符号----------------------
if (passwordChar != '\0'&&tempText!=prompt)//\0为空字符 char
{
foreach (Char c in tempText.ToCharArray())
{
tempText = tempText.Replace(c, passwordChar);
}
}
//--------------------------------------------------------------

//------------------画字符串-------------------------------------
if (s.Width > this.Width - cornerRadius*2)//如果字符串宽度大于要显示的矩形区域,那么重新计算画字符串的起始坐标点。
{
g.DrawString(tempText, this.Font, Brushes.Ivory,
new PointF(-(s.Width-(this.Width-cornerRadius/2)),(Convert.ToSingle(this.Height)-this.Font.Height)/2));//
}
else
{
g.DrawString(tempText, this.Font, Brushes.Ivory, new PointF(cornerRadius/2, (Convert.ToSingle(this.Height) - this.Font.Height) / 2));
}
//---------------------------------------------------------------
if (this.Focused && isTwinkle)
{
if (s.Width > this.Width - cornerRadius)//此时只在矩形最右侧画线
{
g.DrawLine(Pens.Ivory, new PointF(this.Width - cornerRadius/2-1, 6), new PointF(this.Width - cornerRadius/2-1, this.Height - 6));
}
else
{
g.DrawLine(Pens.Ivory, new PointF(cornerRadius / 2 + s.Width, 6), new PointF(cornerRadius / 2 + s.Width, this.Height - 6));
}
}
base.OnPaint(e);
}

  总结:其中画字符串的时候当超过矩形区域,隐藏前面的,显示刚写上去的字符的时候,用SizeF sf = g.MeasureString(this.Text, this.Font);测长度画会随着字符串的长度变化而与焦点线之间的距离越来越大,Size s = TextRenderer.MeasureText(this.Text, this.Font);这种测量没有发现什么问题。

  代码中有些地方大家看了肯定有觉得不美或者有需要修改改进的地方,希望大家提出,我们共同进步。

  附上总的工程:

  http://my.csdn.net/wll929187348  由于不知道博客园如何上传资源,想要资源的到这里面可以下载

继承control的自定义TextBox的更多相关文章

  1. WPF自定义TextBox及ScrollViewer

    原文:WPF自定义TextBox及ScrollViewer 寒假过完,在家真心什么都做不了,可能年龄大了,再想以前那样能专心坐下来已经不行了.回来第一件事就是改了项目的一个bug,最近又新增了一个新的 ...

  2. 自定义textbox加入左右晃动效果

    应用开发过程中经常会要求用户在textbox进行输入.例如:登陆,发布. 而一般没进行输入的时候我们都会简单的进行弹窗提示用户输入. 前阵子ios的同学搞了一个左右晃动的效果,觉得还不错,于是也搞了个 ...

  3. .Net 配置文件--继承ConfigurationSection实现自定义处理类处理自定义配置节点

    除了使用继承IConfigurationSectionHandler的方法定义处理自定义节点的类,还可以通过继承ConfigurationSection类实现同样效果. 首先说下.Net配置文件中一个 ...

  4. .Net 配置文件——继承ConfigurationSection实现自定义处理类处理自定义配置节点

    除了使用继承IConfigurationSectionHandler的方法定义处理自定义节点的类,还可以通过继承ConfigurationSection类实现同样效果. 首先说下.Net配置文件中一个 ...

  5. android继承Dialog实现自定义对话框

    有时需要自定义对话框,可以使用AlterDialog.Bulider,比如下面的代码片段 new AlertDialog.Builder(self) .setTitle("标题") ...

  6. Swift - 继承UIView实现自定义可视化组件(附记分牌样例)

    在iOS开发中,如果创建一个自定义的组件通常可以通过继承UIView来实现.下面以一个记分牌组件为例,演示了组件的创建和使用,以及枚举.协议等相关知识的学习. 效果图如下:    组件代码:Score ...

  7. WPF 自定义TextBox,可控制键盘输入内容

    非原创,整理之前的代码的时候找出来的,可用,与大家分享一下! public class NumbericBoxWithZero : NumericBox { public NumbericBoxWit ...

  8. WPF 自定义TextBox

    1.TextBox前加图标. 效果: <TextBox Width="300" Height="30" Style="{StaticResour ...

  9. Azure ARM (17) 基于角色的访问控制 (Role Based Access Control, RBAC) - 自定义Role

    <Windows Azure Platform 系列文章目录> 在上面一篇博客中,笔者介绍了如何在RBAC里面,设置默认的Role. 这里笔者将介绍如何使用自定的Role. 主要内容有: ...

随机推荐

  1. 批处理获取IP地址

    setlocal ENABLEEXTENSIONS & set "i=0.0.0.0" & set "j=" for /f "toke ...

  2. 原生态纯JavaScript 100大技巧大收集---你值得拥有

    1.原生JavaScript实现字符串长度截取 function cutstr(str, len) { var temp; var icount = 0; var patrn = /[^\x00-\x ...

  3. 疯狂html5演讲(两):HTML5简经常使用的元素和属性(一个):html5保留经常使用的元素

    html5取出一小部分的元素和属性:主要删除的各种元素和属性与文档相关的风格.例<font>.width等待,html5建议规范css样式表来控制html文档样式. 1.基本元素 < ...

  4. 《Shell十三问》笔记(上)

    <shell十三问>是网中人前辈首发在CU论坛上对SHELL的一些整理,非常值得一读 注:笔记的标号非问题标号,而是知识点的标号.本篇笔记记录的是1-10问的知识点 (1)IFS:Shel ...

  5. linux下getrusage()

    #include <sys/resource.h> /* Return resource usage information on process indicated by WHOand ...

  6. MyEclipse的真正价值——时间等于金钱

    全世界成千上万的Java开发者选择MyEclipse作为首选的Eclipse IDE,甚至超过了著名的开发工具 IBM Rational和Eclipse Java. 为什么? 很简单,MyEclips ...

  7. SSIS Package to Call Web Service

    原文 SSIS Package to Call Web Service SSIS Package to Call Web Service. You can Call WebService from S ...

  8. WebIM(1)

    WebIM系列文章 之前笔者发布的云翔在线软件平台中已经包含了一个功能相对比较齐全的WebIM,这个系列的文章就是介绍如何开发出功能类似的WebIM,在文章开始前,先介绍一下相关的技术: 1.Come ...

  9. 【PLSQL】过程procedure形参和参数

    ************************************************************************   ****原文:blog.csdn.net/clar ...

  10. 基于EF+MVC+Bootstrap的通用后台管理系统及架构

    分享基于EF+MVC+Bootstrap的通用后台管理系统及架构 基于EF+MVC+Bootstrap构建通用后台管理系统,集成轻量级的缓存模块.日志模块.上传缩略图模块.通用配置及服务调用, 提供了 ...