PictureBoxにマウスでフリーハンドの絵を描く様子を保存し、再生する。

昨日のコードに PictureBox を追加する。

  • 「記録開始」ボタンを押すとマウス操作の記録開始。(ボタン表示は「保存」と変わる)
  • 「1」〜「5」のボタンを押すと上の青いラベルに数字が挿入される。
  • 下の白いキャンバス(PictureBox)で左ボタン押しつつマウスを動かすと線が描画される。
  • 「保存」ボタンを押すと、操作履歴がファイルに保存される。
  • 「読込」ボタンを押して保存ファイルを読み込むと(ビデオ再生の様に)文字入力/絵の描画が行われる。

「1」〜「5」のボタンは Button1〜Button5に、「表示をクリア」「記録開始」「読込」ボタンはButton100〜Button102に割り振っている(記録対象となる編集ボタンはButton6以降に後から追加できる様に!)。

Imports System.Threading
Imports System.Drawing
Imports System.Drawing.Drawing2D

Public Structure CommandClass
    Public name As String
    Public arg As String
    Public Sub New(ByVal s1 As String, ByVal s2 As String)
        Me.name = s1
        Me.arg = s2
    End Sub
End Structure

Public Class Form1
    Private status As Integer = 0 '0:定常状態 1:記録中
    Private commands As Collection = New Collection()
    Private commandPreTime As Date
    Private timerDelegate As TimerCallback = New TimerCallback(AddressOf timerFunc)
    Private myTimer As Timer = New Timer(timerDelegate, Nothing, Timeout.Infinite, -1)
    Private myCount As Integer = 0
    Private preX As Integer = -1
    Private preY As Integer = -1

    Private Function eventInvoke(ByVal cmd As String, ByVal code As String) As String
        If cmd = "r" Or cmd = "p" Then
            Label1.Text = Label1.Text & code
        ElseIf cmd = "t" Or cmd = "m" Then
            Dim xy As String() = code.Split(",")
            Dim x As Integer = CInt(xy(0))
            Dim y As Integer = CInt(xy(1))
            If preX >= 0 Then
                Dim g As Graphics = PictureBox1.CreateGraphics
                g.DrawLine(Pens.Red, preX, preY, x, y)
                g.Dispose()
            End If
            preX = x
            preY = y
        End If

        If status = 1 Then '記録中
            If commands.Count > 0 Then
                Dim span As TimeSpan = Now() - commandPreTime
                commands.Add(New CommandClass("s", span.TotalSeconds))
            End If
            commandPreTime = Now()
            commands.Add(New CommandClass(cmd, code))
        End If
        eventInvoke = code
    End Function

    Private Sub Button_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
            Handles Button1.Click, Button2.Click, Button3.Click, Button4.Click, Button5.Click
        eventInvoke("r", sender.Text)
    End Sub

    Private Sub Button100_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button100.Click
        If status = 0 Then
            commands.Clear()
            preX = -1
            preY = -1
            status = 1
            Button100.Text = "保存する"
        Else
            status = 0
            Dim sfd As New SaveFileDialog()
            sfd.FileName = "events.txt"
            'sfd.InitialDirectory = "C:\"
            sfd.Filter = "TEXTファイル(*.txt)|*.txt|すべてのファイル(*.*)|*.*"
            sfd.FilterIndex = 1 '初期表示は*.txtだけ
            sfd.Title = "保存先のファイルを選択してください"
            If sfd.ShowDialog() = DialogResult.OK Then
                Dim stream As System.IO.Stream = sfd.OpenFile()
                If Not (stream Is Nothing) Then
                    Dim sw As New System.IO.StreamWriter(stream)
                    sw.WriteLine("# s:指定秒数SLEEPする / t:タッチパネルイベント発行 / " & _
                                 "p:指定されたキーのPressイベントを発行 / r:指定されたキーのReleaseイベントを発行")
                    For Each com In commands
                        sw.WriteLine(com.name & " " & com.arg.ToString)
                    Next
                    sw.Close()
                    stream.Close()
                End If
            End If
            Button100.Text = "記録開始"
        End If

    End Sub

    Private Sub Button101_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button101.Click
        Dim ofd As New OpenFileDialog()
        ofd.FileName = "events.txt"
        'ofd.InitialDirectory = "C:\"
        ofd.Filter = "TEXTファイル(*.txt)|*.txt|すべてのファイル(*.*)|*.*"
        ofd.FilterIndex = 1
        ofd.Title = "開くファイルを選択してください"
        ofd.RestoreDirectory = True
        If ofd.ShowDialog() = DialogResult.OK Then
            Dim stream As System.IO.Stream = ofd.OpenFile()
            If Not (stream Is Nothing) Then
                Dim sr As New System.IO.StreamReader(stream)
                Dim sarray As String()
                commands.Clear()
                preX = -1
                preY = -1
                While sr.Peek() > -1
                    Dim s As String = sr.ReadLine()
                    If s.Length < 3 Or s.StartsWith("#") Then
                        ' Do nothing
                        'Next While 'break; とか next に該当するのって何?
                    Else
                        sarray = s.Split(" ")
                        commands.Add(New CommandClass(sarray(0), sarray(1)))
                    End If
                End While
                sr.Close()
                stream.Close()
                myCount = 1
                myTimer.Change(10, Timeout.Infinite)
            End If
        End If
    End Sub

    Private Sub Button102_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button102.Click
        Dim g As Graphics = PictureBox1.CreateGraphics()
        Label1.Text = ""
        g.Clear(PictureBox1.BackColor)
        g.Dispose()
    End Sub

    Delegate Function FuncDelegate(ByVal cmd As String, ByVal arg As String)

    Public Sub timerFunc(ByVal o As Object)
        While myCount <= commands.Count
            Dim com As CommandClass = commands(myCount)
            myCount = myCount + 1
            If com.name = "s" Then
                myTimer.Change(Convert.ToInt32(com.arg * 1000), Timeout.Infinite)
                Exit Sub
            Else
                Invoke(New FuncDelegate(AddressOf eventInvoke), com.name, com.arg)
            End If
        End While
        myTimer.Change(Timeout.Infinite, Timeout.Infinite)
        MessageBox.Show("再生を終了しました")
    End Sub

    Private Sub PictureBox1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)_
            Handles PictureBox1.MouseDown
        eventInvoke("t", e.X & "," & e.Y)
    End Sub

    Private Sub PictureBox1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)_
            Handles PictureBox1.MouseUp
        eventInvoke("T", e.X & "," & e.Y)
    End Sub

    Private Sub PictureBox1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)_
            Handles PictureBox1.MouseMove
        If e.Button = Windows.Forms.MouseButtons.Left Then
            eventInvoke("m", e.X & "," & e.Y)
        End If
    End Sub
End Class