Introduction
This tutorial describes an approach for creating a postit control in C# (.NET 3.5). It started when I was experimenting in Paint to draw a postit. At some point,
I decided to make a control out of it. This could be used, for instance, in a virtual SCRUM-board or in a todo-application.
Features
Before I started, I defined a short list of features to implement. Other features will be certainly added in future versions.
Properties to set at creation:
Message
: The message itself
Font
: The font type and size
TextColor
: The text color of the message
PostitColor
: The color of the postit
Urgency
: A flag that indicates whether the task is urgent or not
Actions to perform on the postit:
- Move: moving the postit around
- Change size: making the postit larger or smaller with the mouse wheel
- Reset size: Setting the postit to its original size
Using the code
This section first describes the implementation of the control and then the implementation of a sample client using the control.
Step 1: Create an image
After some experimentation, I created a bitmap that was good enough. You can of course use the image that is available in the attachments.
Step 2: Project creation
Create a project of type Windows Forms Application in Visual Studio.
Step 3: Add image as resource
- Open the project properties and go to the Resources tab
- Select Add Resource and then Add Existing File.
- Select the bitmaps attached to this article (‘lightyellow.bmp’, ‘flatyellow.bmp’)
Step 4: Add user control
Add a new user control via the Solution Explorer and name it Postit.
Step 5: Implementing the control
The following properties are defined here:
private string message; private Size postitSize; private Bitmap postitBitMap;
private int sizeDiff = 20; private int maxWidth = 750; private int maxHeigth = 750; private int minWidth = 150; private int minHeigth = 150;
private int blackBorderWidth = 50; private int blackBorderHeight = 70;
private int messageLeft = 5; private int messageTop = 100;
private bool isUrgent = false; private int urgencyFlagLeft = 5; private int urgencyFlagTop = 5;
private Size originalSize; private Font messageFont = new Font("Tahoma", 20); private Brush messageColor = Brushes.Black; private PostitColor postitColor;
The postit control has three different constructors:
public Postit(String message)
{
this.message = message;
this.postitColor = PostitColor.FlatYellow;
this.init();
}
public Postit(String message, Font messageFont, Brush messageColor, PostitColor postitColor)
{
this.message = message;
this.messageFont = messageFont;
this.messageColor = messageColor;
this.postitColor = postitColor;
this.init();
}
public Postit(String message, Font messageFont, Brush messageColor, PostitColor postitColor, bool isUrgent)
{
this.message = message;
this.messageFont = messageFont;
this.messageColor = messageColor;
this.postitColor = postitColor;
this.isUrgent = isUrgent;
this.init();
}
All constructors call the init()
-method, that does all the general initialization, i. e. it creates the bitmap, defines the size of the postit and the original size
and sets the event handlers:
private void init()
{
SetStyle(ControlStyles.OptimizedDoubleBuffer |
ControlStyles.UserPaint |
ControlStyles.OptimizedDoubleBuffer, true);
InitializeComponent();
if (this.postitColor == PostitColor.LightYellow)
this.postitBitMap = PostitDemo.Properties.Resources.lightyellow;
else
this.postitBitMap = PostitDemo.Properties.Resources.flatyellow;
this.postitSize = this.postitBitMap.Size;
this.originalSize = this.Size;
this.MouseWheel += new MouseEventHandler(this.OnMouseWheel);
this.DoubleClick += new EventHandler(this.OnDoubleClick);
}
The postit control is painted with the help of the OnPaint
and OnPaintBackground
methods.
Both methods are overridden:
protected override void OnPaint(PaintEventArgs e)
{
this.drawPostit(e.Graphics);
}
private void drawPostit(Graphics controlGraphics)
{
Rectangle imageRect = new Rectangle(0, 0, this.postitSize.Width+this.blackBorderWidth,
this.postitSize.Height+this.blackBorderHeight);
controlGraphics.DrawImage(this.postitBitMap, imageRect);
RectangleF textRect = new RectangleF(this.messageLeft, this.messageTop, this.postitSize.Width,
this.postitSize.Height);
StringFormat drawFormat = new StringFormat();
drawFormat.Alignment = StringAlignment.Center;
controlGraphics.DrawString(this.message, this.messageFont, this.messageColor, textRect,
drawFormat);
if (this.isUrgent)
{
RectangleF urgentRect = new RectangleF(this.urgencyFlagLeft, this.urgencyFlagTop,
this.postitSize.Width / 10, 100);
controlGraphics.DrawString("!", new Font("Tahoma", 50,FontStyle.Bold), Brushes.Red,
urgentRect);
}
}
The drawPostit
method first paints the bitmap. Thereafter, the message is painted
with the given font. At last, the urgency flag is painted, if the flag is set.
The OnPaintBackground
-method is empty to avoid that the background is painted. This minimizes problems with flickering when the postit is moved.
protected override void OnPaintBackground(PaintEventArgs pevent)
{
}
Resizing of the postit is handled by the OnMouseWheel
method:
protected void OnMouseWheel(object sender, MouseEventArgs e)
{
this.resize(e);
}
private void resize(MouseEventArgs e)
{
int width = this.Size.Width;
int height = this.Size.Height;
if (e.Delta > 0)
{
if ((width + this.sizeDiff <= this.maxWidth)
&& (height + this.sizeDiff <= this.maxHeigth))
{
width += this.sizeDiff;
height += this.sizeDiff;
}
}
else {
if ((width - this.sizeDiff >= this.minWidth)
&& (height - this.sizeDiff >= this.minHeigth))
{
width -= this.sizeDiff;
height -= this.sizeDiff;
}
}
this.Size = new Size(width, height);
this.postitSize = new Size(width - 25, height - 35);
}
As stated above, the size of the postit shall be reset through a double click:
protected void OnDoubleClick(object sender, EventArgs e)
{
this.resetSize();
}
private void resetSize()
{
this.Size = this.originalSize;
this.postitSize = this.postitBitMap.Size;
}
Step 6: Implement the form
Now the postit control is ready and the following describe the creation of a Windows Form that uses the postit control:
The form contains the following controls:
- a groupbox (name=grpPostitSettings)
- a label (name=lblMessageText)
- a textbox (name=txtMessage)
- ...and a button (name=btnCreatePostit)
Besides, the form has the following properties:
private Postit currentPostit = null; private List<Postit> postitList = new List<Postit>();
private Point ptStartPosition; private Point ptEndPosition;
When the button is clicked, a new postit is created, event handlers are set and the control is added to the form:
private void btnCreatePostit_Click(object sender, EventArgs e)
{
Postit postit = new Postit(this.txtMessage.Text, new Font("Comic", 20),
Brushes.Green, PostitColor.FlatYellow, this.chkUrgent.Checked);
postit.Location = new Point(5, 100);
postit.MouseDown += new MouseEventHandler(this.OnMouseDown);
postit.MouseMove += new MouseEventHandler(this.OnMouseMove);
postit.BackColor = Color.FromKnownColor(KnownColor.Transparent);
this.Controls.Add(postit);
this.currentPostit = postit;
this.postitList.Add(postit);
}
The mouse events MouseDown
and MouseMove
are handled here as well:
private void OnMouseDown(object sender, MouseEventArgs e)
{
this.currentPostit = sender as Postit;
this.ptStartPosition = this.PointToScreen(e.Location);
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
Cursor.Current = Cursors.SizeAll;
this.ptEndPosition = this.currentPostit.PointToScreen(e.Location);
ptEndPosition.Offset(-ptStartPosition.X, -ptStartPosition.Y);
this.currentPostit.Location = ptEndPosition;
this.Invalidate();
}
}
History
- 2012-07-30: This is the first version.
- 2012-08-09: Added the postit color as new property.
Helpful resources