2011-03-02 2 views
3

내 제목 잘 모르겠어요하지만 내가 지금 직면하고있는 문제가 MVVM 구현은 .. 나는 .. XAML 코드 아래를상호 배타적 콤보 -

이 기본적으로 무엇을
 <ItemsControl.ItemTemplate> 

     <DataTemplate> 

      <StackPanel Orientation="Horizontal"> 

         <ComboBox ItemsSource="{Binding Path=AvailableFields}" 

          SelectedItem="{Binding Path=SelectedField}" 

          ></ComboBox> 

      </StackPanel> 

     </DataTemplate> 

     </ItemsControl.ItemTemplate> 

내 데이터 소스 10 항목이 포함되어있는 경우,이 콤보의 10 행을 생성하는 것입니다 및 모든 콤보가 같은 itemsource에 묶여되어있다.

이제 첫 번째 콤보 상자에서 항목을 선택하면 해당 항목을 후속 콤보 상자에서 사용할 수 없어야합니다. MVVM 및 WPF에서이 요구 사항을 어떻게 충족시킬 수 있습니까?

+0

이 답변이 도움이 되었나요? 그렇다면 당신은 upvote 받아 받아야한다, 그렇지 않으면 우리가 잘못 된 부분을 알 수 있도록 몇 가지 피드백을 제공합니다. –

답변

2

이 기능은 WPF에서 제공하지 않지만 일부 사용자 지정 코딩을 사용하여 구현할 수 있습니다.

내가 3 개 뷰 모델 클래스를 만들었습니다

PreferencesVM - 이것은 우리의 DataContext 될 것입니다. 여기에는 ComboBox에 나타날 수있는 옵션의 마스터 목록이 포함되어 있으며 다양한 ComboBox에서 선택된 항목을 추적하는 SelectedOptions 속성도 있습니다. 그것은 또한 ItemsControl.ItemsSource를 바인딩 할 Preferences 속성을 가지고 있습니다.

PreferenceVM - 이것은 하나의 ComboBox를 나타냅니다. ComboBox.SelectedItem이 바인딩 된 SelectedOption 속성이 있습니다. 또한 PreferencesVM에 대한 참조와 Options (ComboBox.ItemsSource가 바인딩 됨)라는 속성이 있으며 ComboBox에 항목이 표시 될 수 있는지 확인하는 필터를 통해 PreferencesVM에 대한 옵션을 반환합니다.

옵션 VM - ComboBox의 행을 나타냅니다. PreferenceVM.SelectedOption 설정하면

  1. (a ComboBoxItem 선택한 IE), 아이템이 PreferencesVM.AllOptions 컬렉션에 추가된다

    다음 사항

    용액에 키를 형성한다.
  2. PreferenceVM은 Preferences.SelectedItems.CollectionChanged를 처리하고 Options 속성에 대해 PropertyChanged를 발생시켜 새로 고침을 트리거합니다.
  3. PreferenceVM.Options는 필터를 사용하여 반환 할 항목을 결정합니다. 이는 PreferencesVM.SelectedOptions에없는 항목 만 허용합니다 (SelectedOption이 아닌 경우).

위에서 설명한 내용만으로도 충분하지만 두통을 줄이기 위해 아래 코드를 게시 할 것입니다.

PreferencesVM.cs :

public class PreferencesVM 
     { 
      public PreferencesVM() 
      { 
       PreferenceVM pref1 = new PreferenceVM(this); 
       PreferenceVM pref2 = new PreferenceVM(this); 
       PreferenceVM pref3 = new PreferenceVM(this); 

       this._preferences.Add(pref1); 
       this._preferences.Add(pref2); 
       this._preferences.Add(pref3); 
       //Only three ComboBoxes, but you can add more here. 

       OptionVM optRed = new OptionVM("Red"); 
       OptionVM optGreen = new OptionVM("Green"); 
       OptionVM optBlue = new OptionVM("Blue"); 


       _allOptions.Add(optRed); 
       _allOptions.Add(optGreen); 
       _allOptions.Add(optBlue); 
      } 

      private ObservableCollection<OptionVM> _selectedOptions =new ObservableCollection<OptionVM>(); 
      public ObservableCollection<OptionVM> SelectedOptions 
      { 
       get { return _selectedOptions; } 
      } 

      private ObservableCollection<OptionVM> _allOptions = new ObservableCollection<OptionVM>(); 
      public ObservableCollection<OptionVM> AllOptions 
      { 
       get { return _allOptions; } 
      } 


      private ObservableCollection<PreferenceVM> _preferences = new ObservableCollection<PreferenceVM>(); 
      public ObservableCollection<PreferenceVM> Preferences 
      { 
       get { return _preferences; } 
      } 
     } 

PreferenceVM.cs :

