【问题标题】:One combo box filtering another combo box in a WPF Datagrid一个组合框过滤 WPF Datagrid 中的另一个组合框
【发布时间】:2013-01-23 06:50:43
【问题描述】:

我有一个数据网格,其中有两个组合框列。第一个组合框是 PersonnelTypes 列表。根据选择的 PersonnelType,第二个组合框应填写与所选 PersonnelType 匹配的资源列表

问题是,假设我有两行数据,如果我更改一行的 PersonnelType,数据网格将为每一行中的所有资源设置 itemsource。我只希望它过滤我所在的行,而不是所有行。

这是具有组合框的数据网格部分的 xaml:

                    <DataGridTemplateColumn  Header="Personnel Type" Width="Auto">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <Grid>
                                    <ComboBox Name="cmbPersonnelTypes" FontWeight="Bold" ItemsSource="{Binding ViewModel.PersonnelTypes, RelativeSource={RelativeSource AncestorType=Window}}"  SelectedItem="{Binding PersonnelType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedValuePath="ID" DisplayMemberPath="Description" SelectionChanged="cmbPersonnelTypes_SelectionChanged" />
                                </Grid>
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                    <DataGridTemplateColumn  Header="Name" Width="Auto">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <Grid>
                                    <ComboBox Name="cmbPersonnelName" FontWeight="Bold" ItemsSource="{Binding ViewModel.ResourcesToChooseFrom, RelativeSource={RelativeSource AncestorType=Window},UpdateSourceTrigger=PropertyChanged}"   SelectedItem="{Binding Resource, Mode=TwoWay}" SelectedValuePath="Refno" DisplayMemberPath="Name" />
                                </Grid>
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn> 

这是整个数据网格的 xaml(以防万一您需要查看):

            <DataGrid AutoGenerateColumns="False" CanUserSortColumns="False" CanUserDeleteRows="True" IsReadOnly="True" Background="LightGray" CanUserAddRows="False" Margin="5" SelectedItem="{Binding SelectedLA_JobPersonnel}" ItemsSource="{Binding LA_Personnel}" Grid.ColumnSpan="4" MouseDoubleClick="DataGrid_MouseDoubleClick_1">
                <DataGrid.Resources>
                    <ViewModels:BindingProxy x:Key="proxy" Data="{Binding}" />
                </DataGrid.Resources>
                <DataGrid.Columns>
                    <DataGridTemplateColumn  Header="Personnel Type" Width="Auto">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <Grid>
                                    <ComboBox Name="cmbPersonnelTypes" FontWeight="Bold" ItemsSource="{Binding ViewModel.PersonnelTypes, RelativeSource={RelativeSource AncestorType=Window}}"  SelectedItem="{Binding PersonnelType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedValuePath="ID" DisplayMemberPath="Description" SelectionChanged="cmbPersonnelTypes_SelectionChanged" />
                                </Grid>
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                    <DataGridTemplateColumn  Header="Name" Width="Auto">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <Grid>
                                    <ComboBox Name="cmbPersonnelName" FontWeight="Bold" ItemsSource="{Binding ViewModel.ResourcesToChooseFrom, RelativeSource={RelativeSource AncestorType=Window},UpdateSourceTrigger=PropertyChanged}"   SelectedItem="{Binding Resource, Mode=TwoWay}" SelectedValuePath="Refno" DisplayMemberPath="Name" />
                                </Grid>
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>                            <DataGridTemplateColumn Header="Date Out"  Width="20*" >

                        <DataGridTemplateColumn.CellTemplate>

                            <DataTemplate>

                                <TextBlock Background="LightGray" FontWeight="Bold" Text="{Binding DateOut, Mode=TwoWay, Converter={StaticResource thisNullDateConverter}, StringFormat={}{0:MMM-dd-yyyy hh:ss tt}}">
                                </TextBlock>
                            </DataTemplate>

                        </DataGridTemplateColumn.CellTemplate>

                        <DataGridTemplateColumn.CellEditingTemplate>

                            <DataTemplate>
                                <Toolkit:DateTimePicker Background="LightGray" FontWeight="Bold" Value="{Binding Path=DateOut, Mode=TwoWay, Converter={StaticResource thisNullDateConverter}}" Format="Custom" FormatString="MMM dd yyyy hh:ss tt"></Toolkit:DateTimePicker>

                            </DataTemplate>

                        </DataGridTemplateColumn.CellEditingTemplate>
                    </DataGridTemplateColumn>
                    <DataGridTemplateColumn Header="Date In"  Width="20*">

                        <DataGridTemplateColumn.CellTemplate>

                            <DataTemplate>

                                <TextBlock Background="LightGray" FontWeight="Bold" Text="{Binding DateIn, Mode=TwoWay, Converter={StaticResource thisNullDateConverter}, StringFormat={}{0:MMM-dd-yyyy hh:ss tt}}">
                                </TextBlock>
                            </DataTemplate>

                        </DataGridTemplateColumn.CellTemplate>

                        <DataGridTemplateColumn.CellEditingTemplate>

                            <DataTemplate>
                                <Toolkit:DateTimePicker Background="LightGray" FontWeight="Bold" Value="{Binding Path=DateIn, Mode=TwoWay, Converter={StaticResource thisNullDateConverter}}" Format="Custom" FormatString="MMM dd yyyy hh:ss tt"></Toolkit:DateTimePicker>

                            </DataTemplate>

                        </DataGridTemplateColumn.CellEditingTemplate>
                    </DataGridTemplateColumn>
                </DataGrid.Columns>


            </DataGrid>

