tagCANDY CGI VBレスキュー(花ちゃん) の Visual Basic 2010 用 掲示板(VB.NET 掲示板) [ツリー表示へ]   [Home]
一括表示(VB.NET VB2005)
タイトルCombobox/Dropdownリストのクリック
記事No9419
投稿日: 2009/09/28(Mon) 06:32
投稿者ダンボ
VB2005で、Comboboxを継承して下記のExCombobox.dllを作ろうと考えています。
追加する機能は、
・参照回数によるリストの自動整列
・ドロップダウンリストを右クリックするとメニューを出し、
 項目名の変更/項目の削除/新規項目の追加ができる
ですが、後者のドロップダウンリストに対するマウスクリックイベントが
拾えません。

検索して一番参考になりそうなのが、
http://202.218.219.160/bbs/phpBB/viewtopic.php?topic=28307&forum=7
でしたが、こんなことまでしないと駄目ですか?
APIを使うというところでちょっと嫌だし、ListBoxHandleからドロップダウンリスト
へ関連付けて、AddHandlerという感じだと思うのですが具体的な方法がわかりません。

(諸般の事情で書き込みは遅くなりますが読むのは頻繁に読みますのでよろしくお願いします)

[ツリー表示へ]
タイトルRe: Combobox/Dropdownリストのクリック
記事No9424
投稿日: 2009/10/01(Thu) 10:56
投稿者たくボン@10年ぶり
> VB2005で、Comboboxを継承して下記のExCombobox.dllを作ろうと考えています。

> 検索して一番参考になりそうなのが、
> http://202.218.219.160/bbs/phpBB/viewtopic.php?topic=28307&forum=7
> でしたが、こんなことまでしないと駄目ですか?
> APIを使うというところでちょっと嫌だし、ListBoxHandleからドロップダウンリスト
> へ関連付けて、AddHandlerという感じだと思うのですが具体的な方法がわかりません。

どうやらComboBoxの構造を理解されていないようですね。
ComboBoxのDropDownの部分の矩形は、どのように描画されているかわかっていますか?

わからないのであれば、画面の下部にComboBoxを貼り付けて表示させてみてください。画面の矩形からはみ出しませんか?

自分の知っている範囲だけで理解しようとするのではなく、なぜこのような作りをしているか。そこから考えてみてください。

API使うのが嫌ならこのような仕様にすべきではありません。

[ツリー表示へ]
タイトルTopIndexの実装
記事No9430
投稿日: 2009/10/04(Sun) 18:29
投稿者ダンボ
たくボン@10年ぶり さん、どうもありがとうございます。
事情があって返信も遅れてすみません。

> どうやらComboBoxの構造を理解されていないようですね。
> ComboBoxのDropDownの部分の矩形は、どのように描画されているかわかっていますか?
> API使うのが嫌ならこのような仕様にすべきではありません。

「DropDownの部分の矩形は独立したWindowだからComboBoxのメンバーとしては
一般提供されていない。やりたければAPIで処理できるのだからAPIを厭うべきでない」
ということをおっしゃっているのでしょうか?

.NetFrameworkでは、非常に多くのクラスが用意されておりVB6時代のAPI Hellから
抜け出せたんだと感激しました。ひょっとしたら.NetFrameworkでは(現バージョンでは
無理としても)一切APIを直接コールする必要が無くなる、それも.NetFrameworkの
目標の一つと勝手に解釈しておりました。

.NetFramework2.0ではAPI使用で行くしか無いようですので、下記のコードでなんとか
動作確認をとれるところまで来ました。しかしバグがありました!!(いや、知っては
いたんですがTopIndexを足せば済むだけと後回しにしていました)
ところが、VB2005ではComboBoxのTopIndexがサポートされなくなりました。
http://msdn.microsoft.com/ja-jp/library/cxh2w9wf%28VS.80%29.aspx

ということで、TopIndexをなんとか実装しようと考えている最中なのですが
よいアイデアが浮かびません。

