2009-09-27 9 views
3

나는 다음과 같은 XAML 마크 업이 있습니다WPF 콤보 상자 바인딩 행동

<TextBox x:Name="MyTextBox" Text="{Binding Path=SelectedCustomer.FavouriteProduct.ProductNumber, UpdateSourceTrigger=PropertyChanged}" /> 
<ComboBox x:Name="MyComboBox" ItemsSource="{Binding Products}" DisplayMemberPath="ProductName" 
    SelectedValue="{Binding Path=SelectedCustomer.FavouriteProduct.ProductNumber}" 
    SelectedValuePath="ProductNumber" /> 

내보기의 DataContext에이 SelectedCustomer라는 공용 속성을 포함하는 뷰 모델에 바인딩됩니다. 고객 오브젝트에는 Product 유형의 FavouriteProduct 특성이 있고 Product 오브젝트에는 Public 특성 ProductNumber 및 ProductName이 들어 있습니다.

내가 찾고있는 동작은 ComboBox의 SelectedItem에서 TextBox의 텍스트를 업데이트하고 그 반대의 경우도 마찬가지입니다. TextBox에 ComboBox는 잘 작동합니다. ComboBox에서 제품을 선택하면 해당 제품의 제품 번호로 TextBox가 업데이트됩니다. 그러나 내가 다른 길로 가려고 할 때 나는 이상한 행동을한다. 선택한 항목 앞에 오는 항목에 대해서만 작동합니다. 내가 설명하려고합니다 :

제품의 다음 목록 고려 ([제품 번호], [제품 이름]) :

  1. 환타
  2. 펩시
  3. 코카콜라
  4. 스프라이트

이제 SelectedCustomer의 favouri te 제품은 코카콜라 (개발자 여야 함)입니다. 그래서 창이 열리면 TextBox는 3을 읽고 ComboBox는 Coca Cola를 읽습니다. 아름다운. 이제 TextBox의 제품 번호를 2로 변경할 수 있습니다. ComboBox는 값을 Pepsi로 업데이트합니다. 이제 TextBox의 제품 번호를 Coca Cola (3) 이상의 번호로 변경하십시오. 그리 좋지 않아. 4 (Sprite) 또는 5 (Water)를 선택하면 ComboBox가 코카콜라로 돌아갑니다. 따라서 ItemSource의 목록에서 창 너비를 여는 항목 아래에 아무 것도 작동하지 않는 것 같습니다. 그것을 1 (Fanta)로 설정하고 다른 것은 작동하지 않습니다. 5 (물)로 설정하면 모두 작동합니다. 이 작업은 ComboBox의 초기화 작업과 관련이 있습니까? 잠재적 인 버그? 누군가 다른 사람이이 행동을 본다면 궁금합니다.

UPDATE : SelectedProduct 및 SelectedProductNumber에 대한

마이크 브라운의 응답을 읽은 후 내가 만든 속성. 이 문제는 ComboBox에서 무언가를 선택하자마자 속성이 서로 유지되는 무한 루프에 빠지게됩니다. OnPropertyChanged 처리기를 잘못 구현했거나 누락 된 부분이 있습니까? 나는 두 속성에서 SelectedCustomer.FavouriteProduct를 업데이트 한 후 그 값을 읽을 때 것을 사용하여 약간 이제 구현을 변경 한 2

private int _SelectedProductNumber = -1; 
     public int SelectedProductNumber 
     { 
      get 
      { 
       if (_SelectedProductNumber == -1 && SelectedCustomer.Product != null) 
        _SelectedProductNumber = SelectedCustomer.Product.ProductNumber; 
       return _SelectedProductNumber; 
      } 
      set 
      { 
       _SelectedProductNumber = value; 
       OnPropertyChanged("SelectedProductNumber"); 
       _SelectedProduct = ProductList.FirstOrDefault(s => s.ProductNumber == value); 
      } 
     } 

     private Product _SelectedProduct; 
     public Product SelectedProduct 
     { 
      get 
      { 
       if (_SelectedProduct == null) 
        _SelectedProduct = SelectedCustomer.Product; 
       return _SelectedProduct; 
      } 
      set 
      { 
       _SelectedProduct = value; 
       OnPropertyChanged("SelectedProduct"); 
       _SelectedProductNumber = value.ProductNumber; 
      } 
     } 

     public event PropertyChangedEventHandler PropertyChanged; 

     protected virtual void OnPropertyChanged(string property) 
     { 
      if (PropertyChanged != null) 
       PropertyChanged(this, new PropertyChangedEventArgs(property)); 
     } 

