C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[2]: VB.NET OpenFileDialog について


(過去ログ 171 を表示中)

[トピック内 8 記事 (1 - 8 表示)]  << 0 >>

■98591 / inTopicNo.1)  VB.NET OpenFileDialog について
  
□投稿者/ BanditH (1回)-(2021/12/06(Mon) 10:19:37)

分類:[VB.NET/VB2005 以降] 

Winsdows10 Pro(64bit) VB.NET(VS2017) FW=4.5 で
初期表示ファイル名に長いファイル名を指定して OpenFileDialog を使用してファイル選択をしようとしたときに、ファイル名が選択状態なって前の部分が隠れてしまい、後ろの部分しか表示されません。
Homeキー等を押さないとファイル名全体を確認できません。
解決方法はあるのでしょうか?
よろしくお願いします。

    Dim CsvFile As String = ""
    Dim ofd As New OpenFileDialog()
    ofd.Filter = "CSVファイル(*.csv)|*.csv"
    ofd.Title = "CSVファイルを選択してください"
    ofd.FileName = "hogehogehogehoge.csv"
    If ofd.ShowDialog() = DialogResult.OK Then
        CsvFile = ofd.FileName
    End If



引用返信 編集キー/
■98592 / inTopicNo.2)  Re[1]: VB.NET OpenFileDialog について
□投稿者/ kiku (243回)-(2021/12/06(Mon) 17:39:28)
No98591 (BanditH さん) に返信
> Winsdows10 Pro(64bit) VB.NET(VS2017) FW=4.5 で
> 初期表示ファイル名に長いファイル名を指定して OpenFileDialog を使用してファイル選択をしようとしたときに、ファイル名が選択状態なって前の部分が隠れてしまい、後ろの部分しか表示されません。
> Homeキー等を押さないとファイル名全体を確認できません。
> 解決方法はあるのでしょうか?
> よろしくお願いします。

思いつく方法と2つ考えました。
案1)OpenFileDialogの使用をやめて、自分で必要機能をFormで作る。
案2)OpenFileDialogでOKで確定後、パスすべてを確認できるFormを自分で作り、そこで確認させる。
引用返信 編集キー/
■98593 / inTopicNo.3)  Re[2]: VB.NET OpenFileDialog について
□投稿者/ くま (39回)-(2021/12/06(Mon) 18:46:34)
kiku さんの書かれている通り
まず
1. OpenFileDialogに選択を解除するプロパティ等存在しない。
2. OpenFileDialogを拡張しようとしてもクラス自体拡張禁止になっている
3. 1から大元のダイアログ系APIを使用した形で作り直す。これは大変面倒です。
詳しくは「vb.net windows API GetOpenFileName GetSaveFileName」辺りで検索してください
4. 自分でFormを作り直す。これは大変面倒です。


5. ダイアログを開く前に非同期タスクを立ち上げてダイアログが開いたタイミングを取得
コントロールに[HOME]キーを送信して選択を解除する。

というのがあります。
(長いので分けます)

引用返信 編集キー/
■98594 / inTopicNo.4)  Re[3]: VB.NET OpenFileDialog について
□投稿者/ くま (40回)-(2021/12/06(Mon) 18:48:35)
2021/12/06(Mon) 19:21:26 編集(投稿者)
モジュール OpenFileDialogEx.vb
'----- ここから
Imports System.Runtime.InteropServices
Imports System.Text

