Silverlight中的InkPresenter(可以保存、加载)

InkPresenter,这个东西再熟悉不过,没错就是手写板,我们可以在它上边想怎么画怎么画,其实他的原理很简单,就是捕捉鼠标的轨迹,然后使用指定的颜色和宽度组成线条,然后不停的记录和显示。

先看个效果

就是一个简单的手写(鼠标).

下面开始详细介绍InkPresenter的使用。

首先看下要使用到的类,以及方法:

1.InkPresenter,InkPresenter成为墨迹控件,继承自Canvas类,它不单单是一个控件,而是一个可以接收SL中输入的接口

  主要属性Strokes,一个集合(StrokeCollection),表示要显示的画笔。

2.Stroke,表示单个墨迹画笔,属性DrawingAttributes表示当前画笔的属性,指定 Stroke 的外观。StylusPoints(StylusPointCollection类型集合),返回Stroke的触笔

接触点。

  DrawingAttributes属性如下:

名称说明
公共属性 Color                              获取或设置 Stroke 的颜色。
公共属性 FitToCurve            获取或设置一个值,该值指示是否使用贝塞尔曲线平滑法来呈现 Stroke
公共属性 Height            获取或设置用于绘制 Stroke 的触笔的高度。
公共属性 IgnorePressure            获取或设置一个值,该值指示呈现的 Stroke 的粗细是否会随应用的压力而更改。
公共属性 IsHighlighter            获取或设置一个值,该值指示 Stroke 看起来是否像一支荧光笔。
公共属性 StylusTip           获取或设置用于绘制 Stroke 的触笔的形状。
公共属性 StylusTipTransform                  获取或设置 Matrix,它指定要在触笔笔尖上执行的变换。
公共属性 Width                            获取或设置用于绘制 Stroke 的触笔的宽度。

有了以上两个类,就可以完成墨迹控件的使用,下面开始看看如何在案例中使用:

  <InkPresenter x:Name="inkTest" Height="400" Width="600" 
Background
="AliceBlue"
MouseMove
="inkTest_MouseMove"
MouseLeftButtonDown
="inkTest_MouseLeftButtonDown"
MouseLeftButtonUp
="inkTest_MouseLeftButtonUp">
   </InkPresenter>

可以看到仅仅一个标签就完成了一个InkPresenter的定义,在这里有一点要注意,有些童鞋发现拉过来一个控件之后,包括鼠标的事件代码都写了,可是就是不能画,首先

要确保当前的InkPresenter是否定义了BackGround(或者给InkPresenter添加了其他的元素),其次看看画笔的颜色问题。

下面看下在Inkpresenter中嵌套一个元素:

   <InkPresenter x:Name="inkTest" Height="400" Width="600" 
MouseMove
="inkTest_MouseMove"
MouseLeftButtonDown
="inkTest_MouseLeftButtonDown"
MouseLeftButtonUp
="inkTest_MouseLeftButtonUp">
<Rectangle RadiusX="15" RadiusY="15" Margin="5" Height="400" Width="600">
<Rectangle.Fill>
<ImageBrush ImageSource="Chrysanthemum.jpg" Opacity="0.5" ></ImageBrush>
</Rectangle.Fill>
</Rectangle>
</InkPresenter>

可以看到在InkPresenter中嵌套了一个Rectangle,同时指定Rectangle的背景图片,这样就可以在这个图片上进行画东西了,效果也就是本文开头的那副图片。
大家可能注意到了在Ink上定义了三个鼠标事件,没错这个事件也是重点。

MouseMove事件用于记录鼠标在移动的过程中将轨迹写入到Stroke的StylusPoints集合中去;

MouseLeftButtonDown事件,用于捕获鼠标的坐标同时记录一个新的Stroke(新的画笔)开始,标识一个状态,以便在Move事件中进行点的记录;

MouseLeftButtonUp事件用于释放当前的鼠标捕获,同时释放Stroke,标识当前的Stroke已经结束;