업데이트 : 여기 내 뷰 모델에서 코드 예제이다. 이제는 작동하지만 올바른 방법이라고 확신하지 못합니다.

private int _SelectedProductNumber = 0; 
public int SelectedProductNumber 
{ 
    get 
    { 
     if (SelectedCustomer.Product != null) 
      _SelectedProductNumber = SelectedCustomer.Product.ProductNumber; 
     return _SelectedProductNumber; 
    } 
    set 
    { 
     _SelectedProductNumber = value; 
     SelectedCustomer.FavouriteProduct = ProductList.FirstOrDefault(s => s.ProductNumber == value); 
     OnPropertyChanged("SelectedProductNumber"); 
     OnPropertyChanged("SelectedProduct"); 
    } 
} 

private Product _SelectedProduct; 
public Product SelectedProduct 
{ 
    get 
    { 
     if (SelectedCustomer.Product != null) 
      _SelectedProduct = SelectedCustomer.Product; 
     return _SelectedProduct; 
    } 
    set 
    { 
     _SelectedProduct = value; 
     SelectedCustomer.FavouriteProduct = value; 
     OnPropertyChanged("SelectedProduct"); 
     OnPropertyChanged("SelectedProductNumber"); 
    } 
} 
+0

오류가 발생했습니다 ... 올바른 코드로 답변을 업데이트했습니다. –

+0

속성이 실제로 변경되지 않았을 때 변경된 이벤트가 발생하지 않도록하려면 'if (_selectedProduct! = value) {/*...*/}'블록을 사용하십시오. –

+0

귀하의 새로운 솔루션은 완벽하게 유효합니다 (실제로 반복되는 정보는 피할 수 있습니다). 그러나 나에게 이상한 점은 스택 오버 플로우가 여전히 발생하고 있다는 것입니다. 나는 코드를 VS에 넣었고 나를 위해 잘 돌아갔다. 너의 편에서 무엇이 잘못 될지 확신하지 못한다. 변경 사항이 selectedcustomer.favoriteproduct로 변경 되었습니까? 다른 두 속성에 대한 업데이트도 필요합니까? 어느 방법을 사용해도 내 샘플 코드를 My Machine (TM)에서 작동하는 버전으로 업데이트했습니다 –

답변

0

시도 : 의 selectedItem = "{바인딩 경로 = YourPath, 모드 = 양방향"} 대신 SelectedValue 및 SelectedValuePath을 설정 .

SelectedValue에도 사용할 수 있습니다. 기본값이 아니기 때문에 Mode = TwoWay를 잊지 마십시오.

마스터 세부 패턴을 사용하는 것이 좋습니다. 데이터 원본 컬렉션과 세부 정보보기 (예 : 텍스트 상자)에 마스터 (항목보기, 예 : 콤보 상자)를 원본 컬렉션의 선택한 항목에 바인딩 적절한 속성을 읽고 쓰는 바인딩 변환기. 여기

은 예이다 : 결합 마스터 폼 {바인딩} 또는 {SourceCollection 바인딩}와 결합 세부 형태 {바인딩}이다 또는 {SourceCollection 바인딩}이다 http://blogs.microsoft.co.il/blogs/tomershamam/archive/2008/03/28/63397.aspx

통지.

이 작업을 수행하려면 선택한 항목을 유지하는 개체로 컬렉션을 래핑해야합니다. WPF에는 이러한 기본 제공 개체 중 하나 인 ObjectDataProvider가 있습니다.

