2016-10-27 5 views
1

私はお互いを指している2つの異なるオブジェクトを持っています。最初のオブジェクトは、会社内の部門を表します。そのオブジェクトには2つのコレクションがあります。従業員は部門内で働くすべての従業員であり、プロジェクトはその部門内で進行中の特別プロジェクトです。だから、最初のオブジェクトは、次のようになります。部門に新しいプロジェクトを追加するとき、私はこのようになり、そのプロジェクトへの分割、への参照を渡すことComboBox、ItemsSourceとSelectedValueを異なるソースにバインドすることは可能ですか?

public class Division : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged = delegate { }; 

    ObservableCollection<Employee> _employees; 
    ObservableCollection<Project> _projects; 

    public Division() 
    { 
     Employees = new ObservableCollection<Employee>(); 
     Projects = new ObservableCollection<Project>(); 
    } 

    public ObservableCollection<Employee> Employees 
    { 
     get { return _employees; } 
     set 
     { 
      if (_employees != value) 
      { 
       _employees = value; 
       PropertyChanged(this, new PropertyChangedEventArgs("Employees")); 
      } 
     } 
    } 

    public ObservableCollection<Project> Projects 
    { 
     get { return _projects; } 
     set 
     { 
      if (_projects != value) 
      { 
       _projects = value; 
       PropertyChanged(this, new PropertyChangedEventArgs("Projects")); 
      } 
     } 
    } 

    public void AddNewProject() 
    { 
     this.Projects.Add(new Project(this)); 
    } 
} 

お知らせ:

public class Project : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged = delegate { }; 

    string _projectName; 
    DateTime _deadline = DateTime.Now; 
    Division _division; 
    ObservableCollection<Employee> _members; 

    public Project() 
    { 
     Members = new ObservableCollection<Employee>(); 
    } 

    public Project(Division div) 
    { 
     Members = new ObservableCollection<Employee>(); 
     Division = div; 
    } 

    public string ProjectName 
    { 
     get { return _projectName; } 
     set 
     { 
      if (_projectName != value) 
      { 
       _projectName = value; 
       PropertyChanged(this, new PropertyChangedEventArgs("ProjectName")); 
      } 
     } 
    } 

    public DateTime Deadline 
    { 
     get { return _deadline; } 
     set 
     { 
      if (_deadline != value) 
      { 
       _deadline = value; 
       PropertyChanged(this, new PropertyChangedEventArgs("Deadline")); 
      } 
     } 
    } 

    public Division Division 
    { 
     get { return _division; } 
     set 
     { 
      if (_division != value) 
      { 
       if (_division != null) 
       { 
        _division.Employees.CollectionChanged -= members_CollectionChanged; 
       } 
       _division = value; 
       if (_division != null) 
       { 
        _division.Employees.CollectionChanged += members_CollectionChanged; 
       } 
       PropertyChanged(this, new PropertyChangedEventArgs("Division")); 
      } 
     } 
    } 

    public ObservableCollection<Employee> Members 
    { 
     get { return _members; } 
     set 
     { 
      if (_members != value) 
      { 
       if (_members != null) 
       { 
        _members.CollectionChanged -= members_CollectionChanged; 
       } 
       _members = value; 
       if (_members != null) 
       { 
        _members.CollectionChanged += members_CollectionChanged; 
       } 
       PropertyChanged(this, new PropertyChangedEventArgs("Members")); 
      } 
     } 
    } 

    public ObservableCollection<Employee> AvailableEmployees 
    { 
     get 
     { 
      if (Division != null){ 
       IEnumerable<Employee> availables = 
       from s in Division.Employees 
       where !Members.Contains(s) 
       select s; 

       return new ObservableCollection<Employee>(availables); 
      } 

      return new ObservableCollection<Employee>(); 
     } 
    } 

    void members_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) 
    { 
     PropertyChanged(this, new PropertyChangedEventArgs("AvailableEmployees")); 
    } 
} 

私がこれをやっている理由は、プロジェクトにはどんなタイプのチームも働くことができますが、部門内からだけです。したがって、部門のダッシュボードを構築する場合、マネージャはそのプロジェクトの従業員を選択できますが、すでに割り当てられている従業員を配置することはありません。したがって、プロジェクトオブジェクトのAvailableEmployeesプロパティは、常にそのプロジェクトに割り当てられていない人を追跡します。

私が抱えている問題は、これをUIに変換する方法です。このビューに関連付けられたビューモデルは以下のようである

<UserControl x:Class="Test.Views.TestView" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     mc:Ignorable="d" 
     xmlns:local="clr-namespace:Test.Views" 
     d:DesignHeight="300" d:DesignWidth="300"> 

