继承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. C#创建和初始化类

    using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace C_编辑 ...

  2. AngularJS应用开发思维之1:声明式界面

    这篇博客之前承接上一篇:http://www.cnblogs.com/xuema/p/4335180.html 重写示例:模板.指令和视图 AngularJS最显著的特点是用静态的HTML文档,就可以 ...

  3. Linux 经常使用 性能 检测 命令 说明

    1.uptime [root@smgsim02 ~]# uptime  15:08:15 up 98 days,  4:19,  2 users,  load average: 0.07, 0.29, ...

  4. Warning: session_start() [function.session-start]: Cannot send session cookie - headers already sent by

    配置php网站的时候,经常会在页首出现Warning: session_start() [function.session-start]: Cannot send session cache limi ...

  5. 快速构建Windows 8风格应用17-布局控件

    原文:快速构建Windows 8风格应用17-布局控件 本篇博文主要介绍三种常用的布局控件:Canvas.Grid.StackPanel. Panel类是开发Windows 8 Store应用中一个重 ...

  6. testNg自动化,读取excel的数据

    自己写了一个testng执行excel用例的小程序,主要是运行.xlsx的,需要支持xls可以自己扩展,分享一下.下载地址:http://yun.baidu.com/share/link?sharei ...

  7. Spring IOC之 使用JSR 330标准注解

    从Spring 3.0开始,Spring提供了对 JSR 330标准注解的支持.这些注解可以喝Spring注解一样被扫描到.你只需要将相关的Jar包加入到你的classpath中即可. 注意:如果你使 ...

  8. 编写SQL的辅助工具

    原文:编写SQL的辅助工具 今天在同事的帮助下,下载了一个工具:ApexSQL edit,可能是我孤陋寡闻,不知道还有这样的好工具,它可以在我键入SQL时,帮助我提示表的名称和列名称.还可以帮助我格式 ...

  9. SSMS2008插件开发(1)--介绍

    原文:SSMS2008插件开发(1)--介绍 SSMS2008就是Microsoft Sql Server Management Studio 2008的简称.许多人叫做SQL2008或SQL SER ...

  10. Javascript Array API

    JS数组对象提供了很多API方法,由于前段时间要用到某一些方法,但是突然一时又想不起来该怎么用了,上网找有很多资料都不全,所以就自己整理了一篇,完全是自己写的的JS,只是复制到这里来了 ,要用到的朋友 ...