예 : 나는 folloiwng 그래서 내가 볼 수 있습니다 옵션을 지원 쓴 있도록 http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/068977c9-95a8-4b4a-9d38-b0cc36d06446

+0

SelectedItem을 사용하여 ProductNumber를 기반으로 선택한 제품을 설정하는 방법이 표시되지 않습니다 –

+0

수정 된 답변으로 문제를 해결할 수 있습니다. –

1

당신의 목표도 명확하지 않다. 아래 그림과 같이

동기에 하나 개의 항목에 바인딩 두 가지 요소 당신이 당신의 콤보 상자에 IsSynchronizedWithCurrentItem="True"을 설정할 수를 유지하려면 :

<TextBox x:Name="MyTextBox" Text="{Binding Path=SelectedCustomer.FavouriteProduct.ProductNumber, UpdateSourceTrigger=PropertyChanged}" /> 
<ComboBox x:Name="MyComboBox" ItemsSource="{Binding Products}" DisplayMemberPath="ProductName" 
    SelectedValue="{Binding Path=SelectedCustomer.FavouriteProduct.ProductNumber}" 
    IsSynchronizedWithCurrentItem="True" 
    SelectedValuePath="ProductNumber" /> 

이에 계속됩니다 같은 배경 개체에 바인딩 현재 창에서 모든 것을 의미합니다 동기화하고보고있는 이상한 행동을하지 마십시오.

이 더 이상 MSDN article이 인용 형태는 효과를 설명 :

IsSynchronizedWithCurrentItem 속성은, 때 선택의 변경, 즉 이까지 는 AS "현재 항목"어떤 변화 점에서 중요하다 창이 관련되어 있습니다. 그러면이 개체가 WPF 엔진에 이 현재 항목을 변경하는 데 사용됩니다. 이 특성이 없으면 DataContext의 현재 항목이 이 아니므로 텍스트 상자 은 여전히 ​​목록의 첫 번째 항목 인 에있는 것으로 간주합니다. 만 보장합니다 다른 대답에 의해 제안

그런 다음 Mode=TwoWay 설정이 모두 당신은 기본 객체가 업데이트됩니다 텍스트 상자와 때 텍스트 상자가 업데이트되는 개체를 업데이트를 업데이트 할 때.

이 선택한 항목 텍스트와하지가 일치하는 텍스트로 콤보 목록에서 항목을 선택하고 텍스트 상자 편집을합니다 (대체 당신이 달성하려고 할 수 있다고 생각하는?)

가 동기화 선택 효과를 달성하기 위해 콤보 상자에 IsEditable="True"을 설정하면 사용자가 텍스트 상자에 항목을 입력하고 텍스트 상자를 삭제할 수 있습니다. 또는 두 개의 상자가 필요한 경우 IsSynchronizedWithCurrentItem="True"IsEditable="True"의 두 번째 콤보 상자로 텍스트 상자를 바꾸면 텍스트 상자처럼 보이게 스타일이 지정됩니다.

+0

이것은 매우 유망 해 보였지만 불행히도 나는 여전히 같은 행동을하고 있습니다. ComboBox에서 무언가를 선택하면 TextBox가 업데이트되지만 다른 방법은 제대로 작동하지 않습니다. 나는 그것이 틀리게 생각할지도 모른다라고 생각한다. –

+0

텍스트 상자의 텍스트를 검색하여 선택 항목을 변경하거나 선택한 항목의 텍스트를 변경/편집 하시겠습니까? 두 가지 해결책으로 답변을 업데이트했습니다. – John

+0

나는 제품을 선택하는 두 가지 옵션을 사용자에게 제공하려고한다. ComboBox에서 제품을 선택하거나 제품 번호를 입력하십시오. ComboBox를 편집 가능하게 만들 수는 있지만 제품 번호를 입력 할 수 없습니다. IsSynchronizedWithCurrentItem 옵션을 사용하여 별도의 ComboBox를 사용하지 않았습니다. –

1