<StackPanel> 
    <ListBox ItemsSource="{Binding Div.Projects}"> 
     <ListBox.ItemTemplate> 
      <DataTemplate> 
       <Border Background="Transparent" 
         BorderThickness="0, 0, 0, 2" 
         BorderBrush="Black" 
         Margin="0, 0, 0, 5" 
         Padding="0, 0, 0, 5"> 
        <StackPanel> 
         <TextBox Text="{Binding ProjectName}"/> 
         <ListBox ItemsSource="{Binding Members}"> 
          <ListBox.ItemTemplate> 
           <DataTemplate> 
            <ComboBox ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=local:TestView}, Path=DataContext.AvailableEmployees}" 
               DisplayMemberPath="FirstName" 
               Text="{Binding FirstName}"/> 
           </DataTemplate> 
          </ListBox.ItemTemplate> 
         </ListBox> 
         <Button Content="Add Employee to Project" 
           Command="{Binding RelativeSource={RelativeSource AncestorType=local:TestView}, Path=DataContext.AddEmployeeToProject}" 
           CommandParameter="{Binding}"/> 
        </StackPanel> 
       </Border> 
      </DataTemplate> 
     </ListBox.ItemTemplate> 
    </ListBox> 
    <Button Content="Add New Project" 
      Command="{Binding AddNewProject}" /> 
</StackPanel> 

public class TestViewModel : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged = delegate { }; 

    private Division _div; 

    public TestViewModel(Division div) 
    { 
     Div = div; 

     AddNewProject = new DelegateCommand(OnAddNewProject); 
     AddEmployeeToProject = new DelegateCommand<Project>(OnAddEmployeeToProject); 
    } 

    public DelegateCommand AddNewProject { get; set; } 
    public DelegateCommand<Project> AddEmployeeToProject { get; set; } 

    public Division Div 
    { 
     get { return _div; } 
     set 
     { 
      if (_div != value) 
      { 
       _div = value; 
       PropertyChanged(this, new PropertyChangedEventArgs("Div")); 
      } 
     } 
    } 

    private void OnAddNewProject() 
    { 
     Div.AddNewProject(); 
    } 

    private void OnAddEmployeeToProject(Project proj) 
    { 
     var availables = proj.AvailableEmployees; 

     if (availables.Count > 0) 
     { 
      proj.Members.Add(availables[0]); 
     } 
    } 
} 

しかし、私はそれぞれのコンボボックスを得ることができない私がこれまで行ってきた実験では、このようになります各プロジェクトの従業員が働きます。選択したアイテム/値がアイテムソースにバインドされているように見えますが、コンボボックスが空白になるたびに表示されます。私はこれをSelectedValueとSelectedItemプロパティでコンボボックスにもしようとしましたが、うまくいきませんでした。

どのようにしてこれら2つを分けますか。私がここで逃しているものは他にありますか?

+0

おそらくOTですが、なぜDivを変更したときに 'PropertyChanged(" Class ")を呼びますか?また、「選択したアイテム/値がアイテムソースにバインドされているようです」という意味はどうですか?私はその文の意味を理解できません。 –

+0

実際には、コード内のどこでも 'SelectedValue'を使って何もしません。 –

+0

PropertyChangedの "クラス"は、(私がコードで行った以前の変更の)タイプミスです。それはPropertyChanged( "Div")でなければなりません。投稿で修正します。それは私のコメントになると、私はコンボボックスが毎回空白になる理由がわからないので、何が問題なのかちょうど推測しています。私はおそらく、コンボボックスのitemssourceがMembersコレクションに実際に入力された値に基づいて動的に変化するため、コンボボックスに実際に表示される値に影響を与えてしまうからです。 – user2921009

答えて

0

OK。私が思いついた最善の解決策は、ボタンとコンボボックスの両方で構成された独自のユーザーコントロールを作成することでした。自分のコンボボックスに期待していた動作を模倣しました。

最初に、プロジェクトと部門の両方のメンバーにEmployeeの同じインスタンスが含まれているモデルで、私は本当にばかげたミスを犯しました。これは、AvailableEmployeesプロパティをバグにします。私が本当にしなければならなかったのは、参照だけでなく、プロジェクト内の従業員のコピーのリストを作成することでした。

いずれにしても、私は新しいユーザーコントロールを作成し、DynamicSourceComboBoxという名前を付けました。このコントロールのXAMLは次のようになります。

<Grid> 
    <Button x:Name="selected" 
      Content="{Binding RelativeSource={RelativeSource AncestorType=local:DynamicSourceComboBox}, Path=SelectedValue}" 
      Click="selected_Click"/> 
    <ComboBox x:Name="selections" 
       ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=local:DynamicSourceComboBox}, Path=ItemsSource}" 
       DisplayMemberPath="{Binding RelativeSource={RelativeSource AncestorType=local:DynamicSourceComboBox}, Path=DisplayMemberPath}" 
       Visibility="Collapsed" 
       SelectionChanged="selections_SelectionChanged" 
       MouseLeave="selections_MouseLeave"/> 
</Grid> 

私はここにボタンからいくつかのバインディングと私のユーザーコントロールのプロパティにコンボボックスを持っています。これらは実際には依存関係のプロパティです。私のユーザーコントロールのコードビハインドは、次のようになります。

public partial class DynamicSourceComboBox : UserControl 
{ 
    public DynamicSourceComboBox() 
    { 
     InitializeComponent(); 
    } 

    public object SelectedValue 
    { 
     get { return (object)GetValue(SelectedValueProperty); } 
     set { SetValue(SelectedValueProperty, value); } 
    } 
    public static readonly DependencyProperty SelectedValueProperty = 
     DependencyProperty.Register("SelectedValue", typeof(object), typeof(DynamicSourceComboBox), new PropertyMetadata(null)); 

    public IEnumerable ItemsSource 
    { 
     get { return (IEnumerable)GetValue(ItemsSourceProperty); } 
     set { SetValue(ItemsSourceProperty, value); } 
    } 
    public static readonly DependencyProperty ItemsSourceProperty = 
     ComboBox.ItemsSourceProperty.AddOwner(typeof(DynamicSourceComboBox)); 

    public string DisplayMemberPath 
    { 
     get { return (string)GetValue(DisplayMemberPathProperty); } 
     set { SetValue(DisplayMemberPathProperty, value); } 
    } 
    public static readonly DependencyProperty DisplayMemberPathProperty = 
     ComboBox.DisplayMemberPathProperty.AddOwner(typeof(DynamicSourceComboBox)); 

    private void selected_Click(object sender, RoutedEventArgs e) 
    { 
     selected.Visibility = Visibility.Hidden; 
     selections.Visibility = Visibility.Visible; 
     selections.IsDropDownOpen = true; 
    } 

    private void selections_SelectionChanged(object sender, SelectionChangedEventArgs e) 
    { 
     selections.Visibility = Visibility.Collapsed; 
     selected.Visibility = Visibility.Visible; 
     selections.IsDropDownOpen = false; 

     if (e.AddedItems.Count == 1) 
     { 
      var item = e.AddedItems[0]; 

      Type itemType = item.GetType(); 
      var itemTypeProps = itemType.GetProperties(); 

      var realValue = (from prop in itemTypeProps 
          where prop.Name == DisplayMemberPath 
          select prop.GetValue(selections.SelectedValue)).First(); 

      SelectedValue = realValue; 
     } 
    } 

    private void selections_MouseLeave(object sender, MouseEventArgs e) 
    { 
     selections.Visibility = Visibility.Collapsed; 
     selected.Visibility = Visibility.Visible; 
     selections.IsDropDownOpen = false; 
    } 
} 

これらの依存関係プロパティは、コンボボックスで似た名前を持つプロパティを真似たが、彼らは内部コンボボックスとそれらが一緒に動作可能な方法でボタンにフックアップされています単一の複雑なコンボボックスとして。

ボタン内のClickイベントはそれを隠し、コンボボックスを表示して、開かれているボックスだけの効果を作ります。次に、コンボボックスでSelectionChangedイベントを発生させて、必要な情報とMouseLeaveイベントをすべて更新します。ユーザーが実際の選択を変更しない場合に備えてです。

私は新しいユーザーコントロールを使用する必要があるとき、私はこのようにそれを設定します。それのすべてが機能するために、もちろん

<local:DynamicSourceComboBox ItemsSource="{Binding RelativeSource={RelativeSource AncestorLevel=1, AncestorType=ListBox}, Path=DataContext.AvailableEmployees}" 
                   DisplayMemberPath="FirstName" 
                   SelectedValue="{Binding FirstName, Mode=TwoWay}"/> 

、私は中にPropertyChangedイベントにフックアップの多くをしなければなりませんしたがって、Projectインスタンスは、変更が行われるたびにAvailableEmployeesのPropertyChangedイベントを発生させることを知っていますが、実際にはこのユーザーコントロール自身の懸念事項ではありません。

これは非常に厄介な解決策です。余分なコードがたくさんありますが、それは実際に私が持っていた問題を思い付くことができる最高の(実際は唯一の)解決策です。

関連する問題