65.9K
CodeProject 正在变化。 阅读更多。
Home

保存-重建 InkCanvas 笔画

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.85/5 (7投票s)

2006年11月29日

CPOL

2分钟阅读

viewsIcon

95027

downloadIcon

2001

一篇关于 InkCanvas 和 InkPresenter WPF 控件的文章。

InkCanvas and InkPresenter

引言

WPF 中最酷的控件之一是 <InkCanvas…/>。这是 Windows SDK 文档中的定义:“定义一个接收并显示墨迹笔画的区域”。当我看到这个控件可以做什么时,我认为如果我们可以使用这个控件来保存签名并将这些签名保存到数据库中,那就太好了。这个 <InkCanvas…/> 控件在 WPF 中有一个“兄弟”。它的兄弟是 <InkPresenter…/>。这个控件的定义是:“在表面上渲染墨迹”。

使用代码

当我开始研究 <InkCanvas…/> 属性时,我看到了 Strokes(它是一个 StrokeCollection)属性,它“获取或设置由 InkCanvas 收集的墨迹 Stroke 对象的集合”。所以...我们序列化这些 Stroke,将它们插入到数据库中,当我们想看到它们时,我们从数据库中反序列化数据并在 InkPresenter 控件中绘制 Stroke。作为它的兄弟,InkPresenter 具有 Strokes 属性。StrokeCollection 具有一个 Add 方法,该方法“将 Stroke 添加到集合中”。现在,我们必须序列化 InkCanvas 具有的所有 Stroke。但是 StokesStrokeCollection 没有标记为 Serializable。因此,我们必须耍一个小技巧。我们构建自己的类,该类具有一个标记为 SerializablePoint[][]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>,该属性还有两个属性:XY。因此,我们将此信息保存在 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>

首先,我们从数据库中下载“图像”,反序列化“图像”,然后重建它。我创建了一个方法来完成所有这三件事,该方法在 Window2Loaded 事件中调用

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();
}

就是这样!

© . All rights reserved.