データグリッド上のコンボボックスで、"←"キーでセルを左に移動させるには?


まい  2004-08-20 07:47:33  No: 115793

いつもお世話になっております。
私は今、データグリッドにコンボボックスを表示させていて、コンボボックス内にフォーカスがあるときに"←"キー押下でセルを左に移動させる処理を考えているのですが、どうもうまくいきません。
Protected Overrides Function ProcessCmdKeyを使って"←"キーをオーバーライドさせてセル移動も考えたのですが、入力途中で左前の文字に行こうとして"←"キーを押下すると、セル移動してしまいだめという状態です。
だれか分かる方がいましたらご教授お願いいたします。


特攻隊長まるるう  2004-08-20 19:56:14  No: 115794

コンボボックス側で入力開始から終了までフラグを
立てておき、フラグが立っている時は無視、立って
いない時は DataGrid に通知すれば…できるかなぁ(未確認)


まい  2004-08-20 20:33:19  No: 115795

特攻隊長まるるうさん、返答ありがとうございます。入力開始から終了フラグをたてるといいますと、どのようになるのでしょうか?入力開始、終了の判断というのはイベントとかですか?
開始、終了の判断ができるものがあれば教えていただけると幸いです。


特攻隊長まるるう  2004-08-20 21:00:01  No: 115796

データグリッドにコンボボックスを表示させる方法は独自のものですよね?
だったらコンボボックス側の仕様が分からないと何とも言えません。

DataGrid 側では編集の開始は DataGrid.ColumnStartedEditing メソッド
のオーバーロードされた2つの関数をオーバーライドすれば分かります。
編集の終了は…なんだろ?イベントかなぁ?
…でも既存の機能で『←』移動できますよね? DataGrid は。コンボボックス
側で何か作らないと出来ないと思いますよ?。


まい  2004-08-20 23:12:40  No: 115797

特攻隊長まるるうさん、早速の返答ありがとうございます。
http://dobon.net/vb/dotnet/samples/datagridcomboboxcolumn.html
コンボボックスは↑のサンプルをほぼそのまま活用しています。
>>…でも既存の機能で『←』移動できますよね?
既在の機能では、文字編集中に一つ前の文字とかに戻るのには”←”は移動できますが、1番左に移動しているときに”←”を押しても、左のセルには移動できません。
コンボボックス側で移動できる処理を加えなければいけないということですね。がんばって考えてみます。


まい  2004-08-24 02:24:36  No: 115798

いつもお世話になっております。表題の件ですが、いろいろ試してみたのですが、うまくいきません。
コンボボックス側の仕様は以下のようになっております。
このコンボボックスにキー押下時の処理を加えてあげればよいのでしょうか!?
初心者なもので、このような低レベルな質問になってしまいますが、分かる方がおりましたら、宜しくお願いいたします。

Imports System
Imports System.Data
Imports System.Windows.Forms
Imports System.Drawing