Module OpenFileDialogEx
    Private Delegate Function WNDENUMPROC(ByVal hWnd As IntPtr, ByVallparam As IntPtr) As Boolean

    <DllImport("user32.dll")>
    Private Function EnumWindows(lpEnumFunc As WNDENUMPROC, lparam As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
    End Function
    <DllImport("user32.dll")>
    Private Function EnumChildWindows(ByVal hwndParent As Integer, ByVal lpEnumFunc As WNDENUMPROC, ByVal lParam As Integer) As Integer
    End Function
    <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
    Private Function GetWindowText(hWnd As IntPtr, lpString As StringBuilder, nMaxCount As Integer) As Integer
    End Function
    <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
    Private Function GetWindowTextLength(hWnd As IntPtr) As Integer
    End Function
    <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
    Private Function GetClassName(hWnd As IntPtr, lpClassName As StringBuilder, nMaxCount As Integer) As Integer
    End Function
    <DllImport("user32.dll")>
    Private Function SetForegroundWindow(hWnd As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
    End Function

    <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)>
    Private Structure KEYBDINPUT
        Public wVk As Short
        Public wScan As Short
        Public dwFlags As Integer
        Public time As Integer
        Public dwExtraInfo As IntPtr
    End Structure
    <StructLayout(LayoutKind.Sequential, Size:=28)>
    Private Structure INPUT
        Public type As Integer
        Public ki As KEYBDINPUT
    End Structure

    <DllImport("user32.dll", CharSet:=CharSet.Auto)>
    Private Function SendInput(ByVal nInputs As Integer, ByVal pInputs() As INPUT, ByVal cbsize As Integer) As Integer
    End Function
    <DllImport("user32.dll", CharSet:=CharSet.Auto)>
    Private Function MapVirtualKey(ByVal wCode As Integer, ByVal wMapType As Integer) As Integer
    End Function

    Private Const WM_KEYDOWN As Integer = &H100
    Private Const WM_KEYUP As Integer = &H101
    Private Const VK_HOME As Integer = &H24
    Private Const KEYEVENTF_KEYUP As Integer = &H2        'キーアップ キーダウン = 0
    Private Const KEYEVENTF_EXTENDEDKEY As Integer = &H1  'スキャンコードは拡張コード
    Private Const INPUT_KEYBOARD As Integer = 1           'キーボードイベントを発生させます

    Private isCancel As Boolean = False
    Private DialogTitle As String = ""
    Public Function ShowDialogEx(target As OpenFileDialog) As DialogResult

        'ダイアログの非同期取得
        isCancel = False
        If target.Title.Length = 0 Then
            DialogTitle = "開く"
        Else
            DialogTitle = target.Title
        End If

        Dim taskShowDialog = Task.Run(
            Sub()
                Do Until isCancel
                    Application.DoEvents()
                    System.Threading.Thread.Sleep(500)
                    EnumWindows(New WNDENUMPROC(AddressOf EnumWindowsProc), IntPtr.Zero)
                Loop
            End Sub
        )

        'ダイアログの表示
        Dim result As DialogResult = target.ShowDialog()

        'ダイアログの非同期取得を終了させる
        Do Until taskShowDialog.IsCompleted
            isCancel = True
            Application.DoEvents()
            System.Threading.Thread.Sleep(250)
        Loop

        Return result
    End Function

    Private Function EnumWindowsProc(ByVal hWnd As IntPtr, ByVal lparam As IntPtr) As Boolean
        If isCancel = False Then
            'ウィンドウのタイトルの長さを取得する
            Dim textLen As Integer = GetWindowTextLength(hWnd)
            If textLen > 0 Then
                'ウィンドウのクラス名を取得する
                Dim csb As New StringBuilder(256)
                GetClassName(hWnd, csb, csb.Capacity)
                If csb.ToString() = "#32770" Then
                    'ウィンドウのタイトル名を取得する
                    Dim tsb As New StringBuilder(textLen + 1)
                    GetWindowText(hWnd, tsb, tsb.Capacity)
                    If tsb.ToString() = DialogTitle Then
                        EnumChildWindows(hWnd, New WNDENUMPROC(AddressOf EnumChildProc), IntPtr.Zero)
                    End If
                End If
            End If
        End If

        'すべてのウィンドウを列挙する
        Return True
    End Function

    Private Function EnumChildProc(ByVal hWnd As IntPtr, ByVal lparam As IntPtr) As Boolean
        If isCancel = False Then
            'クラス名取得
            Dim csb As New StringBuilder(256)
            GetClassName(hWnd, csb, csb.Capacity)
            If csb.ToString() = "ComboBoxEx32" Then
                SetForegroundWindow(hWnd)
                WindowToSendInput(VK_HOME)
                isCancel = True
            End If
        End If
        Return True
    End Function

    Private Sub WindowToSendInput(ByVal wVK As Short)
        Dim pInputs() As INPUT
        ReDim pInputs(0 To 1)
        pInputs(0).type = INPUT_KEYBOARD
        With pInputs(0).ki
            .wVk = wVK                              'キーコードを指定
            .wScan = CShort(MapVirtualKey(wVK, 0))  'スキャンコードを指定
            .dwFlags = KEYEVENTF_EXTENDEDKEY Or 0               'キーダウン
            .time = 100
            .dwExtraInfo = IntPtr.Zero
        End With
        pInputs(1).type = INPUT_KEYBOARD
        With pInputs(1).ki
            .wVk = pInputs(0).ki.wVk                'キーコードを指定
            .wScan = pInputs(0).ki.wScan            'スキャンコードを指定
            .dwFlags = KEYEVENTF_EXTENDEDKEY Or KEYEVENTF_KEYUP 'キーアップ
            .time = 100
            .dwExtraInfo = IntPtr.Zero
        End With
        SendInput(pInputs.Length, pInputs, Marshal.SizeOf(GetType(INPUT)))
    End Sub

End Module
'----- ここまで
※フォーカス移動が抜けていたので追加しました

引用返信 編集キー/
■98595 / inTopicNo.5)  Re[4]: VB.NET OpenFileDialog について
□投稿者/ くま (41回)-(2021/12/06(Mon) 18:58:33)
使い方

        Dim CsvFile As String = ""
        Dim ofd As New OpenFileDialog()
        ofd.Filter = "CSVファイル(*.csv)|*.csv"
        ofd.Title = "CSVファイルを選択してください"
        ofd.FileName = "hogehogehogehoge.csv"

        'If ofd.ShowDialog() = DialogResult.OK Then  'ここを
        If ShowDialogEx(ofd) = DialogResult.OK Then  'こっちへ変更
            CsvFile = ofd.FileName
        End If

