多次元配列のインデックスを取得するには?

解決


Alice  2009-11-30 20:42:18  No: 146593

初めまして。
早速で申し訳ないのですが、
現在、VB6で作成されたプログラムをVB2008で作動するように書き換えを行っている中で不明な点がありましたので、
是非アドバイスを頂けたらと思い投稿しました。
こちらの動作環境は  VB2008,XP(SP3)  です。

フォームに  CombBox...cmbA0,cmbA1,cmbB0,cmbB1,cmbC0,cmbC1があります。
cmbA0の表示が変わったら、cmbA1の表示も変わる。
cmbA1の表示が変わっても、同じようにcmbA0の表示も変わる。
cmbB0,cmbB1,cmbC0,cmbC1についても同じ動作をします。
以下、VB6で作成されたプログラムです。

'cmbA0,cmbA1
    Private Sub cmbA_SelectedIndexChanged(ByVal sender As  System.Object, ByVal e As System.EventArgs) Handles cmbA.SelectedIndexChanged
        Dim Index As Short = cmbA.GetIndex(sender)
        If cmbA(0).SelectedIndex <> cmbA(1).SelectedIndex Then
            cmbA(1 - Index).SelectedIndex = cmbA(Index).SelectedIndex
        End If
    End Sub
    'cmbB0,cmbB1
    Private Sub cmbB_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmbB.SelectedIndexChanged
        Dim Index As Short = cmbB.GetIndex(sender)
        If cmbB(0).SelectedIndex <> cmbB(1).SelectedIndex Then
            cmbB(1 - Index).SelectedIndex = cmbB(Index).SelectedIndex
        End If
    End Sub
    'cmbC0,cmbC1
    Private Sub cmbC_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmbC.SelectedIndexChanged
        Dim Index As Short = cmbC.GetIndex(sender)
        If cmbC(0).SelectedIndex <> cmbC(1).SelectedIndex Then
            cmbC(1 - Index).SelectedIndex = cmbC(Index).SelectedIndex
        End If
    End Sub

cmbA(cmbA0,cmbA1),cmbB(cmbB0,cmbB1),cmbC(cmbC0,cmbC1)はVB6→VB2008にアップグレードの際、
Microsoft.VisualBasic.Compatibility.VB6のCombBoxArrayを使用し、配列になっています。
上記をまとめて…ニュアンス的には下記のような感じにしたいのですが、
IndexOfでは一次元の配列のインデックスしか取得できないということで、
インデックス取得ができずに躓いています。