Public Class DataGridComboBoxColumn
    Inherits DataGridTextAlignColumn
    'Inherits DataGridTextBoxColumn
    Private _comboBox As DataGridComboBox
    Private _sorce As CurrencyManager
    Private _rowNumber As Integer
    Private _editing As Boolean

    '/ <summary>
    '/ DataGridComboBoxColumnのコンストラクタ
    '/ </summary>
    '/ <param name="dataSource">ComboBoxのDataSource</param>
    '/ <param name="displayMember">ComboBoxのDisplayMember</param>
    '/ <param name="valueMember">ComboBoxのValueMember</param>
    Public Sub New(ByVal dataSource As DataView, _
            ByVal displayMember As String, _
            ByVal valueMember As String)
        '初期設定
        _sorce = Nothing

        _editing = False
       
        'DataGridComboBoxの作成
        _comboBox = New DataGridComboBox
        'ComboBoxの設定
        _comboBox.DropDownStyle = ComboBoxStyle.DropDown

        _comboBox.Visible = True
        'データソースの設定
        _comboBox.DataSource = dataSource
        _comboBox.DisplayMember = displayMember
        _comboBox.ValueMember = valueMember

        'イベントハンドラ
        AddHandler _comboBox.Leave, AddressOf _comboBox_Leave
        AddHandler _comboBox.SelectionChangeCommitted, _
            AddressOf _comboBox_SelectionChangeCommitted

        'AddHandler _comboBox.TextChanged, AddressOf _comboBox_TextChanged

    End Sub

    'プロパティ
    '/ <summary>
    '/ 管理されている ComboBox コントロールを取得します。
    '/ </summary>
    Public ReadOnly Property ComboBox() As DataGridComboBox
        Get
            Return _comboBox
        End Get
    End Property

    'ComboBoxからフォーカスが離れた
    Private Sub _comboBox_Leave(ByVal sender As Object, _
            ByVal e As EventArgs)
        If _editing Then
            'ComboBoxが編集中だったとき
            _editing = False
         

            '行の値を更新する
            SetColumnValueAtRow(_sorce, _rowNumber, _
                _comboBox.Text)
            Invalidate()
        End If
        'ComboBoxを隠す
        _comboBox.Visible = False
        'DataGridのスクロールイベントを捕捉する
        AddHandler DataGridTableStyle.DataGrid.Scroll, _
            AddressOf DataGrid_Scroll
    End Sub

    'DataGridがスクロール
    Private Sub DataGrid_Scroll(ByVal sender As Object, _
            ByVal e As EventArgs)
        'ComboBoxを消す
        If _comboBox.Visible Then
            _comboBox.Visible = False
        End If
    End Sub

    'ComboBoxで選択した項目が変更されて、その変更がコミットされた
    Private Sub _comboBox_SelectionChangeCommitted( _
            ByVal sender As Object, ByVal e As EventArgs)
        _editing = True
        'DataGridに列が編集開始されたことを知らせる
        MyBase.ColumnStartedEditing(CType(sender, Control))
    End Sub

    '行の最小の高さ
    Protected Overrides Function GetMinimumHeight() As Integer
        Return _comboBox.PreferredHeight
    End Function

    'ComboBoxをDataGridのControl.ControlCollectionに追加する
    Protected Overrides Sub SetDataGridInColumn( _
            ByVal value As DataGrid)
        MyBase.SetDataGridInColumn(value)
        _comboBox.Parent = CType(value, Control)
    End Sub

    '列が管理しているコントロールへのフォーカスを
    '放棄する必要があることを列に通知
    Protected Overrides Sub ConcedeFocus()
        MyBase.ConcedeFocus()
        _comboBox.Visible = False
    End Sub

    ''編集するためにセルを準備する
    Protected Overloads Overrides Sub Edit( _
            ByVal source As CurrencyManager, _
            ByVal rowNum As Integer, _
            ByVal bounds As Rectangle, _
            ByVal [readOnly] As Boolean, _
            ByVal instantText As String, _
            ByVal cellIsVisible As Boolean)
        '基本クラスのEditを呼び出す
        MyBase.Edit(source, rowNum, bounds, [readOnly], _
            instantText, cellIsVisible)
        'TextBoxを消す
        Me.TextBox.Visible = False

        '値の保存
        _rowNumber = rowNum
        _sorce = source

        '表示させるComboBoxの設定をする
        _comboBox.Bounds = bounds
        _comboBox.RightToLeft = _
            Me.DataGridTableStyle.DataGrid.RightToLeft

        '非表示、読み取り専用のときは、ComboBoxを表示しない
        If cellIsVisible AndAlso Not [readOnly] Then
            'ComboBoxを表示
            _comboBox.Visible = True
            _comboBox.BringToFront()
            _comboBox.Focus()
        End If

        '選択項目の変更
        'ComboBoxを表示する前に行うと、
        '新しい行で値を変更した時不都合が起きる
        _comboBox.SelectedIndex = _
            _comboBox.FindStringExact(Me.TextBox.Text)

        'DataGridのスクロールイベントを捕捉する
        AddHandler DataGridTableStyle.DataGrid.Scroll, _
            AddressOf DataGrid_Scroll
    End Sub

    '編集プロシージャを完了する
    Protected Overrides Function Commit( _
            ByVal dataSource As CurrencyManager, _
            ByVal rowNum As Integer) As Boolean
        If _editing Then
            '編集中のときは、rowNum行の値を設定する
            _editing = False
          
            SetColumnValueAtRow(dataSource, rowNum, _
                _comboBox.Text)
        End If

        Return True
    End Function

    '指定した行の値を設定
    Protected Overrides Sub SetColumnValueAtRow( _
            ByVal source As CurrencyManager, _
            ByVal rowNum As Integer, ByVal value As Object)
        'ValueMemberの値を設定する
        MyBase.SetColumnValueAtRow(source, rowNum, _
            _comboBox.FindValueMember(value))
    End Sub

    '指定した行の値を取得
    Protected Overrides Function GetColumnValueAtRow( _
            ByVal source As CurrencyManager, _
            ByVal rowNum As Integer) As Object
        Dim val As Object = _
            MyBase.GetColumnValueAtRow(source, rowNum)
        'DisplayMemberの値を返す
        Return _comboBox.FindDisplayMember(val)
    End Function