処理概要
1. ダイアログボックスが開く前、定期間隔(500ミリ秒)で現在開いているウインドウを取得するタスクを非同期で実行しておく(ウインドウ検索タスク)
2. ダイアログボックスを開く
3. 先のウインドウ検索タスクがダイアログボックスを見つけると、その子供のコントロールを検索する。
4. ファイル名が入力されているコントロール(クラスComboBoxEx32)を探す。
5. コントロールが見つかった場合[HOME]のキーダウン・キーアップを送信
6. タスク完了用にisCanselを=Trueに設定
7. ダイアログが終了したら一応タスクが完了しているか確認。
です。

いや〜先の「他のアプリ上にあるマウスポインタを消去する方法」
http://bbs.wankuma.com/index.cgi?mode=al2&namber=98588
で話していた内容で重なってくるとは、偶然とは恐ろしいですね。


引用返信 編集キー/
■98597 / inTopicNo.6)  Re[5]: VB.NET OpenFileDialog について
□投稿者/ くま (43回)-(2021/12/06(Mon) 19:32:44)
もっとシンプルでフォーカスあてただけでも全部表示されるっぽい
その場合簡単になるのでこちらで

モジュール OpenFileDialogEx.vb
'----- ここから
Imports System.Runtime.InteropServices
Imports System.Text

