精通
英语
和
开源
,
擅长
开发
与
培训
,
胸怀四海
第一信赖
锐英源精品开源心得,转载请注明:“锐英源www.wisestudy.cn,孙老师作品,电话13803810136。需要全文内容也请联系孙老师。
In this article, I'll show you how to code a custom textbox with a watermark / placeholder. I have been looking all around for a control like this, because I think it is damn sexy to give the user some info when they fill in a formula instead of having labels as hints. It saves space and looks awesome.
在本文中,我将向你展示如何用水印/占位符编写自定义的文本框。我一直在到处寻找这样的控件,因为我认为这是非常有吸引力的给用户一些信息当他们填写一个公式而不是标签作为提示时。它可以节省空间,看起来太棒了。
I once searched the web for a control like this, but without luck. The only example I got was using some kind of user32.dll call and that was just like the one Windows 7 uses in the Search field of the Start menu. When you click the textbox, the hint goes away and that in my opinion doesn't look very professional. I am not saying mine is pro at all, but it gets the job done in a smooth way. I also found an article where the author used labels to write the watermark, and that was disturbing to use because of its lack of padding. It simply overlaps the blinking cursor which is the signal to the user telling them to type in. So I figured out I would try to use a panel instead because you can set the panel to negative coordinates and that's just what I needed
我曾经在网络搜寻这样的控件,但是运气不好。我得到的唯一的例子是使用某种user32.dll调用,就像一个Windows 7用于搜索字段的开始菜单。当你点击文本框,提示消失,在我看来很不专业。我不是说我很专业。但它可以顺利完成工作。我还发现一篇文章,作者使用标签写水印,这是使用起来很烦人,因为它缺乏内边距。它简单的重复闪烁的光标是告诉用户正在输入的信号。所以我想我会尝试使用面板代替,因为你可以设置面板来消除坐标,这正是我需要的。
It is really simple to use this code. All you have to do is import the DLL to your project and drag and drop it just like you would do with a regular TextBox. Also, in design time, you can set the color of the watermark when it is active or passive. You can also set the text and font of the watermark. By default the font is the same as its parent which will be the textbox base.
这段代码使用起来很简单的。你需要做的就是将DLL导入到你的项目中,拖拽它,就像处理普通文本框。另外,在设计时,当水印是主动或者被动时,你都可以设置水印的颜色。你也可以设置水印的文字和字体。默认的字体是和文本框的基础文字相同。
So let's start: 所以让我们开始:
Note that I will go through the class chronologically starting from the top. This will cause a lot of errors while writing but in the end, it will work without defects Just be patient and create a new class and follow me. Else you can go download the source if you get any errors.
请注意,我将按类的时间顺序从顶部开始。这将导致在写的时候出现很多错误但是最终,它的工作将会没有缺陷,要有耐心并且跟着我创建一个新的类。另外,如果你有任何错误都可以下载源代码。
First we declare the class. (Remember to add the following references: System.Windows.Forms and System.Drawing.)
首先,我们声明类。(记得要添加以下引用:System.Windows。Forms和System.Drawing)。
using System; using System.Windows.Forms; using System.Drawing; using System.ComponentModel; namespace ChreneLib.Controls.TextBoxes { public class CTextBox : TextBox {
The next thing to do is to declare some variables: 下一步是声明一些变量:
Here we set up a string to hold the watermark text, and set the color of the watermark depending on whether the control has focus or not. We also set up a new panel which will be the place later on to draw the watermark text in. And at last we have a font and a brush. The font will be the font of the drawn watermark, and by default we will set that to its parent's font. The solid brush will be used to hold the color of the font which will change dynamically.
在这里我们设置一个字符串保存水印文本,并根据控件是否已聚焦设置水印的彩色。我们还建立了一个新的面板,这将是以后绘制水印文本的地方。字体将是绘制水印的字体,默认情况下,我们将其设置为其父类字体。固体刷将用于保存动态字体的颜色,这些字体将会改变的。
#region Fields #region Protected Fields protected string _waterMarkText = "Default Watermark..."; //The watermark text protected Color _waterMarkColor; //Color of the watermark when the control does not have focus protected Color _waterMarkActiveColor; //Color of the watermark when the control has focus #endregion #region Private Fields private Panel waterMarkContainer; //Container to hold the watermark private Font waterMarkFont; //Font of the watermark private SolidBrush waterMarkBrush; //Brush for the watermark #endregion #endregion
Now we will set up our constructor. 现在我们将建立构造函数。
The constructor doesn't really do anything other than calling the Initialize method which we will get to in a bit. So go copy paste or whatever you want to
我们接触到的构造函数实际上除了调用初始化方法并没有做任何事。所以去复制粘贴或任何你想要的
#region Constructors public CTextBox() { Initialize(); } #endregionAdding methods 添加方法
This chapter will be a little long, but I'm sure you will get it all anyways 本章将会有点长,但我相信你会得到一切
Initialize();
We start out by making our Initialize method which basically just sets our variables to some default values. After that we call the DrawWaterMark() method, which will draw the watermark, so we can see it just as we put it in. Actually I don't think it is needed to be called as we will call it later in our onPaint method, but what the heck. After we have drawn our watermark, we set some event listeners. We need them because they actually decide how the control should look like. For instance, type in some text, and the watermark should be removed, and therefore we will need to write those instructions later on. As you can see, we are adding a new EventHandler, and within the parenthesis we write the methods which should be triggered whenever a specific thing happens, e.g., when we click on the control.
我们开始制作初始化方法基本上就是变量设置为默认值。之后,我们调用DrawWaterMark()方法来绘制水印,所以我们可以看到它就像我们把它放进去一样。其实我认为没必要调用它因为之后我们将调用onPaint方法,但到底是什么。我们得出水印后,设置一些事件监听器。我们需要他们,因为他们实际上决定控件应该是什么样的。例如,键入一些文本水印应该删除,因此我们需要写这些指令。正如你所看到的,我们添加一个新的事件,并且我们在括号内写下当特殊情况发生时需要引用的方法。比如,当我们点击控件。
RemoveWaterMark();
Next we will create our method to remove the watermark. This will be called whenever the length of the text the user is typing is greater than zero. So let's say you type something in the textbox, then the control will remove the watermark. And if you decide to delete that text again, the watermark will be redrawn. But for the algorithm, it goes like this. First we check to see if our watermark container (a panel) is created or not. If it isn't created, then we should not do anything, but if it is created, then it should remove it and release the memory allocated for it.
接下来,我们将创建我们去除水印的方法。当用户输入文本的长度大于零时将会被调用。假设你在文本框输入一些东西,然后这个控件将移除水印。并且如果您决定删除文本水印重绘。对于算法,它是这样的。首先,我们要检查是否创建了水印容器(面板)。如果没有创建,那么我们不应该做任何事情,但如果它已经被创建,那么应该删除它并释放分配的内存。
DrawWaterMark();
This method is called whenever the textbox is empty and there isn't already a watermark drawn. First, we initialize the watermark container, and then we set its properties. Then we add a new event listener for when it is clicked. This is used so the control also will get focus if we click the container. If it wasn't there, the control wouldn't get focus and you would have some serious problems gaining focus to that control, and by that I mean, it would be hard to type in some text.
调用此方法时,文本框是空的,已经没有一个水印。首先,我们初始化水印容器,然后设置其属性。然后,我们添加一个新的点击事件监听器。这使用于如果我们单击容器控件也会获得焦点。如果没有它,控件不会获得焦点,你会在控件获得焦点时遇到一些严重的问题,意思是,很难键入一些文本。
#region Private Methods /// <summary> /// Initializes watermark properties and adds CtextBox events /// </summary> private void Initialize() { //Sets some default values to the watermark properties _waterMarkColor = Color.LightGray; _waterMarkActiveColor = Color.Gray; waterMarkFont = this.Font; waterMarkBrush = new SolidBrush(_waterMarkActiveColor); waterMarkContainer = null; //Draw the watermark, so we can see it in design time DrawWaterMark(); //Eventhandlers which contains function calls. //Either to draw or to remove the watermark this.Enter += new EventHandler(ThisHasFocus); this.Leave += new EventHandler(ThisWasLeaved); this.TextChanged += new EventHandler(ThisTextChanged); } /// <summary> /// Removes the watermark if it should /// </summary> private void RemoveWaterMark() { if (waterMarkContainer != null) { this.Controls.Remove(waterMarkContainer); waterMarkContainer = null; } } /// <summary> /// Draws the watermark if the text length is 0 /// </summary> private void DrawWaterMark() { if (this.waterMarkContainer == null && this.TextLength <= 0) { waterMarkContainer = new Panel(); // Creates the new panel instance waterMarkContainer.Paint += new PaintEventHandler(waterMarkContainer_Paint); waterMarkContainer.Invalidate(); waterMarkContainer.Click += new EventHandler(waterMarkContainer_Click); this.Controls.Add(waterMarkContainer); // adds the control } } #endregion
We are almost done. Now we need to add some event listeners. 我们接近完成了。现在我们需要添加一些事件监听器。
ThisHasFocus();
This event handler is triggered whenever the control gains focus. First it will change the color of the watermark to the active color. Then we check to see if the user has written anything in the control and if they have, we should not do anything. However if it is empty, we will remove the watermark and then redraw it.
这个事件处理函数是触发控件获得焦点的。首先,它将改变水印的颜色为活跃的颜色。然后我们检查用户是否已经在控件中写过什么,如果有,我们不应该做任何事情。但是如果它是空的,我们将删除水印,然后重新画一下。
ThisWasLeaved();
So basically, in this event handler, we will check if the user has written anything. If something is written, we will remove the watermark, and again.. I don't know if this is needed, but double check is always better than zero check. Well, and if the user hasn't written anything, we will invalidate the control, which means we will trigger the paint event which will redraw the control. We do that so we make sure the watermark changes its color to the passive color.
基本上,在这个事件处理程序中,我们将检查用户写了什么。如果写的有东西,我们将删除水印,还有. 我不知道这是必要的,但仔细检查总是大于零。好,如果用户没有写任何东西,我们的控件将会失效,这意味着我们将触发绘制事件将重新制定控件。我们这样做确保水印改变其颜色到被动色彩。
ThistextChanged();
This method is triggered whenever the user changes the text. And here we will check for the text length again, so if there is no text in the control, we will draw a watermark, but if there is text, we will remove it.
每当用户更改文本时会触发此方法。在这里,我们将再次检查文本长度,如果没有文本控件,我们会画一个水印,但是如果有文本,我们会删除它。
OnPaint();
We override the existing OnPaint method so we can make sure the watermark will be refreshed whenever the control is invalidated. This will even work in design time.
我们重写现有的OnPaint方法,这样我们可以确保每当控件失效时水印将被刷新。甚至会在设计时工作。
OnInvalidated();
We also override this method, so we can make sure that the watermark container also gets invalidated.
我们也覆盖这个方法,所以我们可以确保该水印容器也会失效。
#region CTextBox Events private void ThisHasFocus(object sender, EventArgs e) { //if focused use focus color waterMarkBrush = new SolidBrush(this._waterMarkActiveColor); //The watermark should not be drawn if the user has already written some text if (this.TextLength <= 0) { RemoveWaterMark(); DrawWaterMark(); } } private void ThisWasLeaved(object sender, EventArgs e) { //if the user has written something and left the control if (this.TextLength > 0) { //Remove the watermark RemoveWaterMark(); } else { //But if the user didn't write anything, Then redraw the control. this.Invalidate(); } } private void ThisTextChanged(object sender, EventArgs e) { //If the text of the textbox is not empty if (this.TextLength > 0) { //Remove the watermark RemoveWaterMark(); } else { //But if the text is empty, draw the watermark again. DrawWaterMark(); } } #region Overrided Events protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); //Draw the watermark even in design time DrawWaterMark(); } protected override void OnInvalidated(InvalidateEventArgs e) { base.OnInvalidated(e); //Check if there is a watermark if (waterMarkContainer != null) //if there is a watermark it should also be invalidated(); waterMarkContainer.Invalidate(); } #endregion #endregion
The final step is to set up some properties, and methods so we can change the properties during runtime and during design time. I will not explain this much, because it is just some get/set methods to change the color, font, and text of the watermark. However, you may see something different over each method. These are called attributes, and are used at design time. The category will create a new little node at design time so you can gather the properties in one place. And the description shows some text about what that specific attribute is going to do.
最后一步是设置一些属性和方法,我们可以在运行时或者设计时间更改属性。我就不解释这么多了,因为它仅仅是一些get / set方法来改变颜色,字体,水印文本。然而,您可能会在每个方法中看到不同的东西。这些被称为属性,并且在设计时被使用。该类别将在设计时创建一个新的小节点,以便您可以在一个地方聚集属性。描述显示一些文本关于特定属性是要做什么。
#region Properties [Category("Watermark attribtues")] [Description("Sets the text of the watermark")] public string WaterMark { get { return this._waterMarkText; } set { this._waterMarkText = value; this.Invalidate(); } }
[Category("Watermark attribtues")] [Description("When the control gaines focus, " + "this color will be used as the watermark's forecolor")] public Color WaterMarkActiveForeColor { get { return this._waterMarkActiveColor; }
set { this._waterMarkActiveColor = value; this.Invalidate(); } }
[Category("Watermark attribtues")] [Description("When the control looses focus, this color " + "will be used as the watermark's forecolor")] public Color WaterMarkForeColor { get { return this._waterMarkColor; }
set { this._waterMarkColor = value; this.Invalidate(); } }
[Category("Watermark attribtues")] [Description("The font used on the watermark. " + "Default is the same as the control")] public Font WaterMarkFont { get { return this.waterMarkFont; }
set { this.waterMarkFont = value; this.Invalidate(); } }
#endregion } }