public class PreferenceVM:INotifyPropertyChanged 
    { 
     private PreferencesVM _preferencesVM; 
     public PreferenceVM(PreferencesVM preferencesVM) 
     { 
      _preferencesVM = preferencesVM; 
      _preferencesVM.SelectedOptions.CollectionChanged += new NotifyCollectionChangedEventHandler(SelectedOptions_CollectionChanged); 
     } 

     void SelectedOptions_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) 
     { 
      if (this.PropertyChanged != null) 
       this.PropertyChanged(this,new PropertyChangedEventArgs("Options")); 
     } 

     private OptionVM _selectedOption; 
     public OptionVM SelectedOption 
     { 
      get { return _selectedOption; } 
      set 
      { 
       if (value == _selectedOption) 
        return; 
       if (_selectedOption != null) 
        _preferencesVM.SelectedOptions.Remove(_selectedOption); 
       _selectedOption = value; 
       if (_selectedOption != null) 
        _preferencesVM.SelectedOptions.Add(_selectedOption); 
      } 
     } 

     private ObservableCollection<OptionVM> _options = new ObservableCollection<OptionVM>(); 
     public IEnumerable<OptionVM> Options 
     { 
      get { return _preferencesVM.AllOptions.Where(x=>Filter(x)); } 
     } 

      private bool Filter(OptionVM optVM) 
      { 
       if(optVM==_selectedOption) 
        return true; 
       if(_preferencesVM.SelectedOptions.Contains(optVM)) 
        return false; 
       return true; 
      } 

      public event PropertyChangedEventHandler PropertyChanged; 
    } 

OptionVM.cs: 

    public class OptionVM 
    { 
     private string _name; 
     public string Name 
     { 
      get { return _name; } 
     } 

     public OptionVM(string name) 
     { 
      _name = name; 
     } 
} 

MainWindow.xaml.cs를 :

public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 
      this.DataContext = new PreferencesVM(); 
     } 
} 

MainWindow를.xaml :

<Window x:Class="WpfApplication64.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindow" Height="350" Width="525"> 
    <Grid> 
     <ItemsControl ItemsSource="{Binding Path=Preferences}"> 
      <ItemsControl.ItemTemplate> 
       <DataTemplate> 
        <ComboBox ItemsSource="{Binding Path=Options}" DisplayMemberPath="Name" SelectedItem="{Binding Path=SelectedOption}"></ComboBox> 
       </DataTemplate> 
      </ItemsControl.ItemTemplate> 
     </ItemsControl> 
    </Grid> 
</Window> 

** 코드 줄을 줄이기 위해 제공된 솔루션은 10 개가 아닌 3 개의 ComboBox 만 생성합니다.

2

이것은 코딩을 시작할 때 생각했던 것보다 힘들었습니다. 아래 샘플은 원하는 것을 수행합니다. 콤보 상자에는 아직 사용할 수 있고 다른 콤보 상자에서 선택되지 않은 모든 문자가 포함됩니다.

XAML : 뒤에

<Window x:Class="TestApp.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="Window1" Height="300" Width="300"> 

    <StackPanel> 
     <ItemsControl ItemsSource="{Binding Path=SelectedLetters}"> 
      <ItemsControl.ItemTemplate> 
       <DataTemplate> 
        <ComboBox 
         ItemsSource="{Binding Path=AvailableLetters}" 
         SelectedItem="{Binding Path=Letter}" /> 
       </DataTemplate> 
      </ItemsControl.ItemTemplate> 
     </ItemsControl> 
    </StackPanel> 

</Window> 

코드 :

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Linq; 
using System.Windows; 

namespace TestApp 
{ 
    public partial class Window1 : Window 
    { 
     public Window1() 
     { 
      InitializeComponent(); 

      DataContext = new VM(); 
     } 
    } 

    public class VM : INotifyPropertyChanged 
    { 
     public VM() 
     { 
      SelectedLetters = new List<LetterItem>(); 
      for (int i = 0; i < 10; i++) 
      { 
       LetterItem letterItem = new LetterItem(); 
       letterItem.PropertyChanged += OnLetterItemPropertyChanged; 
       SelectedLetters.Add(letterItem); 
      } 
     } 

     public List<LetterItem> SelectedLetters { get; private set; } 

     private void OnLetterItemPropertyChanged(object sender, PropertyChangedEventArgs e) 
     { 
      if (e.PropertyName != "Letter") 
      { 
       return; 
      } 

      foreach (LetterItem letterItem in SelectedLetters) 
      { 
       letterItem.RefreshAvailableLetters(SelectedLetters); 
      } 
     } 

     public event PropertyChangedEventHandler PropertyChanged; 

     public class LetterItem : INotifyPropertyChanged 
     { 
      static LetterItem() 
      { 
       _allLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".Select(c => c.ToString()); 
      } 

      public LetterItem() 
      { 
       AvailableLetters = _allLetters; 
      } 

      public void RefreshAvailableLetters(IEnumerable<LetterItem> letterItems) 
      { 
       AvailableLetters = _allLetters.Where(c => !letterItems.Any(li => li.Letter == c) || c == Letter); 
      } 

      private IEnumerable<string> _availableLetters; 
      public IEnumerable<string> AvailableLetters 
      { 
       get { return _availableLetters; } 
       private set 
       { 
        _availableLetters = value; 
        if (PropertyChanged != null) 
        { 
         PropertyChanged(this, new PropertyChangedEventArgs("AvailableLetters")); 
        } 
       } 
      } 


      private string _letter; 
      public string Letter 
      { 
       get { return _letter; } 
       set 
       { 
        if (_letter == value) 
        { 
         return; 
        } 
        _letter = value; 
        if (PropertyChanged != null) 
        { 
         PropertyChanged(this, new PropertyChangedEventArgs("Letter")); 
        } 
       } 
      } 

      public event PropertyChangedEventHandler PropertyChanged; 

      private static readonly IEnumerable<string> _allLetters; 
     } 
    } 
} 
관련 문제