2013-03-13 1 views
1

두 개의 콤보 상자가있는 Windows 양식이 있습니다. 각 콤보 상자의 SelectedValue 속성은 간단한 DTO의 속성에 바인딩 된 데이터입니다. 각 콤보 상자의 옵션은 모델 객체 목록에서 가져옵니다. DTO를 업데이트하기 위해 양식의 컨트롤 만 필요합니다. DTO의 속성을 프로그래밍 방식으로 수정하고 해당 컨트롤이 업데이트되는지 확인할 필요가 없습니다. 즉, 단방향 (컨트롤 -> 소스) 데이터 바인딩 만 작동하면됩니다.왜 Winforms 계단식 콤보 상자 집합에 바인딩하면 루트 콤보 상자의 값이 제대로 설정되지 않습니까?

사용자가 첫 번째 콤보 상자의 값을 변경하면 두 번째 콤보 상자의 옵션이 완전히 변경됩니다. 첫 번째 콤보 상자가 변경 될 때마다

  1. , NRE가 생성 프레임 워크를 데이터 바인딩에 의해 삼켰을 : 그러나, 나는이 발생하거나 해결하는 방법을 왜 알아낼 수 없습니다이 설정 두 가지 문제로 실행 한 (Visual Studio IDE의 직접 실행 창에 표시되는 것을 볼 수 있습니다.)이 팁은 올바르게 설정되지 않았다는 것을 알려줍니다. 두 번째 콤보 상자 또는 다른 관련되지 않은 데이터 바인딩 된 컨트롤 (콤보 상자 또는 기타)을 변경하면 NRE가 생성되지 않습니다.
  2. 위의 NRE를 생성 한 후 첫 번째 콤보 상자가 변경 될 때마다 두 번째 콤보 상자가 성공적으로로드되지만 첫 번째 콤보 상자의 선택한 인덱스는 -1로 다시 설정됩니다. 데이터 바인딩의 "밀어 넣기"이벤트가 컨트롤을 업데이트하기 위해 발생하고 첫 번째 콤보 상자를 지원하는 내 DTO 속성의 값이 NULL/Nothing으로 재설정되기 때문에 이것이 원인이라고 생각됩니다.

이러한 일이 발생하는 이유에 대해 아는 사람이 있습니까? 나는 위의 두 가지 문제를 보여주는 내 문제를 조롱했다. 또한 다른 콤보 상자에 대한 종속성이없는 콤보 상자가 정상적으로 작동하는지 확인하기위한 위생 상태 검사와 마찬가지로 첫 번째 항목과 상관이없는 세 번째 콤보 상자를 추가했습니다.

이 코드는 Visual Basic Windows Forms 프로젝트 (3.5 Framework)의 기본 Form1 클래스에 대한 코드로 붙여 넣습니다.

Imports System 
Imports System.Collections.Generic 
Imports System.Linq 
Imports System.Windows.Forms 