Dim cmbABC()() As ComboBox
    Private Sub Form_Load(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles MyBase.Load
        cmbABC = New ComboBox()() {New ComboBox() {cmbA0, cmbA1}, New ComboBox() {cmbB0, cmbB1}, New ComboBox() {cmbC0, cmbC1}}
    End Sub
    Private Sub cmbABC_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmbA0.SelectedIndexChanged,cmbA1.SelectedIndexChanged,cmbB0....
        Dim Index As Byte = Array.IndexOf(cmbABC, sender)
        If cmbABC()(0).SelectedIndex <> cmbABC()(1).SelectedIndex Then
            cmbABC()(1 - Index).SelectedIndex = cmbABC()(Index).SelectedIndex
        End If
    End Sub

cmbABC()の一次元であれば楽にできるのですが、それですと0と1の対になっている動作が出来ずどうしたものかと・・・
やりたいことは伝わりますでしょうか?
先駆者の方々のご指摘、アドバイスをお待ちしております。
どうぞよろしくお願いいたします。


特攻隊長まるるう  2009-11-30 22:22:26  No: 146594

> cmbABC()の一次元であれば楽にできるのですが、それですと0と1の対になっている動作が出来ずどうしたものかと・・・
必ず2個対になってると分かってるなら2で割った商と余りで分かるのでは?


魔界の仮面弁士  2009-11-30 22:38:44  No: 146595

こういう事で良いのかな?

Public Class Form1
    Private cmbABC()() As ComboBox

    Private Sub Form_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
        cmbABC = New ComboBox()() {New ComboBox() {cmbA0, cmbA1}, New ComboBox() {cmbB0, cmbB1}, New ComboBox() {cmbC0, cmbC1}}
    End Sub

    Private Sub cmbABC_SelectedIndexChanged(ByVal sender As ComboBox, ByVal e As EventArgs) _
    Handles cmbA0.SelectedIndexChanged, _
            cmbA1.SelectedIndexChanged, _
            cmbB0.SelectedIndexChanged, _
            cmbB1.SelectedIndexChanged, _
            cmbC0.SelectedIndexChanged, _
            cmbC1.SelectedIndexChanged

        Dim q = cmbABC.Where(Function(x) x.Contains(sender)).Select(Function(ComboBoxes) _
                New With {ComboBoxes, .Index = Array.IndexOf(ComboBoxes, sender)}).First()

        If q.ComboBoxes(0).SelectedIndex <> q.ComboBoxes(1).SelectedIndex Then
            q.ComboBoxes(1 - q.Index).SelectedIndex = q.ComboBoxes(q.Index).SelectedIndex
        End If
    End Sub
End Class


Alice  2009-11-30 23:10:21  No: 146596

特攻隊長まるるう様
アドバイスをありがとうございます。
商と余り…[\][MOD]を使用して、ということですね。なんとなく理解できそうなので、早速やってみます。

魔界の仮面弁士様
まさに!という回答を提示して頂きまして、ありがとうございます。理想の動作をしました。
しかしながらレベルが高すぎて、理解が追いついていません…。
少しずつ調べながら紐解いていきたいと思います。

確実に動作するプログラムを頂いておいて恐縮なのですが、
理解が及ばない箇所があるのと、先に頂いたアドバイスが実行できていない為、
理解し、実行したうえで解決にさせていただきたいと思っています。
解決は今しばらくお待ちください。


魔界の仮面弁士  2009-12-01 00:16:53  No: 146597

{{0, 1},{2, 3},{4, 5}} という階層配列にするのではなく、
{0, 1, 2, 3, 4, 5} という 1 次元配列にて管理するならば、

Dim Index1 As Integer = Array.IndexOf(cmbABC, sender)
Dim Index2 As Integer = Index1 + If(Index1 Mod 2 = 0, 1, -1)

If cmbABC(Index1).SelectedIndex <> cmbABC(Index2).SelectedIndex Then
    cmbABC(Index2).SelectedIndex = cmbABC(Index1).SelectedIndex
End If

と書けますね。

あるいは、相方を Tag プロパティに
  'Form の Load イベント
  cmbA0.Tag = cmbA1
  cmbA1.Tag = cmbA0
  cmbB0.Tag = cmbB1
  cmbB1.Tag = cmbB0
  cmbC0.Tag = cmbC1
とセットしておけば、SelectedIndexChanged では
  Dim a As ComboBox = DirectCast(sender, ComboBox)
  Dim b As ComboBox = DirectCast(a.Tag, ComboBox)
  If a.SelectedIndex <> b.SelectedIndex Then
      b.SelectedIndex = a.SelectedIndex
  End If
のように、短く記述することができます。


Alice  2009-12-01 17:46:50  No: 146598

魔界の仮面弁士様
>If(Index1 Mod 2 = 0, 1, -1)
という書き方があることを初めて知りました。
なるほど、私のレベルでも直感的にわかりやすい構文になっていて、他でも使い回しが出来そうです。
[Tag]の使い方も今一解らなかったのですが、提示していただいたような使い方も出来るのですね。
こちらも非常に解りやすく、使えそうです。

今回は、この6つのCombBoxを他のイベントにも使うので、
上記に提示していただいた一次元配列にしてまとめようと思います。
以下、動作済みのプログラムにです。

Dim cmbABC(5) As ComboBox
Private Sub Form_Load(ByVal sender As System.Object, ByVal e As  System.EventArgs) Handles MyBase.Load
    cmbABC = New ComboBox() {cmbA0, cmbA1, cmbB0, cmbB1, cmbC0, cmbC1}
    For i = 0 To 5
        AddHandler cmbABC(i).SelectedIndexChanged, AddressOf cmbABC_SelectedIndexChanged
    Next
End Sub
Private Sub cmbABC_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs)
    Dim Index1 As Integer = Array.IndexOf(cmbABC, sender)
    Dim Index2 As Integer = Index1 + If(Index1 Mod 2 = 0, 1, -1)

    If cmbABC(Index1).SelectedIndex <> cmbABC(Index2).SelectedIndex Then
        cmbABC(Index2).SelectedIndex = cmbABC(Index1).SelectedIndex
    End If
End Sub

特攻隊長まるるう様
魔界の仮面弁士様
頂いたアドバイスと提示して頂いたプログラムをすばやく理解できるよう、
精進していきたいと思います。
次の機会があった時、またお力を貸していただければと思います。
お付き合い頂き、ありがとうございました。


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

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






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