UIElement.CaptureMouse()方法用于鼠标捕获当前的元素;

UIElement.ReleaseMouseCapture()方法,如果该元素具有鼠标捕获,则释放鼠标捕获。

从这里开始,编写后台代码:

        Color currentColor = Colors.Black;//定义默认颜色
Stroke newStroke;//定义全局的画笔,用于在MouseDown中实例化,同时在Up中清空该对象
IsolatedStorageSettings setting = IsolatedStorageSettings.ApplicationSettings;//定义独立缓存,用于保存
        //Ink鼠标移动事件
private void inkTest_MouseMove(object sender, MouseEventArgs e)
{
//如果不为空则说明已经按下了鼠标
if (newStroke != null)
{
//将鼠标移动中的点的轨迹添加到Stroke的StylusPoints
newStroke.StylusPoints.Add(e.StylusDevice.GetStylusPoints(inkTest));
}
}
 
        //Ink鼠标左键按下事件
private void inkTest_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
//捕获鼠标(必须的)
inkTest.CaptureMouse();
//实例化画笔
newStroke = new Stroke();
//设置画笔颜色
newStroke.DrawingAttributes.Color = currentColor;
//指定轮廓的颜色
newStroke.DrawingAttributes.OutlineColor = Colors.Black;
//将鼠标点下的点添加到画笔中区
newStroke.StylusPoints.Add(e.StylusDevice.GetStylusPoints(inkTest));
//将画笔添加到InkPresenter的Strokes(画笔集合)
inkTest.Strokes.Add(newStroke);
}

        //Ink鼠标左键松开事件
private void inkTest_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
newStroke = null;
            //释放鼠标捕获
inkTest.ReleaseMouseCapture();
}

以上代码已经完成了,画图的显示,其实整个原理很简单

在鼠标左键按下时候开始进行鼠标的捕获,同时创建Stroke(画笔),并且将点放到Stroke的StylusPoints中,在此还可以设置Stroke的DrawingAttributes,然后将Stroke添加

到Ink的Strokes中去。

在鼠标的移动事件中,记录这些鼠标经过的点,添加到Stroked的StylusPoints中去。

在鼠标左键放开事件中的任务很简单就是将Stroke对象清空,保证Move事件中不会对空对象(未按下左键)进行操作,同时释放鼠标的捕获.

下面开始进行Ink的保存工作,保存Ink的原理也很简单,将Xaml代码通过程序转换成Xml的形式,保存的最终是将xml文件保存到独立存储中

 private void SaveInk()
{
XElement element = ConvertStrokeToString(inkTest.Strokes);
using (IsolatedStorageFile stroage = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream fs = stroage.CreateFile("Ink.xml"))
{
using (StreamWriter sw = new StreamWriter(fs))
{
sw.WriteLine(element);
}
}
}
HtmlPage.Window.Alert("保存成功!");
}



该方法传递一个StrokeCollection 类型的参数,也就是Ink的Strokes属性,通过循环处理形成Xml节点

 private XElement ConvertStrokeToString(StrokeCollection originStrokes)
{
//添加命名空间
string xmlnsString = "";

XNamespace xmls = xmlnsString;
XElement XStroke = new XElement(xmls + "StrokeCollection", new XAttribute("xmlns", xmlnsString));
//创建笔画
XElement mystroke;
//遍历当前Ink上的所有Stroke
foreach (Stroke item in originStrokes)
{
//实例化XElement对象,并且把Stroke的属性一一对应放到XElement节点中去
mystroke = new XElement(xmls + "Stroke",
new XElement(xmls + "Stroke.DrawingAttributes",
new XElement(xmls + "DrawingAttributes",
new XAttribute("Color", item.DrawingAttributes.Color),
new XAttribute("OutlineColor", item.DrawingAttributes.OutlineColor),
new XAttribute("Width", item.DrawingAttributes.Width),
new XAttribute("Height", item.DrawingAttributes.Height))));
//定义StylusPoint节点
XElement mypoints = new XElement(xmls + "Stroke.StylusPoints");
//遍历Stroke的StylusPoints
foreach (StylusPoint sp in item.StylusPoints)
{
XElement mypoint = new XElement(xmls + "StylusPoint",
new XAttribute("X", sp.X),
new XAttribute("Y", sp.Y));
mypoints.Add(mypoint);
}
//StylusPoint节点添加到Stroke节点中
mystroke.Add(mypoints);
//将Stroke节点添加到根节点中
XStroke.Add(mystroke);
}
return XStroke;
}