Public Class Form1 
    Inherits System.Windows.Forms.Form 

    'Form overrides dispose to clean up the component list. 
    <System.Diagnostics.DebuggerNonUserCode()> _ 
    Protected Overrides Sub Dispose(ByVal disposing As Boolean) 
     Try 
      If disposing AndAlso components IsNot Nothing Then 
       components.Dispose() 
      End If 
     Finally 
      MyBase.Dispose(disposing) 
     End Try 
    End Sub 

    'Required by the Windows Form Designer 
    Private components As System.ComponentModel.IContainer 

    'NOTE: The following procedure is required by the Windows Form Designer 
    'It can be modified using the Windows Form Designer. 
    'Do not modify it using the code editor. 
    <System.Diagnostics.DebuggerStepThrough()> _ 
    Private Sub InitializeComponent() 
     Me.cboA = New System.Windows.Forms.ComboBox() 
     Me.cboB = New System.Windows.Forms.ComboBox() 
     Me.cboC = New System.Windows.Forms.ComboBox() 
     Me.SuspendLayout() 
     ' 
     'cboA 
     ' 
     Me.cboA.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList 
     Me.cboA.FormattingEnabled = True 
     Me.cboA.Location = New System.Drawing.Point(120, 25) 
     Me.cboA.Name = "cboA" 
     Me.cboA.Size = New System.Drawing.Size(121, 21) 
     Me.cboA.TabIndex = 0 
     ' 
     'cboB 
     ' 
     Me.cboB.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList 
     Me.cboB.FormattingEnabled = True 
     Me.cboB.Location = New System.Drawing.Point(120, 77) 
     Me.cboB.Name = "cboB" 
     Me.cboB.Size = New System.Drawing.Size(121, 21) 
     Me.cboB.TabIndex = 1 
     ' 
     'cboC 
     ' 
     Me.cboC.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList 
     Me.cboC.FormattingEnabled = True 
     Me.cboC.Location = New System.Drawing.Point(120, 132) 
     Me.cboC.Name = "cboC" 
     Me.cboC.Size = New System.Drawing.Size(121, 21) 
     Me.cboC.TabIndex = 2 
     ' 
     'Form1 
     ' 
     Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) 
     Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font 
     Me.ClientSize = New System.Drawing.Size(284, 262) 
     Me.Controls.Add(Me.cboC) 
     Me.Controls.Add(Me.cboB) 
     Me.Controls.Add(Me.cboA) 
     Me.Name = "Form1" 
     Me.Text = "Form1" 
     Me.ResumeLayout(False) 

    End Sub 
    Friend WithEvents cboA As System.Windows.Forms.ComboBox 
    Friend WithEvents cboB As System.Windows.Forms.ComboBox 
    Friend WithEvents cboC As System.Windows.Forms.ComboBox 

    Private _DataObject As MyDataObject 
    Private _IsInitialized As Boolean = False 

    Public Sub New() 
     ' This call is required by the Windows Form Designer. 
     InitializeComponent() 

     ' Add any initialization after the InitializeComponent() call. 
     _DataObject = New MyDataObject() 
     BindControls() 
    End Sub 

    Private Sub BindControls() 
     LoadComboA(cboA) 
     Dim cmbABinding As New Binding("SelectedValue", _DataObject, "ValueA", True, DataSourceUpdateMode.OnPropertyChanged) 
     cboA.DataBindings.Add(cmbABinding) 

     Dim cmbBBinding As New Binding("SelectedValue", _DataObject, "ValueB", True, DataSourceUpdateMode.OnPropertyChanged) 
     cboB.DataBindings.Add(cmbBBinding) 

     LoadComboC(cboC) 
     Dim cmbCBinding As New Binding("SelectedValue", _DataObject, "ValueC", True, DataSourceUpdateMode.OnPropertyChanged) 
     cboC.DataBindings.Add(cmbCBinding) 
    End Sub 

    Protected Overrides Sub OnLoad(ByVal e As System.EventArgs) 
     MyBase.OnLoad(e) 
     _IsInitialized = True 
     cboA.SelectedIndex = 0 
     cboC.SelectedIndex = 0 
    End Sub 

    Private Sub ComboA_SelectedValueChanged(ByVal sender As Object, ByVal e As EventArgs) Handles cboA.SelectedValueChanged 
     If _IsInitialized Then 
      LoadComboB(cboB, cboA.SelectedValue.ToString()) 
      cboB.SelectedIndex = 0 
     End If 
    End Sub 

    Private Sub LoadComboA(ByVal cmbBox As ComboBox) 
     Dim someData As New Dictionary(Of String, String)() 
     someData.Add("Value1", "Text 1") 
     someData.Add("Value2", "Text 2") 
     someData.Add("Value3", "Text 3") 
     cmbBox.DataSource = someData.ToList() 
     cmbBox.DisplayMember = "Value" 
     cmbBox.ValueMember = "Key" 
    End Sub 

    Private Sub LoadComboB(ByVal cmbBox As ComboBox, ByVal selector As String) 
     Dim someSubData As New Dictionary(Of String, String)() 
     Select Case selector 
      Case "Value1" 
       someSubData.Add("SubValue1", "Value1 - Sub Text 1") 
       someSubData.Add("SubValue2", "Value1 - Sub Text 2") 
       someSubData.Add("SubValue3", "Value1 - Sub Text 3") 
      Case "Value2" 
       someSubData.Add("SubValue4", "Value2 - Sub Text 4") 
       someSubData.Add("SubValue5", "Value2 - Sub Text 5") 
       someSubData.Add("SubValue6", "Value2 - Sub Text 6") 
      Case "Value3" 
       someSubData.Add("SubValue7", "Value3 - Sub Text 7") 
       someSubData.Add("SubValue8", "Value3 - Sub Text 8") 
       someSubData.Add("SubValue9", "Value3 - Sub Text 9") 
     End Select 
     cmbBox.DataSource = someSubData.ToList() 
     cmbBox.DisplayMember = "Value" 
     cmbBox.ValueMember = "Key" 
    End Sub 

    Private Sub LoadComboC(ByVal cmbBox As ComboBox) 
     Dim someData As New Dictionary(Of String, String)() 
     someData.Add("Value100", "Text 100") 
     someData.Add("Value101", "Text 101") 
     cmbBox.DataSource = someData.ToList() 
     cmbBox.DisplayMember = "Value" 
     cmbBox.ValueMember = "Key" 
    End Sub 