这是 xaml (xaml.cs) 的代码:

    public JobEditorViewModel ViewModel
    {
        get { return viewModel; }
    }

private void cmbPersonnelTypes_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        var combobox = sender as ComboBox;
        if (combobox != null)
        {
            var selectedPersonnelType = combobox.SelectedItem as PersonnelType;
            viewModel.SetResourcesToChooseFrom(selectedPersonnelType);
        }
    }

这是 viewModel 中的代码:

public BindingList<PersonnelType> PersonnelTypes
{
    get; set;
}
public JobEditorViewModel(int jobid, string region, DataAccessDataContext db, ServiceUserControlViewModel serviceViewModel)
{

    PersonnelTypes = new BindingList<PersonnelType>(_db.PersonnelTypes.OrderBy(p => p.Head).ThenBy(p => p.Description).ToList());

}


private BindingList<Resource> _resourcesToChooseFrom;


public BindingList<Resource> ResourcesToChooseFrom
{
    get { return _resourcesToChooseFrom; }
    set
    {
        _resourcesToChooseFrom = value;
        NotifyPropertyChanged("ResourcesToChooseFrom");
    }
}

public void SetResourcesToChooseFrom(PersonnelType personnelType)
{
   ResourcesToChooseFrom =
        new BindingList<Resource>(_db.Resources.Where(r => r.Head == personnelType.Head && r.Refno > 2).OrderBy(r=>r.Name).ToList());
}

如果您需要查看更多信息,请告诉我