Public Class ExComboBox
    Inherits ComboBox
    Private Shared ScreenIndex As Integer = -1
    Public Sub New()
        InitializeComponent()  '呼び出しの後で初期化を追加します。
        Me.components = New System.ComponentModel.Container()
    End Sub
    <System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)> _
    Private Structure ComboBoxInfo
        Public Size As Integer
        Public RectItem As System.Drawing.Rectangle
        Public RectButton As System.Drawing.Rectangle
        Public ButtonState As Integer
        Public ComboBoxHandle As System.IntPtr
        Public EditBoxHandle As System.IntPtr
        Public ListBoxHandle As System.IntPtr
    End Structure
    <System.Runtime.InteropServices.DllImport("USER32.DLL")> _
    Private Shared Function GetComboBoxInfo( _
    ByVal hWndCombo As System.IntPtr, _
    ByRef pcbi As ComboBoxInfo) As Boolean
    End Function
    Protected Overrides Sub OnHandleCreated(ByVal e As System.EventArgs)
        Dim cbinfo As New ComboBoxInfo()
        cbinfo.Size = System.Runtime.InteropServices.Marshal.SizeOf(cbinfo)
        If Not GetComboBoxInfo(Me.Handle, cbinfo) Then Return
        If cbinfo.ListBoxHandle.Equals(System.IntPtr.Zero) Then Return
        DropdownList = New ListBoxWindow(Me)
        DropdownList.AssignHandle(cbinfo.ListBoxHandle)
    End Sub
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing = True Then
            If Not Me.components Is Nothing Then
                Me.components.Dispose()
            End If
        End If
        MyBase.Dispose(disposing)
    End Sub
    <System.Runtime.InteropServices.DllImport("USER32.DLL")> _
    Private Shared Function GetWindowInfo( _
        ByVal hwnd As System.IntPtr, _
        ByRef info As WINDOWINFO) As Boolean
    End Function
    Structure WINDOWINFO
        Dim cbSize As UInt32
        Dim rcWindow As Rectangle
        Dim rcClient As Rectangle
        Dim dwStyle As UInt32
        Dim dwExStyle As UInt32
        Dim dwWindowStatus As UInt32
        Dim cxWindowBorders As UInt32
        Dim cyWindowBorders As UInt32
        Dim atomWindowType As UInt16
        Dim wCreatorVersion As UInt16
    End Structure

    Private WithEvents DropdownList As ListBoxWindow
    Public Class ListBoxWindow
        Inherits System.Windows.Forms.NativeWindow
        Public Event MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)
        Private Parent As ComboBox
        Public Sub New(ByVal parentComboBox As ComboBox)
            Parent = parentComboBox
            AddHandler parentComboBox.HandleDestroyed, AddressOf Me.OnHandleDestroyed
        End Sub
        Private Sub OnHandleDestroyed(ByVal sender As Object, ByVal e As System.EventArgs)
            Me.ReleaseHandle()
        End Sub
        Public Function PointToIndex(ByVal Y As Integer) As Integer
            Return CInt(Fix(Y \ Parent.ItemHeight))
        End Function
        <System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.Demand)> _
        Protected Overrides Sub WndProc(ByRef m As Message)
            Const WM_RBUTTONDOWN As Integer = &H204
            Const WM_MOUSEMOVE As Integer = &H200
            Dim info As New WINDOWINFO
            Select Case m.Msg
                Case WM_MOUSEMOVE
                    If ScreenIndex < 0 Then MyBase.WndProc(m)
                Case WM_RBUTTONDOWN
                    info.cbSize = Convert.ToUInt32(System.Runtime.InteropServices.Marshal.SizeOf(info))
                    Dim b As Boolean = GetWindowInfo(m.HWnd, info)
                    Dim X As Integer = info.rcWindow.X + LOWORD(m.LParam)
                    Dim Y As Integer = info.rcWindow.Y + HIWORD(m.LParam)
                    Dim i As Integer = PointToIndex(HIWORD(m.LParam))
                    Parent.SelectedIndex = i
                    Dim e As New System.Windows.Forms.MouseEventArgs(Windows.Forms.MouseButtons.Right, 1, X, Y, i)
                    RaiseEvent MouseDown(Me, e)
                    MyBase.WndProc(m)
                Case Else
                    MyBase.WndProc(m)
            End Select
        End Sub
        Private Function LOWORD(ByVal value As IntPtr) As Integer
            Return CInt((value.ToInt32 And &HFFFF&))
        End Function
        Private Function HIWORD(ByVal value As IntPtr) As Integer
            Return (value.ToInt32 And &HFFFF0000%) \ &H10000%
        End Function
    End Class

    Private Sub DropdownList_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles DropdownList.MouseDown
        Dim X As Integer = e.X
        Dim Y As Integer = e.Y
        ScreenIndex = e.Delta
        SubMenu.Show(X, Y)
    End Sub

    Private Sub SubMenu_Remove_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles SubMenu_Remove.Click
        Me.Items.RemoveAt(ScreenIndex)
        ScreenIndex = -1
    End Sub
    Private Sub SubMenu_Rename_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles SubMenu_Rename.Click
        Me.Items.Insert(ScreenIndex, Me.Items(ScreenIndex).ToString & "XXX")
        Me.Items.RemoveAt(ScreenIndex)
        ScreenIndex = -1
    End Sub
End Class

[ツリー表示へ]
タイトルRe: TopIndexの実装
記事No9431
投稿日: 2009/10/05(Mon) 20:46
投稿者魔界の仮面弁士
> ということで、TopIndexをなんとか実装しようと考えている最中なのですが
> よいアイデアが浮かびません。

実装してみました。
http://www.vb-user.net/junk/replySamples/2009.10.05.20.44/ComboBoxTopIndex.TXT

[ツリー表示へ]
タイトル[解決]TopIndexの実装
記事No9433
投稿日: 2009/10/06(Tue) 21:39
投稿者ダンボ
魔界の仮面弁士さん、いつもいつもありがとうございます。

> 実装してみました。
> http://www.vb-user.net/junk/replySamples/2009.10.05.20.44/ComboBoxTopIndex.TXT

MSDNで、「Visual Basic 2005 では、ComboBox コントロールの TopIndex プロパティと、
ListBox コントロールまたは ComboBox コントロールの Scroll イベントはサポート
されなくなりました。」とまで言うのだからあきらめていたのですが、APIレベルでは
ちゃんとあるのですねぇ。私の調査不足・アプローチの視線がまずかったです。
最悪ならオーナードローでe.Indexでも調べようかとまで思っていたので助かりました。

オーソドックスに、下記でやってみました。(APIは嫌と言いながら)

<System.Runtime.InteropServices.DllImport("USER32.DLL", _
    CharSet:=System.Runtime.InteropServices.CharSet.Auto)> _
Private Shared Function SendMessage( _
    ByVal hWnd As IntPtr, _
    ByVal wMsg As Integer, _
    ByVal wParam As Integer, _
    ByVal lParam As Integer) As Integer
End Function
Const CB_GETTOPINDEX As Integer = &H15B 'リストボックスの可視領域の最初の項目インデックスの取得
Public ReadOnly Property TopIndex() As Integer
    Get
        Return SendMessage(Me.Handle, CB_GETTOPINDEX , 0, 0)
    End Get
End Property

[ツリー表示へ]