下面这个方法用于加载Xml(从独立存储中),将Xml转换为Ink所需的对象,返回Ink的Strokes对象所需的StrokeCollection类型

 private StrokeCollection ConvertStringToStroke(string xmlName)
{
StrokeCollection strokeCollection = new StrokeCollection();
XNamespace xmlnsString = "";
//使用独立存储来得到保存的Ink文件xml
using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
{
//打开指定的文件
using (IsolatedStorageFileStream fs = storage.OpenFile(xmlName, FileMode.Open, FileAccess.Read))
{
//得到读取流
using (StreamReader sr = new StreamReader(fs))
{
string content = sr.ReadToEnd();
//加载xml
XElement document = XElement.Parse(content);
//得到所有的Stroke标签
var strokes = from stroke in document.Elements(xmlnsString + "Stroke") select stroke;
//遍历Stroke节点
foreach (XElement item in strokes)
{
//得到DrawingAttribute节点
XElement drawingAttribute = item.Element(xmlnsString + "Stroke.DrawingAttributes").Element(xmlnsString + "DrawingAttributes");
//实例化Stroke
Stroke newStroke = new Stroke();
//实例化Stroke的StylusPoint
StylusPoint point = new StylusPoint();
//实例化 StylusPointCollection
StylusPointCollection pointCollection = new StylusPointCollection();
//实例化DrawingAttributes
DrawingAttributes attribute = new DrawingAttributes();
//赋值给DrawingAttributes对象
attribute.Color = ReturnColorFromString(drawingAttribute.Attributes("Color").First().Value);
attribute.OutlineColor = ReturnColorFromString(drawingAttribute.Attributes("OutlineColor").First().Value);
attribute.Width = Convert.ToDouble(drawingAttribute.Attributes("Width").First().Value);
attribute.Height = Convert.ToDouble(drawingAttribute.Attributes("Height").First().Value);
newStroke.DrawingAttributes = attribute;

//得到StylusPointCollection集合
var styluspoints = from p in item.Element(xmlnsString + "Stroke.StylusPoints").Elements(xmlnsString + "StylusPoint") select p;
//遍历StylusPointCollection节点
foreach (XElement pointElement in styluspoints)
{
point.X = Convert.ToDouble(pointElement.Attribute("X").Value);
point.Y = Convert.ToDouble(pointElement.Attribute("Y").Value);
pointCollection.Add(point);
}
//赋值给Stroke的StylusPoints
newStroke.StylusPoints = pointCollection;
//将Stroke添加到strokeCollection
strokeCollection.Add(newStroke);
}
}
}
}
return strokeCollection;
}

由于需要保存颜色则写了自定义方法:

将RGB码转换为Color类型对象

 public Color ReturnColorFromString(string color)
{
string alpha = color.Substring(1, 2);
string red = color.Substring(3, 2);
string green = color.Substring(5, 2);
string blue = color.Substring(7, 2);

byte alphaByte = Convert.ToByte(alpha, 16);
byte redByte = Convert.ToByte(red, 16);
byte greenByte = Convert.ToByte(green, 16);
byte blueByte = Convert.ToByte(blue, 16);
return Color.FromArgb(alphaByte, redByte, greenByte, blueByte);
}



到这里简单的Ink操作,保存以及加载都已ok,望多多指教,另附上源码下载

[InkDemo]

posted @ 2011-10-23 11:59  wangyafei_it  阅读(503)  评论(0编辑  收藏  举报