End Class

Public Class DataGridComboBox
    Inherits System.Windows.Forms.ComboBox
    Private WM_KEYUP As Integer = &H101

    Protected Overrides Sub WndProc( _
            ByRef theMessage As System.Windows.Forms.Message)
        'キー操作を無効にする()
        If theMessage.Msg = WM_KEYUP Then
            Return
        End If
        MyBase.WndProc(theMessage)
    End Sub

    '/ <summary>
    '/ DisplayMemberからValueMemberの値を探す
    '/ </summary>
    '/ <param name="display">探すDisplayMember値</param>
    '/ <returns>見つかったValueMember値</returns>
    Public Function FindValueMember( _
            ByVal display As Object) As Object
        Dim dv As DataView = CType(DataSource, DataView)
        Dim rowCount As Integer = dv.Count

        'ループして探す
        Dim disp As Object
        Dim i As Integer
        For i = 0 To rowCount - 1
            disp = dv(i)(DisplayMember)
            '見つかった時
            If display.Equals(disp) Then
                Return dv(i)(ValueMember)
            End If
        Next i
        '見つからなかった時
        Return DBNull.Value
    End Function

    '/ <summary>
    '/ ValueMemberからDisplayMemberの値を探す
    '/ </summary>
    '/ <param name="value">探すValueMember値</param>
    '/ <returns>見つかったDisplayMember値</returns>
    Public Function FindDisplayMember( _
            ByVal value As Object) As Object
        Dim dv As DataView = CType(DataSource, DataView)
        Dim rowCount As Integer = dv.Count

        'ループして探す
        Dim val As Object
        Dim i As Integer
        For i = 0 To rowCount - 1
            val = dv(i)(ValueMember)
            '見つかった時
            If value.Equals(val) Then
                Return dv(i)(DisplayMember)
            End If
        Next i
        '見つからなかった時
        Return DBNull.Value

    End Function

End Class


特攻隊長まるるう  2004-08-24 23:52:33  No: 115799

ソースはリンク先で分かるから改造したとこだけ教えてくれる方が
有難かったかなぁ(^^;)…でないとオイラ…解析しなきゃいけないデショ?w

>初心者なもので、このような低レベルな質問になってしまいますが
『継承』の時点で低レベルじゃないよ?こーゆー書き方ってボクは嫌い。
ボクの回答基準は相手の『誠意』であり『レベル』じゃないのデス。
逆に相手が『初心者』ってゆー言葉を出したら全部答えなきゃならないのか?
それには疑問が残ります。
『初心者』っていっても幅がありますし、結局、相手の力量は書込みの内容から
判断しないといけません。こちらで判断できますし、寧ろこちらで判断すること
だと思ってます。自称『初心者』って意味無いし、鬱陶しい場合が多いのでボクは
嫌ってます。ボクがメインで回答を付けている場合はこの言葉を使わないように
しましょうw。機嫌を損ねます(爆)。…自分のやったことを見せれば他は必要無い
と思うんだけどね…。社交辞令の謙遜(?)も必要かにゃ…。回答者に『あなたなら
分かるはずだから答えて下さい!』と強要されている感じがして謙遜になってない
気がするんですよねぇ。『わたしは分かりません』と宣言されてるみたいにも感じ
ます。オイラだって分かるもののみ答えてたら3割程度しか答えられませんって(汗)。
ほとんどの回答はその場で考えてますからねぇ。質問者の問題なのにボクだけ考え
て、ボクだけ調べて…答えだけ盗まれるのはイヤナノ。

