使用 WPF 探索 Ink API






4.93/5 (57投票s)
2007年6月8日
10分钟阅读

269820

6528
使用 WPF 探索 Ink API
目录
引言
我不知道你们了解多少,但我对.NET 3.0越来越感兴趣,尤其是WPF。到目前为止,我主要通过结合使用Visual Studio 2005和WPF插件WPF的Visual Studio设计器,或者从其他地方(主要是Josh Smith的文章,谢谢Josh!),零散地借用代码片段来构建我的WPF应用程序,比如这篇文章或这篇文章。总之,在这篇文章中,我决定使用Expression BLEND来尝试创作,看看效果如何以及它有多容易使用。
那么这次我决定做什么呢?我最近开始对Ink API产生浓厚兴趣,它有一个很棒的Tablet PC SDK。在我使用的Windows Vista中,相关的API是预装的,但如果你是XP用户,你可能需要下载SDK,这里提供下载:这里。
不过,对于这篇文章来说,只要安装了.NET 3.0框架,我想这就足够了。
总之,正如我所说,我一直在玩Ink API,并对其进行了一些尝试。我一直在MSDN上研究它,然后(出于好奇)我查阅了Charles Petzold的书《Applications=Code+Markup》,发现我试图做的事情与他非常相似,所以这个应用程序可以看作是我和他书本示例的结合。我决定制作最简单的Paint版本,没有任何图层或花哨的功能,只有Ink。
所以这个应用程序将演示Ink的使用方面:
- 保存墨迹
- 将墨迹保存为位图
- 加载墨迹
- 剪切墨迹
- 复制墨迹
- 粘贴墨迹
- 删除墨迹
- 选择墨迹
- 格式化墨迹
- 笔尖颜色
- 更改笔尖类型
- 更改笔
哦,它还将演示以下WPF控件的使用,但不会详述(如果你想了解它们如何工作,请深入研究XAML):
按钮
Grids
InkCanvas
GroupBox
RadioBtton
Expander
弹出窗口
它显然使用Styles和Templates来实现其美观的外观,但这些WPF概念在其他地方已有描述,你可以尝试这里或Josh Smith,他在他关于WPF的美丽系列文章中对此进行了阐述。
本文将讨论Ink的使用,如果认为重要,将解释与特定Ink操作相关的UI元素。
关于演示应用程序的一点说明
在我开始用附带的WPF应用程序的内部机制轰炸大家之前,我们先快速看一下最终产品。
他是不是很漂亮?
该应用程序基于Visual Studio 2005,并安装了WPF的Visual Studio设计器,并结合了Expression BLEND和Visual Studio 2005。我想指出的是,我认为仅使用Expression BLEND并不能让你成为WPF高手,如果你想要写出漂亮的 कोड,你仍然需要能够手工编写代码。但正如我所说,我只是想在本文中尝试一下。而且我不得不说,我认为Expression BLEND团队做得非常出色,它和Adobe Flash一样易于使用,我也非常喜欢Adobe Flash。
必备组件
- 显然,因为是WPF,你还需要.NET 3.0框架
- 如果你真的想在Visual Studio中编辑内容,那么WPF的Visual Studio设计器可能会派上用场。
那么它到底能做什么?
正如我上面所说,该应用程序具有以下功能,我们将逐一研究这些项目。
- 保存墨迹
- 将墨迹保存为位图
- 加载墨迹
- 剪切墨迹
- 复制墨迹
- 粘贴墨迹
- 删除墨迹
- 选择墨迹
- 格式化墨迹
- 笔尖颜色
- 更改笔尖类型
- 更改笔
但在我们深入研究所有这些之前,我们需要理解一件事,那就是,我们实际上将所有这些相关功能应用到了什么上面?
在这个例子中,我将使用Microsoft.Ink
命名空间,特别是将演示使用WPF的InkCanvas
控件。当然,它是一个WPF控件,但Tablet PC SDK中也有等效的控件,但我如上所述,我使用的是InkCanvas
控件。
WPF的InkCanvas
控件公开了一个有趣的类,我们将在本文中反复使用它。这个类是StrokeCollection
,可以通过InkCanvas
类访问。通过使用这两个类,我们可以完成创建这个非常简单的Paint应用程序的所有相关任务。
那么,我们开始吧,好吗?
保存墨迹
保存Ink非常简单,我们只需使用InkCanvas.Strokes
对象的Save()
方法。它接受任何流。在附带的应用程序中,我创建了一个名为“Ink Serialized Format”的新文件格式,但你可以使用任何你喜欢的格式。
this.inkCanv.Strokes.Save(file);
这就足以保存所有必要的信息,以便稍后恢复Ink。
将墨迹保存为位图
将Ink保存为位图要棘手一些,但并非不可能。我们只需使用RenderTargetBitmap
、BmpBitmapEncoder
和BitmapFrame
这三个很棒的类,如下所示。
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();
需要注意的是,这是一个单向过程,我没有找到一种方法可以从图像中检索Ink。尽管如此,将图像添加为InkCanvas
层次结构中的节点并没有问题。这将在本文系列的第二部分中进一步讨论。
加载Ink文件

