# 拓思德点阵笔

引用

```csharp
using TStudyDigitalPen.HID;
```

代码

```csharp
#region 拓思德点阵笔

    #region 值初始化

    // 不同尺寸点阵纸点阵宽高尺寸计算方法为：纸张物理尺寸（毫米）/0.3 *8,详见 开发必读.pdf 文档

    /// <summary>
    /// A4点阵纸点阵宽度
    /// </summary>
    private const int A4_WIDTH = 5600;

/// <summary>
/// A4点阵纸点阵高度
/// </summary>
private const int A4_HEIGHT = 7920;

///// <summary>
///// 画板
///// </summary>
//private Graphics graphics;

/// <summary>
/// 笔画坐标数组
/// </summary>
//private List<System.Drawing.Point> stroke;

/// <summary>
/// 笔序列号
/// </summary>
private string penSerial;

/// <summary>
/// 笔是否在点
/// </summary>
private bool isPenDown;

//当前点阵地址
private string currentPageSerial = string.Empty;

//不同点阵地址对应的笔迹绘制图片，用于实现在不同点阵地址书写切换时，显示书写内容自动切换
//本例图片放在内存中存储，对于大量或者需要在多个点阵地址对应图片进行切换演示，建议将图片存储到文件，以免内存溢出
private Dictionary<string, System.Drawing.Image> pagesDic = new Dictionary<string, System.Drawing.Image>();

#endregion 值初始化

    public void InitTSDPen()
{
    //stroke = new List<System.Drawing.Point>();
    //获取点阵笔实例，并绑定点阵笔事件
    //将授权文件内容传入，获取点阵笔对象实例
    APP.digitalPen = DigitalPenHID.GetInstance(certificates.MyLicense.Bytes);
    //绑定笔连接事件
    APP.digitalPen.PenConnected += OnTSDPenConnect;
    //绑定笔断开事件
    APP.digitalPen.PenDisconnect += OnTSDPenDisconnect;
    //绑定笔书写输出坐标事件
    APP.digitalPen.PenCoordinate += OnTSDPenCoordinate;
    //绑定抬笔事件
    APP.digitalPen.PenUp += OnTSDPenUp;
    //绑定落笔事件
    APP.digitalPen.PenDown += OnTSDPenDown;
    APP.digitalPen.PenBatteryCapacity += OnTSDBatteryCapacity;
    APP.digitalPen.PenMemoryFillLevel += OnTSDMemoryFillLevel;
    //完成初始化点阵笔，开始与点阵笔通信
    ERROR_CODE ER = APP.digitalPen.Start();

    //启动接收笔数据，完成初始化工作
    ERROR_CODE rc = APP.digitalPen.Start();
    //判断是否成功
    if (ER != ERROR_CODE.ERROR_OK)
    {
        MessageWindow.Show("初始化失败，授权过期，返回值：" + ER.ToString());
    }
}

/// <summary>
/// 落笔
/// </summary>
/// <param name="time">
/// 时间戳，1970年1月1日到现在的总毫秒数
/// </param>
/// <param name="penSerial">
/// 点阵笔序列号
/// </param>
/// <param name="penType">
/// 点阵笔型号编号
/// </param>
private void OnTSDPenDown(ulong time, string penSerial, int penType)
{
    if (CheckAccess())
    {
        Action<ulong, string, int> action = new Action<ulong, string, int>(OnTSDPenDown);
        Dispatcher.Invoke(action, new object[] { time, penSerial, penType });
    }
    else
    {

        isPenDown = true;

    }
}

/// <summary>
/// 抬笔
/// </summary>
/// <param name="time">
/// 时间戳，1970年1月1日到现在的总毫秒数
/// </param>
/// <param name="penSerial">
/// 点阵笔序列号
/// </param>
/// <param name="penType">
/// 点阵笔型号编号
/// </param>
private void OnTSDPenUp(ulong time, string penSerial, int penType)
{
    if (CheckAccess())
    {
        Action<ulong, string, int> action = new Action<ulong, string, int>(OnTSDPenUp);
        Dispatcher.Invoke(action, new object[] { time, penSerial, penType });
    }
    else
    {
        isPenDown = false;
        APP.PenSerial = penSerial;

    }
    if (APP.PageContextData.currpage > 0)
    {
        Dispatcher.Invoke(new Action(() =>
                                     {
                                         myblackboard.changepages(0, 0, true, Color, PenSize, APP.PageContextData.currpage - 1, 0);
                                     }));
    }
}

/// <summary>
/// 笔断开
/// </summary>
/// <param name="time">
/// 时间戳，1970年1月1日到现在的总毫秒数
/// </param>
/// <param name="penSerial">
/// 点阵笔序列号
/// </param>
/// <param name="penType">
/// 点阵笔型号编号
/// </param>
private void OnTSDPenDisconnect(ulong time, string penSerial, int penType)
{
    if (CheckAccess())
    {
        Action<ulong, string, int> action = new Action<ulong, string, int>(OnTSDPenDisconnect);
        Dispatcher.Invoke(action, new object[] { time, penSerial, penType });
    }
    else
    {
        APP.PenSerial = penSerial;
        APP.PenStatus = false;
        UpdateDevStatus();
    }
}

/// <summary>
/// 笔连接
/// </summary>
/// <param name="time">
/// 时间戳，1970年1月1日到现在的总毫秒数
/// </param>
/// <param name="penSerial">
/// 点阵笔序列号
/// </param>
/// <param name="penType">
/// 点阵笔型号编号
/// </param>
private void OnTSDPenConnect(ulong time, string penSerial, int penType)
{
    if (CheckAccess())
    {
        Action<ulong, string, int> action = new Action<ulong, string, int>(OnTSDPenConnect);
        Dispatcher.Invoke(action, new object[] { time, penSerial, penType });
    }
    else
    {
        APP.PenSerial = penSerial;
        APP.PenStatus = true;
        this.penSerial = penSerial;
        //连接后，在获取笔数据前，可以清除笔内的历史数据
        //APP.digitalPen.ClearMemory(penSerial);

        //开始接收笔数据
        APP.digitalPen.GetPenData(penSerial);
        UpdateDevStatus();
    }
}

/// <summary>
/// 电池电量
/// </summary>
/// <param name="time">
/// </param>
/// <param name="penSerial">
/// </param>
/// <param name="penType">
/// </param>
/// <param name="capacity">
/// </param>
private void OnTSDBatteryCapacity(ulong time, string penSerial, int penType, byte capacity)
{
    if (CheckAccess())
    {
        Action<ulong, string, int, byte> action = new Action<ulong, string, int, byte>(OnTSDBatteryCapacity);
        Dispatcher.Invoke(action, new object[] { time, penSerial, penType, capacity });
    }
    else
    {
        //System.Windows.MessageWindow.Show("电池电量:" + capacity.ToString());
    }
}

/// <summary>
/// 已用存储
/// </summary>
/// <param name="time">
/// </param>
/// <param name="penSerial">
/// </param>
/// <param name="penType">
/// </param>
/// <param name="fillLevel">
/// </param>
private void OnTSDMemoryFillLevel(ulong time, string penSerial, int penType, byte fillLevel)
{
    if (CheckAccess())
    {
        Action<ulong, string, int, byte> action = new Action<ulong, string, int, byte>(OnTSDMemoryFillLevel);
        Dispatcher.Invoke(action, new object[] { time, penSerial, penType, fillLevel });
    }
    else
    {
    }
}

/// <summary>
/// 笔书写，收到坐标
/// </summary>
/// <param name="time">
/// 时间戳，1970年1月1日到现在的总毫秒数
/// </param>
/// <param name="penSerial">
/// 点阵笔序列号
/// </param>
/// <param name="penType">
/// 点阵笔型号编号
/// </param>
/// <param name="pageSerial">
/// 点阵地址
/// </param>
/// <param name="cx">
/// x坐标
/// </param>
/// <param name="cy">
/// y坐标
/// </param>
/// <param name="force">
/// 压力值
/// </param>
private void OnTSDPenCoordinate(ulong time, string penSerial, int penType, string pageSerial, int cx, int cy, byte force)
{
    if (CheckAccess())
    {
        Action<ulong, string, int, string, int, int, byte> ac = new Action<ulong, string, int, string, int, int, byte>(OnTSDPenCoordinate);
        Dispatcher.Invoke(ac, new object[] { time, pageSerial, penType, pageSerial, cx, cy, force });
    }
    else
    {
        //判断是否是落笔后输出的坐标，在设置悬浮模式下，落笔前的悬浮坐标不绘制
        if (!isPenDown)
        {
            return;
        }
        //stroke.Add(new System.Drawing.Point(cx, cy));

        double PropW = blackboard_canvas.ActualWidth / A4_WIDTH;
        double PropH = blackboard_canvas.ActualHeight / A4_HEIGHT;
        //点
        double tempX = cx * PropW;
        double tempY = cy * PropH;
        //pageSerial //点阵IP地址  与打印的页面关联
        if (APP.PageContextData.currpage > 0)
        {
            Dispatcher.Invoke(new Action(() =>
                                         {
                                             float Pressure = force / 100f;
                                             //myblackboard.changepages(testX, testY,false);
                                             myblackboard.changepages(tempX, tempY, false, Color, PenSize, APP.PageContextData.currpage - 1, Pressure);

                                             #region 设置滚动条位置

                                                 //点在显示页面上方
                                                 if (tempY < ScroMain.VerticalOffset)
                                                 {
                                                     //滚动条当前位置
                                                     double RollCurrentLocation = ScroMain.VerticalOffset;
                                                     //向上滚动至以点为中心需要滚动的距离
                                                     double UpRoll = (RollCurrentLocation - tempY) + (ScroMain.ActualHeight / 2);
                                                     //如果小于0则等于0
                                                     double RollLocation = RollCurrentLocation - UpRoll;
                                                     if (RollLocation < 0)
                                                     {
                                                         RollLocation = 0;
                                                     }
                                                     ////滚动条实际偏移量
                                                     //double RollOffset = RollCurrentLocation - RollLocation;
                                                     ScroMain.ScrollToVerticalOffset(RollLocation);
                                                 }
                                             //点在显示页面下方
                                             if (tempY > ScroMain.VerticalOffset + ScroMain.ActualHeight)
                                             {
                                                 //滚动条当前位置
                                                 double RollCurrentLocation = ScroMain.VerticalOffset;
                                                 //向下滚动至以点为中心需要滚动的距离
                                                 double DownRoll = (tempY - RollCurrentLocation) - (ScroMain.ActualHeight / 2);
                                                 //如果小于0则等于0
                                                 double RollLocation = RollCurrentLocation + DownRoll;
                                                 //滚动条最大滚动值
                                                 double ScrollbarMaxNum = GridM.ActualHeight - ScroMain.ActualHeight;
                                                 if (RollLocation > ScrollbarMaxNum)
                                                 {
                                                     RollLocation = ScrollbarMaxNum;
                                                 }
                                                 ////滚动条实际偏移量
                                                 //double RollOffset = RollLocation-RollCurrentLocation;
                                                 ScroMain.ScrollToVerticalOffset(RollLocation);
                                             }

                                             #endregion 设置滚动条位置

                                                 if (tempX > 0 && tempY > 0)
                                                 {
                                                     //System.Windows.Point getP = blackboard_canvas.PointToScreen(new System.Windows.Point(testX, testY));
                                                     System.Windows.Point getP = ScroMain.PointToScreen(new System.Windows.Point(tempX, tempY - ScroMain.VerticalOffset));
                                                     SetCursorPos((int)getP.X, (int)getP.Y);
                                                 }
                                         }));
        }
    }
}

/// <summary>
/// 清空笔内存储
/// </summary>
public void ClearPenStorage()
{
    if (!string.IsNullOrEmpty(penSerial))
    {
        APP.digitalPen.ClearMemory(penSerial);
    }
}

/// <summary>
/// 获取剩余电量
/// </summary>
public void GetPenElectricityQuantity()
{
    if (!string.IsNullOrEmpty(penSerial))
    {
        APP.digitalPen.GetBatteryCapacity(penSerial);
    }
}

/// <summary>
/// 获取存储空间
/// </summary>
public void GetUsedStorage()
{
    if (!string.IsNullOrEmpty(penSerial))
    {
        APP.digitalPen.GetMemoryFillLevel(penSerial);
    }
}

/// <summary>
/// 开启悬浮
/// </summary>
public void 开启悬浮()
{
    if (!string.IsNullOrEmpty(penSerial))
    {
        APP.digitalPen.SetPenHoverMode(true, penSerial);
    }
}

/// <summary>
/// 关闭悬浮
/// </summary>
public void 关闭悬浮()
{
    if (!string.IsNullOrEmpty(penSerial))
    {
        APP.digitalPen.SetPenHoverMode(false, penSerial);
    }
}

/// <summary>
/// 引用user32.dll动态链接库（windows api）， 使用库中定义 API：SetCursorPos 设置光标位置
/// </summary>
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern int SetCursorPos(int x, int y);

#endregion 拓思德点阵笔
```