さて、ざっと解析しましたが、基本は
http://dobon.net/vb/dotnet/samples/datagridcomboboxcolumn.html
の DataGridComboBoxColumn(DataGridTextBoxColumn の継承)のままですよね?。
ただし、継承しているのは DataGridTextAlignColumn というユーザ定義の
クラス。たぶん
http://dobon.net/vb/dotnet/datagrid/aligncolumnheaderonly.html
↑これ?…こーゆー情報こそ省略されては困るんですが…(^^;)まぁ、…探しますけどね(TT)
改造した点は… DataGridTextAlignColumn の方は全く分からないけど、そのまま
使ってるとして、動作が変わってくる変更点として
        _comboBox.DropDownStyle = ComboBoxStyle.DropDown
のユーザが入力可となってる所くらいですかねぇ。
2段階以上のユーザによる独自の継承をする場合はオーバーロードで動作が矛盾しない
ように気をつけて下さいね。…特にどっかのコピペの場合は十分に動作を確認して下さい。

DataGrid から DataGridTableStyle.GridColumnStyles 経由で DataGridTextBoxColumn
がどう動くかはあまり調べてないんだけど、Edit メソッドでコントロール _comboBox
を表示して編集。選択項目が変われば _comboBox_SelectionChangeCommitted イベント。
テキスト編集中は _editing フラグを立てて、編集終了時に Commit 。SetColumnValueAtRow で
指定行の更新ですね。それとフォーカス移動時の _comboBox_Leave イベント。この辺りが
データ編集の終了処理として関係しているわけです。

>いろいろ試してみたのですが、うまくいきません。
その『何を』『どう』試して『何が』上手くいかなかったかの情報が欲しい
んですが?(^^;)
ボクも『今』解析して『今』考えて『今』回答してるので、まいさんからの
情報が何もないと、最初から考えないといけないわけです。そこでまいさんの
試した内容が書き込まれていると参考に出来ます。ボクがもう一度試すのは
二度手間でしょ?。新しい動作のコーディングに関しては『質問者』はただの
『質問者』であってはいけないと思います。『回答者』の『協力者』でないと…。
『わたしには出来ませんのでどうにかして下さい』的な1から10までこちらで
考えないといけない質問なら、最初から回答する気はありませんので、ご了承
ください。
…まぁ、このコードだと DataGridComboBoxColumn クラスで例にならって
イベントコードの追加でしょうか。とりあえず _comboBox.KeyDown あたりが
無難でしょうね。ローカル変数に _comboBox を持ってるわけですから、これ
を使えばどういう状態なのか分かります。カーソル位置によって編集終了時
の処理を走らせて…でもそれだけだと移動が出来ませんね。…どうしますかねぇ。
とりあえず SendKeys.Send しちゃうのが一番修正が少ないように思います。
編集終了処理をしてコンボボックス隠した後(←隠さないと暴走の危険あり)
    SendKeys.Send("{LEFT}")
か、タブを送っていいなら
    SendKeys.Send("+{TAB}")
で _comboBox_Leave イベントが走って確定してくれますね。

確実にいくなら DataGrid のハンドル使って API の SendMessage かなぁ。
自分自身に送るなら Control.WndProc とか使えそうだけど…うーむ。
…そこそこ動くコードは書いたけど考え中…。とりあえず上に書いたような
方法を調べてコーディングしてみて下さい。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加