【问题讨论】:

    标签: wpf xaml mvvm datagrid combobox


    【解决方案1】:

    嗯,在同事的帮助下,我们找到了我需要做的事情。多重绑定就是答案。首先,我们进行了修改,以便将两个组合框放在同一列中,方法是将它们都放在网格中并将网格放在一列中。所以现在两个组合框可以互相看到,因为它们在同一个 DataGridTemplateColumn 中。以前,我们无法让它们互相看到,因为它们在成为两个单独的 DataGridTemplateColumns 时失去了彼此的范围。

    这是我们在 xaml 中所做的:

                                    <DataGridTemplateColumn  Header="Personnel Type-Name" Width="Auto" >
                                        <DataGridTemplateColumn.CellTemplate>
                                            <DataTemplate>
                                                <Grid >
                                                    <Grid.ColumnDefinitions>
                                                        <ColumnDefinition Width="170"/>
                                                        <ColumnDefinition Width="*"/>
                                                    </Grid.ColumnDefinitions>
    
                                                    <Border Grid.Column="0" BorderBrush="Black" BorderThickness="1">
                                                        <TextBlock Text="{Binding PersonnelType.Description}"/>
                                                    </Border>
                                                    <Border Grid.Column="1" BorderBrush="Black" BorderThickness="1">
                                                        <TextBlock  Text="{Binding Resource.Name}"/>
                                                    </Border>
    
                                                </Grid>
                                            </DataTemplate>
                                        </DataGridTemplateColumn.CellTemplate>
    
    
                                        <DataGridTemplateColumn.CellEditingTemplate>
                                            <DataTemplate>
                                                <Grid >
                                                    <Grid.ColumnDefinitions>
                                                        <ColumnDefinition Width="170"/>
                                                        <ColumnDefinition Width="*"/>
                                                    </Grid.ColumnDefinitions>
    
    
                                                    <ComboBox Name="cmbPersonnelTypes" Grid.Column="0" FontWeight="Bold" ItemsSource="{Binding ViewModel.PersonnelTypes, RelativeSource={RelativeSource AncestorType=Window}, UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding PersonnelType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedValuePath="ID" DisplayMemberPath="Description" />
                                                    <ComboBox Name="cmbPersonnelName" Grid.Column="1"  FontWeight="Bold" SelectedItem="{Binding Resource, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedValuePath="Refno" DisplayMemberPath="Name" >
                                                        <ComboBox.ItemsSource>
                                                            <MultiBinding Converter="{StaticResource FilteredPersonnelConverter}">
                                                                <Binding Path="ViewModel.AvailablePersonnel" RelativeSource="{RelativeSource AncestorType=Window}"/>
                                                                <Binding Path="SelectedItem" ElementName="cmbPersonnelTypes"/>
                                                                <Binding Path="ViewModel.SelectedGlobalResourceViewOption" RelativeSource="{RelativeSource AncestorType=Window}"/>
                                                            </MultiBinding>
                                                        </ComboBox.ItemsSource>
                                                    </ComboBox>
    
                                                </Grid>
                                            </DataTemplate>
                                        </DataGridTemplateColumn.CellEditingTemplate>
    
                                    </DataGridTemplateColumn>
    

    您会注意到 MultiBinding 中有一个名为 FilteredPersonnelConverter 的 ValueConverter。这个值转换器为我做了所有的过滤。这是代码:

    public class FilteredPersonnelListValueConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            var allResources = values[0] as IList<Resource>;
            var personnelType = values[1] as PersonnelType;
            var selectedGlobalResourceView = values[2] as ResourceViewOption;
            if (personnelType == null)
                return allResources;
            if(selectedGlobalResourceView.ResourceViewTitle=="Regional")
                return allResources.Where(r => r.Head == personnelType.Head && r.Obsolete == false && r.Location.Region.RegionID.Trim()==SettingsManager.OpsMgrSettings.Region.Trim()).OrderBy(r => r.Name).ToList();
            if (selectedGlobalResourceView.ResourceViewTitle == "Local")
                return allResources.Where(r => r.Head == personnelType.Head && r.Obsolete == false && r.LocnID.Trim() == SettingsManager.OpsMgrSettings.LOCNCODE.Trim()).OrderBy(r => r.Name).ToList();
    
            return allResources.Where(r => r.Head == personnelType.Head &&r.Obsolete==false).OrderBy(r => r.Name).ToList();
    
        }
    
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    

    所以,如果其他人在做这样的事情,看看 Multibinding,它会改变你的生活

    【讨论】:

      【解决方案2】:

      当用户更改视图中的 PersonelType 下拉列表时,ViewModel 应该过滤资源列表(它应该使用正确的信息更新第二个下拉框)。

      真的,您的视图模型只需要设置为响应视图上的更改。归根结底就是这样。

      看这里:

      public JobEditorViewModel(int jobid, string region, DataAccessDataContext db, ServiceUserControlViewModel serviceViewModel)
      {
      
          PersonnelTypes = new BindingList<PersonnelType>(_db.PersonnelTypes.OrderBy(p => p.Head).ThenBy(p => p.Description).ToList());
      
      }
      

      看起来您在构造函数中设置了人员类型,但您没有响应用户更改。您需要在视图模型的 PersonelType 绑定中执行此操作。

      编辑

      我现在看到您正在处理更改的选择。但实际上我认为这应该在视图模型本身中完成。特别是因为您实际上是从视图访问视图模型以进行更改。

      示例

      这是我的虚拟机:

      class ViewModel : INotifyPropertyChanged
      {
          DispatcherTimer timer = new DispatcherTimer();
      
          public ViewModel()
          {
              // Create my observable collection
              this.DateTimes = new ObservableCollection<DateTime>();
      
              // Every second add anothe ritem
              timer.Interval = TimeSpan.FromSeconds(1);
              timer.Tick += new EventHandler(timer_Tick);
              timer.Start();
          }
      
          void timer_Tick(object sender, EventArgs e)
          {
              // This adds to the collection
              this.DateTimes.Add(DateTime.Now);
          }
      
          public ObservableCollection<DateTime> DateTimes { get; private set; }
      
          public event PropertyChangedEventHandler PropertyChanged;
      }
      

      和我的观点:

      <ListBox ItemsSource="{Binding DateTimes}"/> 
      

      请注意,我不会重建集合。我只是设置一次,然后根据需要更改它的大小。

      【讨论】:

      • 用户更改在后面的代码中处理: private void cmbPersonnelTypes_SelectionChanged(object sender, SelectionChangedEventArgs e) { var combobox = sender as ComboBox; if (combobox != null) { var selectedPersonnelType = combobox.SelectedItem as PersonnelType; viewModel.SetResourcesToChooseFrom(selectedPersonnelType); } }
      • 好的,我想我看到了问题所在。您正在尝试监视列表上的更改。为此,您需要一个同时实现 IEnumerable 和 INotifyCollectionChanged 的​​类。尝试将您的 BindingLists 更改为 ObservableCollections 并查看是否不能解决您的问题。你不能只是切换集合,你真的需要使用可观察的集合。
      • 更多信息在这里:stackoverflow.com/questions/5123537/…
      • 请注意,您只创建一次 ObservableCollection,绑定它,然后对集合进行更改。但是,您不会在每次想要进行更改时都用新的 ObservableCollections 替换它。当你替换一个列表时,你打破了绑定。
      • 如果有帮助,我可以写一个简单的例子。
      猜你喜欢
      • 1970-01-01
      • 2012-11-30
      • 2013-04-25
      • 1970-01-01
      • 2013-03-10
      • 1970-01-01
      • 2015-10-04
      • 2015-10-19
      • 2012-07-24
      相关资源
      最近更新 更多