毫不奇怪,从文件加载Ink就是保存文件的反向操作。和以前一样,我们只需要使用流。在附带的应用程序中,我创建了一个名为“Ink Serialized Format”的新文件格式,但你可以使用任何你喜欢的格式。
this.inkCanv.Strokes = new StrokeCollection(file);
剪切墨迹
目前,我假设至少有一些Ink被选中。你可以看到选中的Ink,因为它会有一个围绕Ink的边界框和调整大小的句柄,如上图所示。所以一旦你选择了一些Ink,你就可以剪切它;这非常容易。
if (this.inkCanv.GetSelectedStrokes().Count > 0)
this.inkCanv.CutSelection();
复制墨迹
复制Ink几乎与剪切Ink相同(假设你已经选中了一些Ink),我们只需使用InkCanvas
的CopySelection
方法而不是CutSelection
。
if (this.inkCanv.GetSelectedStrokes().Count > 0)
this.inkCanv.CopySelection();
粘贴墨迹
粘贴Ink也同样容易(假设你已经选中了一些Ink),让我们来看看。
if (this.inkCanv.CanPaste())
this.inkCanv.Paste();
删除墨迹
删除Ink也非常简单(假设你已经选中了一些Ink),只需检查是否有Strokes
可以移除,然后移除它们。
if (this.inkCanv.GetSelectedStrokes().Count > 0)
{
foreach (Stroke strk in this.inkCanv.GetSelectedStrokes())
this.inkCanv.Strokes.Remove(strk);
}
选择墨迹
回想一下,对于选择、剪切、复制、粘贴和删除操作,我们必须实际选中Ink。那么我们如何选择Ink呢?正如我所说,这可以通过两种方式完成:你可以使用“选择”按钮(此选项),或者可以将当前笔尖更改为选择模式。所以让我们来看看如何选择所有Ink。这非常容易。
this.inkCanv.Select(this.inkCanv.Strokes);
格式化墨迹
格式化Ink取决于你是否首先选中了要格式化的Ink。所以假设你选中了一些Ink(Strokes
),当你使用“格式化Ink”按钮时,你将看到颜色选择器窗口,如下面的“笔尖颜色”部分所示。该窗口将允许你更改Strokes
的属性。
注意:当选择笔画时,选中的笔画看起来会像这样。

“格式化”按钮的代码将简单地尝试获取第一个笔画的颜色,然后显示对话框窗口,我们可以在其中为选定的Strokes
选择新颜色。
StylusSettings dlg = new StylusSettings();
dlg.Owner = this;
// Try getting the DrawingAttributes of the first selected stroke.
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())
{
// Set the DrawingAttributes of all the selected strokes.
foreach (Stroke strk in strokes)
strk.DrawingAttributes = dlg.DrawingAttributes;
}
笔尖颜色
为了让用户控制Ink的颜色和绘制方式,有一个第二个XAML窗口,名为“StylusSettings.xaml”,它包含一个UniformGrid
控件,其中包含按钮,这些按钮的背景只是设置为System.Brushes
集合中的特定Brush
颜色。
System.Brushes
的迭代是通过反射完成的。
private void createGridOfColor()
{
PropertyInfo[] props = typeof(Brushes).GetProperties(BindingFlags.Public |
BindingFlags.Static);
// Create individual items
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);
}
}
此页面还负责显示和设置当前用于绘制的Ink值。这是通过一个名为DrawingAttributes
的类实现的,该类可以从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;
}
}
更改笔尖类型
还可以更改笔尖控制,可选项如下:
- Ink:将绘制Ink
- Erase:将擦除Ink(用笔尖覆盖的部分)
- Erase By Stroke:将擦除选定的
Stroke
- Select:允许用户绘制框以选择Ink
允许用户绘制Ink。
允许用户擦除Ink。
允许用户擦除Ink笔画。
允许用户选择Ink笔画。
更改笔的大小
还可以更改笔的大小。
其他一些很酷的东西
有一些区域我相当喜欢,希望你们也会觉得有趣,例如:
- FishEyePanel.cs:这不是我自己的代码,但我仍然很喜欢。它来自这里,作者是Paul Tallett。基本上,它的工作原理类似于Apple漂亮的工具栏,鼠标附近的图标最大,两侧的图标依次变小,以此类推。
就是这样
我希望这篇文章能说明在你的应用程序中使用Ink是多么容易。也就是说,我只触及了Tablet PC SDK可以做什么的一小部分。我不想在人们完全了解Ink的巨大内部工作原理之前就吓跑他们,而是让他们先看到一个相对简单的应用。我计划写另一篇关于Ink的文章,希望能将其更紧密地结合起来,所以你可能需要等到那时。
那么你觉得呢?
我想请问,如果你喜欢这篇文章,请投票并留下评论,这能让我知道文章是否达到了合适的水平,以及是否包含了人们需要知道的内容。
结论
我很高兴写了这篇文章,正如我所说,我将再写一篇关于Ink的文章,但很抱歉,那将是Vista独有的。它还将使用另一种.NET 3.0技术。我希望下一篇文章对各位好朋友来说会相当有趣,因为它将使用两种.NET 3.0技术和Vista独有的API。
正如我之前所说……如果你想保持工作/生活平衡,我建议你尽量远离WPF,因为一旦它把你拖入它的触角,你就真的无处可逃了。“抵抗是徒劳的。你们将被同化。”
历史
- v1.0 2007/06/06:初始发布
其他
我鼓励大家深入学习WPF,阅读Josh Smith的所有文章,并尝试自己使用Expression BLEND。正如我所说,我认为它很酷,但在某些地方也应该与手工编码结合使用。如果你对WPF是认真的,你应该多阅读这方面的内容,以便了解Expression BLEND生成的代码实际上做了什么。