继承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. Redis 安装与简单示例

    Redis 安装与简单示例 一.Redis的安装 Redis下载地址如下:https://github.com/dmajkic/redis/downloads 解压后根据自己机器的实际情况选择32位或 ...

  2. 【翻译自mos文章】SYS_OP_C2C 导致的全表扫描(fts)/全索引扫描

    SYS_OP_C2C 导致的全表扫描(fts)/全索引扫描 參考原文: SYS_OP_C2C Causing Full Table/Index Scans (Doc ID 732666.1) 适用于: ...

  3. 超高性能的json序列化

    超高性能的json序列化之MVC中使用Json.Net 超高性能的json序列化之MVC中使用Json.Net 先不废话,直接上代码 Asp.net MVC自带Json序列化 1 /// <su ...

  4. 【 D3.js 入门系列 --- 9.6 】 生产的包图

    我的个人博客是:www.ourd3js.com csdn博客为:blog.csdn.net/lzhlzz 转载请注明出处,谢谢. 打包图( Pack ).用于包括与被包括的关系,也表示各个对象的权重, ...

  5. 在标记的HREF属性中javascript:alert(this.innerHTML)会怎么样?

    原文:在标记的HREF属性中javascript:alert(this.innerHTML)会怎么样? <a href="javascript:alert(this.innerHTML ...

  6. sql datalength与len区别用法

    原文:sql datalength与len区别用法 len ( string_expression )参数:要计算的字符串 len() 函数len 函数返回文本字段中值的长度. sql len() 语 ...

  7. 解决Shockwave flash在chrome该浏览器崩溃

    越来越多的人开始使用chrome浏览器,很多用户都遇到过flash崩溃.有时重启chrome为了解决,有时不可能使用chrome无论打开什么网站是什么flash.这个问题是非常小的Firefox或IE ...

  8. 基于.NET Socket Tcp的发布-订阅框架

    基于.NET Socket Tcp的发布-订阅框架 一.分布式消息总线 在很多MIS项目之中都有这样的需求,需要一个及时.高效的的通知机制,即比如当使用者A完成了任务X,就需要立即告知使用者B任务X已 ...

  9. php和cookie

    cookie常用于用户识别,是服务器留在用户计算机中的小文件. cookie在浏览器端和服务器端的通信过程大致是这样: 浏览器向服务器作出请求(如果浏览器有cookie,将在request heade ...

  10. C#实现函数根据返回类型重载

    一直以来都很奇怪为何C#不能直接支持函数返回值重载, 比如如下两个函数是编译不过的 Public Class DbHelper { Public Static int ExecuteScalar(); ...