당신이하고 싶은 일은 현재 선택된 제품과 현재 선택된 제품 번호에 대한 ViewModel의 개별 속성을 노출하는 것입니다. 선택한 제품이 변경되면 제품 번호를 업데이트하고 그 반대의 경우도 마찬가지입니다.그래서 당신의 ViewModel이

public class MyViewModel:INotifyPropertyChanged 
{ 
    private Product _SelectedProduct; 
    public Product SelectedProduct 
    { 
     get { return _SelectedProduct; } 
     set 
     { 
      _SelectedProduct = value; 
      PropertyChanged(this, new PropertyChangedEventArgs("SelectedProduct")); 
      _SelectedProductID = _SelectedProduct.ID; 
      PropertyChanged(this, new PropertyChangedEventArgs("SelectedProductID")); 
     } 
    } 
    private int _SelectedProductID; 
    public int SelectedProductID 
    { 
     get { return _SelectedProductID; } 
     set 
     { 
      _SelectedProductID = value; 
      PropertyChanged(this, new PropertyChangedEventArgs("SelectedProductID")); 
      _SelectedProduct = _AvailableProducts.FirstOrDefault(p => p.ID == value); 
      PropertyChanged(this,new PropertyChangedEventArgs("SelectedProduct")); 
     } 
    } 
    private IEnumerable<Product> _AvailableProducts = GetAvailableProducts(); 

    private static IEnumerable<Product> GetAvailableProducts() 
    { 
     return new List<Product> 
        { 
         new Product{ID=1, ProductName = "Coke"}, 
         new Product{ID = 2, ProductName="Sprite"}, 
         new Product{ID = 3, ProductName = "Vault"}, 
         new Product{ID=4, ProductName = "Barq's"} 
        }; 
    } 

    public IEnumerable<Product> AvailableProducts 
    { 
     get { return _AvailableProducts; } 
    } 
    private Customer _SelectedCustomer; 
    public Customer SelectedCustomer 
    { 
     get { return _SelectedCustomer; } 
     set 
     { 
      _SelectedCustomer = value; 
      PropertyChanged(this, new PropertyChangedEventArgs("SelectedCustomer")); 
      SelectedProduct = value.FavoriteProduct; 
     } 
    } 
    public event PropertyChangedEventHandler PropertyChanged; 
} 

처럼 보일 것이다 이제 당신의 XAML은 해당 속성에 결합하고, 뷰 모델은에서 INotifyPropertyChanged와의 나머지 부분을 구현하는 것을 잊지 마세요 syncrhronization

<TextBox 
    x:Name="MyTextBox" 
    Text="{Binding Path=SelectedProductID, UpdateSourceTrigger=PropertyChanged}" /> 
<ComboBox 
    x:Name="MyComboBox" 
    ItemsSource="{Binding AvailableProducts}" 
    DisplayMemberPath="ProductName" 
    SelectedItem="{Binding SelectedProduct}" /> 

을 담당하는 GetAvailableProducts 기능. 또한 약간의 오류가있을 수 있습니다. 나는 VS를 사용하는 대신에 이것을 여기에 타이프한다. 그러나 당신은 일반적인 아이디어를 얻어야한다.

+0

나는 이런 식으로해야 할 것 같은데. 각각의 속성을 추가했지만 ProductId와 Product 속성간에 문제가 있습니다. 당신이 그들 중 하나를 변경하자마자 끝없는 루프에 들어가서 마침내 StackOverflow 예외를 얻습니다;) 그것은 (내가 WPF에 상당히 익숙하다) 핸들러를 구현하는 방법 때문일 수 있습니다. 코드로 질문을 업데이트하겠습니다. –

+0

죄송합니다 그게 나에게 boneheaded ... 반대 속성을 업데이 트하는 대신 단지 뒷받침 필드를 업데이 트하고 속성 변경 호출 ... 나는 올바른 구현 코드를 업데이 트합니다. –

+1

좋아요 ... 이제는 잘될 것입니다 ... 스택 오버플로가 발생하는 답변에 특별한 배지를 추가해야합니다.) –