Module OpenFileDialogEx
    Private Delegate Function WNDENUMPROC(ByVal hWnd As IntPtr, ByVallparam As IntPtr) As Boolean

    <DllImport("user32.dll")>
    Private Function EnumWindows(lpEnumFunc As WNDENUMPROC, lparam As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
    End Function
    <DllImport("user32.dll")>
    Private Function EnumChildWindows(ByVal hwndParent As Integer, ByVal lpEnumFunc As WNDENUMPROC, ByVal lParam As Integer) As Integer
    End Function
    <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
    Private Function GetWindowText(hWnd As IntPtr, lpString As StringBuilder, nMaxCount As Integer) As Integer
    End Function
    <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
    Private Function GetWindowTextLength(hWnd As IntPtr) As Integer
    End Function
    <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
    Private Function GetClassName(hWnd As IntPtr, lpClassName As StringBuilder, nMaxCount As Integer) As Integer
    End Function
    <DllImport("user32.dll")>
    Private Function SetForegroundWindow(hWnd As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
    End Function

    Private isCancel As Boolean = False
    Private DialogTitle As String = ""
    Public Function ShowDialogEx(target As OpenFileDialog) As DialogResult

        'ダイアログの非同期取得
        isCancel = False
        If target.Title.Length = 0 Then
            DialogTitle = "開く"
        Else
            DialogTitle = target.Title
        End If

        Dim taskShowDialog = Task.Run(
            Sub()
                Do Until isCancel
                    Application.DoEvents()
                    System.Threading.Thread.Sleep(500)
                    EnumWindows(New WNDENUMPROC(AddressOf EnumWindowsProc), IntPtr.Zero)
                Loop
            End Sub
        )

        'ダイアログの表示
        Dim result As DialogResult = target.ShowDialog()

        'ダイアログの非同期取得を終了させる
        Do Until taskShowDialog.IsCompleted
            isCancel = True
            Application.DoEvents()
            System.Threading.Thread.Sleep(250)
        Loop

        Return result
    End Function

    Private Function EnumWindowsProc(ByVal hWnd As IntPtr, ByVal lparam As IntPtr) As Boolean
        If isCancel = False Then
            'ウィンドウのタイトルの長さを取得する
            Dim textLen As Integer = GetWindowTextLength(hWnd)
            If textLen > 0 Then
                'ウィンドウのクラス名を取得する
                Dim csb As New StringBuilder(256)
                GetClassName(hWnd, csb, csb.Capacity)
                If csb.ToString() = "#32770" Then
                    'ウィンドウのタイトル名を取得する
                    Dim tsb As New StringBuilder(textLen + 1)
                    GetWindowText(hWnd, tsb, tsb.Capacity)
                    If tsb.ToString() = DialogTitle Then
                        EnumChildWindows(hWnd, New WNDENUMPROC(AddressOf EnumChildProc), IntPtr.Zero)
                    End If
                End If
            End If
        End If

        'すべてのウィンドウを列挙する
        Return True
    End Function

    Private Function EnumChildProc(ByVal hWnd As IntPtr, ByVal lparam As IntPtr) As Boolean
        If isCancel = False Then
            'クラス名取得
            Dim csb As New StringBuilder(256)
            GetClassName(hWnd, csb, csb.Capacity)
            If csb.ToString() = "ComboBoxEx32" Then
                SetForegroundWindow(hWnd)
                isCancel = True
            End If
        End If
        Return True
    End Function

End Module

'----- ここまで

引用返信 編集キー/
■98605 / inTopicNo.7)  Re[1]: VB.NET OpenFileDialog について
□投稿者/ KOZ (184回)-(2021/12/07(Tue) 08:55:12)
No98591 (BanditH さん) に返信
> Homeキー等を押さないとファイル名全体を確認できません。

ダイアログが表示され、アイドル状態になると、親ウインドウに WM_ENTERIDLE メッセージが通知されます。
フォームの WndProc でそれを捕まえると良いようです。

Private Const WM_ENTERIDLE As Integer = &H121
Private inShowDialog As Boolean = False

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

    Dim CsvFile As String = ""
    Using ofd As New OpenFileDialog()
        ofd.Filter = "CSVファイル(*.csv)|*.csv"
        ofd.Title = "CSVファイルを選択してください" & vbLf
        ofd.FileName = "1234"
        inShowDialog = True
        If ofd.ShowDialog() = DialogResult.OK Then
            CsvFile = ofd.FileName
        End If
    End Using
    'Using winhook As New WindowsHook(HookType.WH_CALLWNDPROCRET)
    '    AddHandler winhook.OnHookAfter, AddressOf OnHookAfter

    'End Using

End Sub

Protected Overrides Sub WndProc(ByRef m As Message)
    Select Case m.Msg
        Case WM_ENTERIDLE
            MyBase.WndProc(m)
            If inShowDialog Then
                inShowDialog = False
                SendKeys.Send("{HOME}")
                SendKeys.Send("+{End}")
            End If
        Case Else
            MyBase.WndProc(m)
    End Select
End Sub

引用返信 編集キー/
■98606 / inTopicNo.8)  Re[2]: VB.NET OpenFileDialog について
□投稿者/ KOZ (185回)-(2021/12/07(Tue) 08:57:02)
No98605 (KOZ) に返信

しまった、変なコードが混じっちゃいましたが、この部分は無視してください。(^_^;)

> 'Using winhook As New WindowsHook(HookType.WH_CALLWNDPROCRET)
> ' AddHandler winhook.OnHookAfter, AddressOf OnHookAfter
>
> 'End Using

引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -