VB のたまご

作成日: 2015/12/27, 更新日: 2015/12/29


タプルを学んで、複数データをまとめて持ち運ぼう

タプルって何?

  •  通常、変数には1つのデータしかセットできません。それは、1つ分の管理しかしないからです。 しかし、VB2010 からタプルが使えるようになりました。 タプルを使うと、複数のデータをセットすることができます。タプルは、複数のデータをまとまりで扱いたい時に使用します。
  • Dim person = Tuple.Create("taro", 25, New Date(1990, 12, 15))
    Console.WriteLine("name = {0}", person.Item1)
    Console.WriteLine("age = {0}", person.Item2)
    Console.WriteLine("birthday = {0}", person.Item3)
    
    ' 出力結果
    name = taro
    age = 25
    birthday = 1990/12/15 0:00:00
    

  •  タプルは、Create メソッドを呼び出して、まとめたい各データをセットして作成します。 セットできるデータは、タプルはジェネリック型なので、いろいろな型のデータをセットできます。 初期値をセットすると、初期値の型と、その初期値にアクセスするためのプロパティが自動的に決まります。 プロパティ名は固定で、データをセットした順に、Item1, Item2, ..., ItemX となります。

  •  ただし、ItemX というプロパティ名なので、Name とか Age 等のように名前推測ができません。 また、タプルは一度セットした値を書き換えることができません。(ReadOnly Property になっている)。 このため、作業用メソッド内等、狭い範囲で使った方が(他の人の)混乱が少なくて、いいのかなと思っています。 そんなタプルですが、本記事を作成するきっかけとなった出来事の解決策として、今回使用しています。


スポンサーリンク


ドラッグアンドドロップ機能を作る、通常版

  •  WinForms なクライアントアプリケーションでドラッグアンドドロップを実装する場合、 画面のプロパティと2つのイベントを利用して実現します。 Form.AllowDrop プロパティを True に変更するのと、Form.DragEnter イベントと、Form.DragDrop イベントです。

  •  それではサンプルを見てみます。今回は、エクスプローラ上からのドラッグアンドドロップを想定します。 種類はファイルのドロップで、vb ファイルを1つだけドラッグアンドドロップした場合のみ、機能します。 フォルダや複数ファイル、テキストファイルの場合は、ドラッグアンドドロップを受け付けません。

  • Imports System.IO
    
    Public Class Form1
    
        ' Me.AllowDrop = True にしておく
    
        Private Sub Form1_DragDrop(sender As Object, e As DragEventArgs) Handles Me.DragDrop
    
            ' エクスプローラ上から、vb ファイルを1つだけ受け取りたい
            ' それ以外は受け付けない
            If Not e.Data.GetDataPresent(DataFormats.FileDrop) Then
                Exit Sub
            End If
    
            Dim dropItems As Object = e.Data.GetData(DataFormats.FileDrop, False)
            Dim dropFiles() As String = CType(dropItems, String())
    
            If dropFiles.Length <> 1 Then
                Exit Sub
            End If
    
            Dim dropFile = dropFiles(0)
            If Not File.Exists(dropFile) OrElse Path.GetExtension(dropFile).ToLower <> ".vb" Then
                Exit Sub
            End If
    
            ' 条件を満たしたファイルに対して、何らかの処理を実施
            Me.Text = New FileInfo(dropFile).Name
    
        End Sub
    
        Private Sub Form1_DragEnter(sender As Object, e As DragEventArgs) Handles Me.DragEnter
    
            ' エクスプローラ上から、vb ファイルを1つだけ受け取りたい
            ' それ以外は受け付けない
            If Not e.Data.GetDataPresent(DataFormats.FileDrop) Then
                Exit Sub
            End If
    
            Dim dropItems As Object = e.Data.GetData(DataFormats.FileDrop, False)
            Dim dropFiles() As String = CType(dropItems, String())
    
            If dropFiles.Length <> 1 Then
                Exit Sub
            End If
    
            Dim dropFile = dropFiles(0)
            If Not File.Exists(dropFile) OrElse Path.GetExtension(dropFile).ToLower <> ".vb" Then
                Exit Sub
            End If
    
            ' 条件を満たしたファイルの場合、カーソルアイコンを変更
            e.Effect = DragDropEffects.Copy
    
        End Sub
    
    End Class
    
  •  このサンプルを実行すると、vb ファイルを1つだけドラッグアンドドロップした場合だけ、カーソルのアイコンが変わります。 それ以外の場合、例えばフォルダをドラッグアンドドロップしても、カーソルのアイコンは変わらず、タイトルも変わりません。

スポンサーリンク


ドラッグアンドドロップ機能を作る、共通処理版

  •  このままでも、ドラッグアンドドロップとしてちゃんと機能しているので、いいっちゃいいんですが、 2つのイベント内に書いた判定処理が、もろにかぶっています。 同じ処理は共通化してしまいたい。と思わずにはいられません。

  •  ただ、共通化する場合、以下のような問題があります。 一方は、ドラッグアンドドロップ ができるのかどうか True/False を判断したい、 もう一方は、ドラッグアンドドロップができる場合は、フルパスを取得したい、と微妙に後続処理が違っています。 さらに、判断処理の途中でファイルを準備しているので、この中で準備できたファイルパスを利用したいところです。

  •  以下のサンプルでは、共通処理を抜き出して、ファイルパスを返すように共通メソッド化したものです。
  • Imports System.IO
    
    Public Class Form1
    
        ' Me.AllowDrop = True にしておく
    
        Private Sub Form1_DragDrop(sender As Object, e As DragEventArgs) Handles Me.DragDrop
    
            Dim dropFile = Me.GetDragDropFile(e)
            If dropFile = String.Empty Then
                Exit Sub
            End If
    
            ' 条件を満たしたファイルに対して、何らかの処理を実施
            Me.Text = New FileInfo(dropFile).Name
    
        End Sub
    
        Private Sub Form1_DragEnter(sender As Object, e As DragEventArgs) Handles Me.DragEnter
    
            Dim dropFile = Me.GetDragDropFile(e)
            If dropFile = String.Empty Then
                Exit Sub
            End If
    
            ' 条件を満たしたファイルの場合、カーソルアイコンを変更
            e.Effect = DragDropEffects.Copy
    
        End Sub
    
        ' 判定がかぶっているので共通化したい
        ' 一方は、True/False を判断したい、一方は、ファイルパスを取得したい
    
        ' 共通メソッドを使うなら、
        ' True/False を返しつつ、クラスメンバーにフルパスをセットしておいて、そっちを見てもらうか。
        ' それとも、戻り値に、条件に一致する場合のみフルパスをセットしておいて、フルパスの有無で、True/False を判断するか。
        ' はたまた、戻り値専用のデータクラスを作って、Boolean と String のプロパティを準備するか。
    
        Private Function GetDragDropFile(e As DragEventArgs) As String
    
            ' エクスプローラ上から、vb ファイルを1つだけ受け取りたい
            ' それ以外は受け付けない
    
            If Not e.Data.GetDataPresent(DataFormats.FileDrop) Then
                Return String.Empty
            End If
    
            Dim dropItems As Object = e.Data.GetData(DataFormats.FileDrop, False)
            Dim dropFiles() As String = CType(dropItems, String())
    
            If dropFiles.Length <> 1 Then
                Return String.Empty
            End If
    
            Dim dropFile = dropFiles(0)
            If Not File.Exists(dropFile) OrElse Path.GetExtension(dropFile).ToLower <> ".vb" Then
                Return String.Empty
            End If
    
            Return dropFile
    
        End Function
    
    
    End Class
    
  •  最初の処理に比べて、機能を分割したのでシンプルに短くなり、処理内容が見やすくなりました。 ただあと一歩ってところで、つっかかっている部分があります。それは、DragEnter イベント内の判定です。 ここでは、ドラッグアンドドロップ ができるのかどうか、つまり True/False で判断したいのです。ファイル有無ではなく。

  •  戻り値は1つなので、Boolean 型か String 型のどちらかしかありません。 そんな時に、タプルが役立ちます。


ドラッグアンドドロップ機能を作る、タプル版

  •  先程の共通処理の戻り値をタプルに変えて、それぞれの受け取り個所を修正します。

  • Imports System.IO
    
    Public Class Form1
    
        Private Sub Form1_DragDrop(sender As Object, e As DragEventArgs) Handles Me.DragDrop
    
            Dim dropFile = Me.CanDragDrop(e).Item2
            If dropFile = String.Empty Then
                Exit Sub
            End If
    
            ' 条件を満たしたファイルに対して、何らかの処理を実施
            Me.Text = New FileInfo(dropFile).Name
    
        End Sub
    
        Private Sub Form1_DragEnter(sender As Object, e As DragEventArgs) Handles Me.DragEnter
    
            If Not Me.CanDragDrop(e).Item1 Then
                Exit Sub
            End If
    
            ' 条件を満たしたファイルの場合、カーソルアイコンを変更
            e.Effect = DragDropEffects.Copy
    
        End Sub
    
        Private Function CanDragDrop(e As DragEventArgs) As Tuple(Of Boolean, String)
    
            ' エクスプローラ上から、vb ファイルを1つだけ受け取りたい
            ' それ以外は受け付けない
    
            Dim x = Tuple.Create(False, String.Empty)
    
            If Not e.Data.GetDataPresent(DataFormats.FileDrop) Then
                Return x
            End If
    
            Dim dropItems As Object = e.Data.GetData(DataFormats.FileDrop, False)
            Dim dropFiles() As String = CType(dropItems, String())
    
            If dropFiles.Length <> 1 Then
                Return x
            End If
    
            Dim dropFile = dropFiles(0)
            If Not File.Exists(dropFile) OrElse Path.GetExtension(dropFile).ToLower <> ".vb" Then
                Return x
            End If
    
            x = Tuple.Create(True, dropFile) ' 更新できないので、再度インスタンス生成
            Return x
    
        End Function
    
    
    End Class
    
  •  このように、戻り値を2つ受け取り、それぞれの処理内で使用することによって、先程の処理より分かりやすくなりました。

  •  最後までこの記事を読んでいただき、ありがとうございました。


スポンサーリンク