保存-重建 InkCanvas 笔画






3.85/5 (7投票s)
一篇关于 InkCanvas 和 InkPresenter WPF 控件的文章。
引言
WPF 中最酷的控件之一是 <InkCanvas…/>
。这是 Windows SDK 文档中的定义:“定义一个接收并显示墨迹笔画的区域”。当我看到这个控件可以做什么时,我认为如果我们可以使用这个控件来保存签名并将这些签名保存到数据库中,那就太好了。这个 <InkCanvas…/>
控件在 WPF 中有一个“兄弟”。它的兄弟是 <InkPresenter…/>
。这个控件的定义是:“在表面上渲染墨迹”。
使用代码
当我开始研究 <InkCanvas…/>
属性时,我看到了 Strokes
(它是一个 StrokeCollection
)属性,它“获取或设置由 InkCanvas
收集的墨迹 Stroke
对象的集合”。所以...我们序列化这些 Stroke
,将它们插入到数据库中,当我们想看到它们时,我们从数据库中反序列化数据并在 InkPresenter
控件中绘制 Stroke
。作为它的兄弟,InkPresenter
具有 Strokes
属性。StrokeCollection
具有一个 Add
方法,该方法“将 Stroke
添加到集合中”。现在,我们必须序列化 InkCanvas
具有的所有 Stroke
。但是 Stokes
和 StrokeCollection
没有标记为 Serializable
。因此,我们必须耍一个小技巧。我们构建自己的类,该类具有一个标记为 Serializable
的 Point[][]
(Point
类是来自 System.Windows
命名空间的类)。
[Serializable]
public sealed class MyCustomStrokes
{
public MyCustomStrokes() { }
/// <SUMMARY>
/// The first index is for the stroke no.
/// The second index is for the keep the 2D point of the Stroke.
/// </SUMMARY>
public Point[][] StrokeCollection;
}
为了下载/上传“图像”,我创建了一个名为“Signature
”的类,该类具有两个静态方法
UploadImage(string connectionString, byte[] signatureBytes)
DownloadImage(string connectionString)
可以自定义该方法的签名。在本文中,我只会下载最后一个签名。正如您所看到的,MyCustomStrokes.StrokeCollection
是一个 2D 矩阵,其元素类型为 Point
。您可能想知道我为什么选择 Point
结构。首先,因为它被标记为 Serializable
。其次,Stroke
具有一个 StylusPoints
属性,该属性返回一个 StylusPointCollection : Collection<StylusPoint>
,该属性还有两个属性:X
和 Y
。因此,我们将此信息保存在 MyCustomStrokes.StrokeCollection
中。现在,对于上传部分,我们必须将 StylusPointCollection
保存在 MyCustomStrokes.StrokeCollection
中。为此,我创建了这个方法,该方法具有一个 StrokeCollection
作为其参数
void UploadStrokes(StrokeCollection strokes)
{
if (strokes.Count > 0)
{
MyCustomStrokes customStrokes = new MyCustomStrokes();
customStrokes.StrokeCollection = new Point[strokes.Count][];
for (int i = 0; i < strokes.Count; i++)
{
customStrokes.StrokeCollection[i] =
new Point[this.MyInkCanvas.Strokes[i].StylusPoints.Count];
for (int j = 0; j < strokes[i].StylusPoints.Count; j++)
{
customStrokes.StrokeCollection[i][j] = new Point();
customStrokes.StrokeCollection[i][j].X =
strokes[i].StylusPoints[j].X;
customStrokes.StrokeCollection[i][j].Y =
strokes[i].StylusPoints[j].Y;
}
}
//Serialize our "strokes"
MemoryStream ms = new MemoryStream();
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(ms, customStrokes);
try
{
Signature.UploadImage(ConnectionString, ms.GetBuffer());
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
正如您所看到的,最后,我调用了 Signature.UploadImage(ConnectionString, ms.GetBuffer());
。
/// <summary>
/// Inserts a BLOB into an Oracle DataBase
/// </summary>
/// <param name="connectionString">The ConnectionString to the database
/// <param name="signatureBytes">Signature bytes to be saved
public static void UploadImage(string connectionString, byte[] signatureBytes)
{
try
{
OracleConnection conn = new OracleConnection(connectionString);
conn.Open() ;
OracleCommand cmd = new OracleCommand("insert into " +
"tabel(Signature) values(:Signature)", conn);
cmd.Parameters.Add("Signature", OracleType.Blob) ;
cmd.Parameters["Signature"].Value = signatureBytes;
cmd.ExecuteNonQuery();
conn.Close();
}
catch(Exception ex)
{
throw ex;
}
}
此窗口的 XAML 代码是
<Window x:Class="WindowsApplication2.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="305" Width="820">
<StackPanel>
<InkCanvas x:Name="MyInkCanvas"></InkCanvas>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button x:Name="UploadButton" Grid.Row="0" Grid.Column="0"
Click="UploadButton_Click">Upload</Button>
<Button x:Name="DownloadButton" Grid.Row="0" Grid.Column="1"
Click="DownloadButton_Click">Download</Button>
<Button x:Name="ClearButton" Grid.Row="0" Grid.Column="2"
Click="ClearButton_Click">Clear</Button>
</Grid>
</StackPanel>
</Window>
用于上传和清除的方法是
void UploadButton_Click(object sender, RoutedEventArgs e)
{
this.UploadStrokes(this.MyInkCanvas.Strokes);
}
void ClearButton_Click(object sender, RoutedEventArgs e)
{
this.MyInkCanvas.Strokes.Clear();
}
这就是上传部分的内容。
对于下载部分,我们反过来做。我创建了第二个 WPF 窗口,该窗口仅包含一个 <InkPresenter…/>
控件作为其内容。XAML 代码是
<Window x:Class="WindowsApplication2.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WindowsApplication2" Height="427"
Width="567" Loaded="Window2_Loaded"
>
<Grid>
<InkPresenter x:Name="MyInkPresenter"></InkPresenter>
</Grid>
</Window>
首先,我们从数据库中下载“图像”,反序列化“图像”,然后重建它。我创建了一个方法来完成所有这三件事,该方法在 Window2
的 Loaded
事件中调用
void BuildImage()
{
try
{
//download the image
byte[] signature = Signature.DownloadImage(ConnectionString);
//deserialize it
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream(signature);
MyCustomStrokes customStrokes = bf.Deserialize(ms) as MyCustomStrokes;
//rebuilt it
for (int i = 0; i < customStrokes.StrokeCollection.Length; i++)
{
if (customStrokes.StrokeCollection[i] != null)
{
StylusPointCollection stylusCollection = new
StylusPointCollection(customStrokes.StrokeCollection[i]);
Stroke stroke = new Stroke(stylusCollection);
StrokeCollection strokes = new StrokeCollection();
strokes.Add(stroke);
this.MyInkPresenter.Strokes.Add(strokes);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Signature
类中的 DownloadImage
方法是
/// <summary>
/// Downloads a byte[] from a BLOB
/// </summary>
/// <param name="connectionString"></param>
/// <returns>The BLOB as byte[]</returns>
public static byte[] DownloadImage(string connectionString)
{
try
{
OracleConnection conn = new OracleConnection(connectionString);
conn.Open();
OracleCommand cmd = new OracleCommand("select signature" +
" from tabel order by id desc", conn);
byte[] barrImg = cmd.ExecuteScalar() as byte [];
conn.Close();
return barrImg;
}
catch(Exception ex)
{
throw ex;
}
}
处理 Window1
中 Download 按钮的方法是
void DownloadButton_Click(object sender, RoutedEventArgs e)
{
Window2 win = new Window2();
win.Height = this.Height;
win.Width = this.Width;
win.Show();
}
就是这样!