End Class 

Public Class MyDataObject ' DTO class 

    Private _ValueA As String 
    Public Property ValueA() As String 
     Get 
      Return _ValueA 
     End Get 
     Set(ByVal value As String) 
      _ValueA = value 
     End Set 
    End Property 

    Private _ValueB As String 
    Public Property ValueB() As String 
     Get 
      Return _ValueB 
     End Get 
     Set(ByVal value As String) 
      _ValueB = value 
     End Set 
    End Property 

    Private _ValueC As String 
    Public Property ValueC() As String 
     Get 
      Return _ValueC 
     End Get 
     Set(ByVal value As String) 
      _ValueC = value 
     End Set 
    End Property 

End Class 
+0

이 질문은 종속 콤보 상자 집합, 개체에 바인딩 된 데이터로 인해 하나 이상의 콤보 상자의 기본 기능이 실패하는 방식을 설명하려고합니다. 계단식/종속 형 콤보 상자는 매우 일반적입니다. 데이터 바인딩 또한 일반적인 관행입니다. 나는이 상황이 매우 좁은 것은 아니라고 생각한다. 이 질문을 다시 입력하십시오. –

+0

동의합니다. 다시여십시오. – OneFineDay

답변

1

오동작하면 데이터 바인딩을 디버깅하기가 어려울 수 있습니다. 여기서 두 가지 일이 잘못되어 진단하기가 어렵습니다. 첫 번째로 계산하지 못한 것은 이 SelectedValueChanged 이벤트를 발생시키고 전에 통화 관리자가 바운드 개체를 업데이트한다는 것입니다. 일반적으로 문제는 아니지만 이벤트 처리기는 부작용이 있습니다. 다음에 계산하지 못한 것은 하나의 바운드 개체의 속성을 변경하면 기타 속성도 업데이트됩니다.

그리고 문지르면 _DataObject.ValueA는 콤보 B를 업데이트 할 때 여전히 Nothing입니다. _DataObject.ValueB를 업데이트합니다. 따라서 통화 관리자는 콤보 A 을 다시으로 업데이트하여 ValueA 속성의 Nothing 값과 일치 시키려고합니다. 어느 것이 NullReferenceException을 생성했는지 또한 알 수 있습니다.

한 가지 가능한 해결 방법은 SelectedValueChanged 이벤트 처리기의 부작용을 지연시키고 통화 관리자가 바운드 개체를 업데이트 할 때까지 연기하는 것입니다. Control.BeginInvoke()를 사용하여 정상적으로 수행 할 수있는 것보다 UI 스레드가 다시 유휴 상태가 될 때 대상이 실행됩니다. 이 문제가 해결되었습니다 :

Private Sub ComboA_SelectedValueChanged(ByVal sender As Object, ByVal e As EventArgs) Handles cboA.SelectedValueChanged 
    If _IsInitialized Then Me.BeginInvoke(New MethodInvoker(AddressOf LoadB)) 
End Sub 

Private Sub LoadB() 
    LoadComboB(cboB, cboA.SelectedValue.ToString()) 
    cboB.SelectedIndex = 0 
End Sub 

콤보 상자를 업데이트하는 대신 _DataObject를 업데이트하는 것이 더 깨끗한 픽스입니다. 그러나 당신은 사전을 사용하여 조금 어려워졌습니다. 나는 그것을 추구하지 않았습니다.

+0

+1 답변과 추수 감사합니다. 내 문제는 단순히 사건 발생 순서에 관한 문제 였고 빠른 해결책은 효과가있었습니다. –

관련 문제