Contents
I don't know a lot about you but I am becoming more and more interested in .NET 3.0 and in particular I am drawn to WPF. Up until now I have been mainly crafting my WPF apps such as this or this or this using a combination of Visual Studio 2005 with The Visual Studio Designer for WPF addon, or have been borrowing snippets of code from here and there (Mainly from Josh Smith actually, so thanks Josh if you're reading this), and piecing together a workable solution. Anyway for this article I decided to use Expression BLEND to see what I could create and just how easy it was.
But just what is it that I decided to do this time? Well I recently started to develop a fascination for the Ink API, for which there is a nice Tablet PC SDK available. In Windows Vista (which I am using) the associated APIs are pre-installed, but if you are an XP user you may need to download the SDK which is available here.
That said for this article if you have the .NET 3.0 framework installed, I would think that, that may be sufficient.
Anyway, as I say I have found myself playing with the Ink API and messing about with it a bit. I have been looking at it in MSDN for while, then I checked (out of curiosity) in my Applications=Code+Markup book, by Charles Petzold, and it turns out I was trying to do something very similar to him, so this app kind of represents a combination of both mine and his books example. I decided to produce the simplest version of paint that one could hope to see, no layers, nothing fancy like that, just Ink.
So this app will demonstrate the following aspects of using Ink
- Save Ink
- Save Ink as a bitmap
- Load Ink
- Cut Ink
- Copy Ink
- Paste Ink
- Delete Ink
- Select Ink
- Format Ink
- Stylus Color
- Change stylus type
- Change pen
Oh it will also demonstrate, but shall not describe the usage of following WPF controls (if you want to see how they work dive into the XAML)
Buttons
Grids
InkCanvas
GroupBox
RadioBtton
Expander
Popup
It obviously uses Styles and Templates to achieve its handsome good looks, but these WPF concepts are described in various other places, you could try here or Josh Smith who talks about it in his lovely series on WPF.
What this article will talk about is the use of Ink, and if it is deemed important will explain the UI elements associated with a particular Ink operation.
Before I start bombarding people with the inner mechanisms of the attached WPF application, shall we have a quick look at the finished product.
Pretty isn't he.
The application is based on using Visual Studio 2005 with The Visual Studio Designer for WPF installed, and using Expression BLEND and Visual Studio 2005 combination. I would just like to point out that I do not think that using Expression BLEND by itself makes you good at WPF, you still need to be able to hand craft code if you want to get neat code. But as I say I just wanted to give it a try, for this article. And I have to say I do actually think the Expression BLEND team have done an excellent job, it is just as easy to use as Adobe Flash, which I also liked very much.
- Obviously as it is WPF you will also need the .NET 3.0 framework
- And if you really want to edit things in Visual Studio The Visual Studio Designer for WPF may not go amiss
As I stated above the application does the following, so we'll investigate these items one by one.
- Save Ink
- Save Ink as a bitmap
- Load Ink
- Cut Ink
- Copy Ink
- Paste Ink
- Delete Ink
- Select Ink
- Format Ink
- Stylus Color
- Change stylus type
- Change pen
But before we go into all of that, we need to understand one thing, and that is, what are we actually applying all these related functions to.
Well in this example I will actually be using the Microsoft.Ink
Namespace and more specifically I will be demonstrating things using the WPF "InkCanvas" target="_blank" hef="http://msdn2.microsoft.com/en-us/library/system.windows.controls.inkcanvas.aspx">InkCanvas
control, which is of course a WPF control, but there is an equivalent in the Tablet PC SDK, but as I say I'm using the "InkCanvas" target="_blank" hef="http://msdn2.microsoft.com/en-us/library/system.windows.controls.inkcanvas.aspx">InkCanvas
control.
The WPF "InkCanvas" target="_blank" hef="http://msdn2.microsoft.com/en-us/library/system.windows.controls.inkcanvas.aspx">InkCanvas
control exposes an interesting object that we will be using over and over in this article. This object is a StrokeCollection
which can be accessed via the InkCanvas
class. It is by using these 2 classes that we are able to carry out all the associated tasks in creating this very simplistic paint application.
So let's get on with it, shall we.
Saving some Ink is child's play, we simply use the Save()
method of the InkCanvas.Strokes
object. Which accepts any stream. In the attached app, I create a new file format called "Ink Serialized Format", but you can use whatever you like.
this.inkCanv.Strokes.Save(file);
And that's enough to save all the information needed to restore the Ink at a later time
Saving the Ink as a Bitmap is a little trickier, but not impossible. We simply use 3 nice objects RenderTargetBitmap
,BmpBitmapEncoder
and BitmapFrame
in the manner shown below:
int marg = int.Parse(this.inkCanv.Margin.Left.ToString());
RenderTargetBitmap rtb =
new RenderTargetBitmap((int)this.inkCanv.ActualWidth - marg,
(int)this.inkCanv.ActualHeight - marg, 0, 0,
PixelFormats.Default);
rtb.Render(this.inkCanv);
BmpBitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(rtb));
encoder.Save(file);
file.Close();
It should be noted that this is a one way trip, I did not find a way to retrieve Ink from an image. Though it is no problem to add images as nodes in the InkCanvas
hierarchy. This will be discussed further in the 2nd part of this article series.
It should come as no surprise that to load some Ink from a file its just the opposite of saving a file. As before we simply use a stream. In the attached app, I create a new file format called "Ink Serialized Format", but you can use whatever you like.
this.inkCanv.Strokes = new StrokeCollection(file);
In order to actually cut the Ink, you must Select some Ink to cut. This can be done in 2 ways, you may either use the Select button (this will be discussed
here or the current stylus may be changed to be in select mode. This is also discussed in more detail
here.
For now I'm going to assume that there is at least some Ink selected. You will be able to see the selected Ink as there will be a bounding rectangle with resize handles surrounding the Ink, as shown in the figure above. So once you've got some ink selected, you will be able to Cut it; it's very easy.
if (this.inkCanv.GetSelectedStrokes().Count > 0)
this.inkCanv.CutSelection();
Copying Ink is almost the same as Cutting it (Assuming you have some Ink selected), we simply use the CopySelection
method of the InkCanvas
instead of the CutSelection
if (this.inkCanv.GetSelectedStrokes().Count > 0)
this.inkCanv.CopySelection();
Pasting Ink is almost as easy (Assuming you have some Ink selected), let's have a look at that.
if (this.inkCanv.CanPaste())
this.inkCanv.Paste();
Deleting Ink is also very simple (Assuming you have some Ink selected), just check that there are some Strokes
to remove, and remove them.
if (this.inkCanv.GetSelectedStrokes().Count > 0)
{
foreach (Stroke strk in this.inkCanv.GetSelectedStrokes())
this.inkCanv.Strokes.Remove(strk);
}
Recall earlier for the Select Cut, Copy, Paste And Delete operations, we had to actually have some Ink selected. Well how do we select some Ink. As I stated that this can be done in 2 ways, you may either use the Select button (this option) or the current stylus may be changed to be in select mode. So let's have a look at how to select all Ink. It's very easy:
this.inkCanv.Select(this.inkCanv.Strokes);
Formatting Ink relies on you first having selected some Ink to format. So assuming you have some Ink (Strokes
) selected when you use the Format Ink button, you will be shown the color picker window as shown below in the Stylus Color section, that window will enable you to change the Strokes
attributes.
Note: When selecting strokes the strokes selected will look as follows:
The code for the Format button is simply going to try and get the color of the 1st Stroke, and then show the dialog window where we can pick a new color for the selected Strokes
.
StylusSettings dlg = new StylusSettings();
dlg.Owner = this;
StrokeCollection strokes = this.inkCanv.GetSelectedStrokes();
if (strokes.Count > 0)
dlg.DrawingAttributes = strokes[0].DrawingAttributes;
else
dlg.DrawingAttributes = this.inkCanv.DefaultDrawingAttributes;
if ((bool)dlg.ShowDialog().GetValueOrDefault())
{
foreach (Stroke strk in strokes)
strk.DrawingAttributes = dlg.DrawingAttributes;
}
In order for the user to control what color and how the Ink should be applied there is a second XAML window that is used, this is called "StylusSettings.xaml" and it contains a UniformGrid
control with buttons which simply have their backgrounds set to a particular Brush
color out of the collection of System.Brushes
.
The iteration of the System.Brushes
is done using Reflection.
private void createGridOfColor()
{
PropertyInfo[] props = typeof(Brushes).GetProperties(BindingFlags.Public |
BindingFlags.Static);
foreach (PropertyInfo p in props)
{
Button b = new Button();
b.Background = (SolidColorBrush)p.GetValue(null, null);
b.Foreground = Brushes.Transparent;
b.BorderBrush=Brushes.Transparent;
b.Click += new RoutedEventHandler(b_Click);
this.ugColors.Children.Add(b);
}
}
Also this page is responsible for showing and setting the current Ink values that will be used for drawing with. This is achieved by the use of a nice class called DrawingAttributes
which can be both retrieved and set on the InkCanvas
public DrawingAttributes DrawingAttributes
{
set
{
chkPressure.IsChecked = value.IgnorePressure;
chkHighlight.IsChecked = value.IsHighlighter;
penWidth = value.Width;
penHeight = value.Height;
currColor = value.Color;
}
get
{
DrawingAttributes drawattr = new DrawingAttributes();
drawattr.IgnorePressure = (bool)chkPressure.IsChecked;
drawattr.Width=penWidth;
drawattr.Height = penHeight;
drawattr.IsHighlighter = (bool)chkHighlight.IsChecked;
drawattr.Color = currColor;
return drawattr;
}
}
There is also the facility to change the stylus control, to any of the following:
- Ink: Will draw Ink
- Erase: Will erase Ink (that is Drawn over with the stylus)
- Erase By Stroke: Will erase the
Stroke
that was selected
- Select: Will allow the user to draw around the Ink to select
Allows the user to draw Ink.
Allows the user to erase Ink.
Allows the user to erase Ink Strokes.
Allows the user to select Ink Strokes.
There is also a facility to change the pen size.
There are a number of areas which I quite like and hope that you may find interesting such as :
- FishEyePanel.cs: Which is not my own, but I like it all the same, it comes from here and is by a fellow called Paul Tallett. Basically it works like those nice task bars that Apples have, where the icon nearest the mouse is biggest and those either side are slightly smaller and so on.
I hope this article shows just how easy it is to use Ink in your applications. That said, I have only touched a very small part of what can be done with the Tablet PC SDK. I did not want to put people off by diving straight into the immense inner workings of Ink without people actually seeing a fairly simple application of it first. I am planning another Ink related article that should tie it together a bit more, so you may have to wait till then.
I would just like to ask, if you liked the article please vote for it, and leave some comments, as it lets me know if the article was at the right level or not, and whether it contained what people need to know.
I have enjoyed writing this article and like I say, I will be writing one more article on Ink, that I'm afraid to say will be VISTA only. It will also use another .NET 3.0 technology. I'm hoping the next article may be quite interesting to you good folk, as it will use two .NET 3.0 technologies and Vista only APIs.
As I've said before....if you want to keep a work/life balance, I would suggest you stay as far away from WPF as possible, cos once its got you in its little tentacles, there really is no escape. "Resistance Is Futile. You Will Be Assimilated".
- v1.0 06/06/07: Initial issue
I would encourage people to get into WPF and read all that Josh Smith writes, and to also try Expression BLEND for themselves. As I say I think it's cool, but should also be used with some hand coding in places. And if you're serious about WPF, you should read around this subject so you know what the code that Expression BLEND produces actually does.