首页 > 解决方案 > 将一个按钮绑定到 3 个不同的 DataGrids,每个 DataGrids 在 TabControl 中

问题描述

我正在开发一个包含TabControl. 在每个TabItems 中都有一个DataGrid绑定到ObservableCollection我的视图模型中的一个。

我需要将Edit Button绑定到DataGrid当前关注的那个(TabItem被聚焦)。如果没有多个“硬”编码为一个DataGrid/TabItem并使用 MVVM 模式的按钮,是否可以相当容易地完成此操作?

所以基本上这个顺序: TabControl-> 选中TabItem-> DataGrid->SelectedItem

带有数据网格和按钮的 Tabcontrol

示例 XAMLcode(基本上没有样式等的真实格式):

<Button Content="Edit"
        Command="{Binding ExecuteEditMessmittelCommand}"
        CommandParameter="{Binding ElementName=Messmittel_DataGrid, Path=SelectedItem}">
</Button>

<TabControl>

   <TabItem Header="Messmittel">
             <DataGrid x:Name="Messmittel_Datagrid"
                       ItemsSource="{Binding MessmittelDisplayCollection}">
                <DataGrid.Columns>
                   <DataGridTextColumn Header="ID"
                        Binding="{Binding Path=Benutzer_ID}"

                   <DataGridTextColumn Header="Seriennummer"
                        Binding="{Binding Path=Seriennummer}"

                   <DataGridTextColumn Header="MessmittelArt"
                        Binding="{Binding Path=Vorname}"
                </DataGrid.Columns>
             </DataGrid>
   </TabItem>

   <TabItem Header="Mechanik">
             <DataGrid x:Name="Mechanik_Datagrid"
                       ItemsSource="{Binding MechanikDisplayCollection}">
                //here is the datagrid content
             </DataGrid>
   </TabItem>

   <TabItem Header="Prüfhilfsmittel">
             <DataGrid x:Name="Pruefhilfsmittel_Datagrid"
                       ItemsSource="{Binding PruefhilfsmittelDisplayCollection}">
                //here is the datagrid content
             </DataGrid>
   </TabItem>

</TabControl>

查看模型(SetProperty仅触发INotifyPropertyChanged并设置值):

public ObservableCollection<MessmittelModel> MessmittelDisplayCollection
{
        get { return DatabaseDisplayModel.MessmittelCollection; }
        set { SetProperty(ref DatabaseDisplayModel.MessmittelCollection, value);}
}

public ObservableCollection<MessmittelModel> MechanikDisplayCollection
{
        get { return DatabaseDisplayModel.MechanischeMessmittelCollection; }
        set { SetProperty(ref DatabaseDisplayModel.MechanischeMessmittelCollection, value); }
}
public ObservableCollection<MessmittelModel> PruefhilfsmittelDisplayCollection
{
        get { return DatabaseDisplayModel.PruefhilfsmittelCollection; }
        set { SetProperty(ref DatabaseDisplayModel.PruefhilfsmittelCollection, value); }
}

在这种情况下,我认为我的视图模型并不重要,但如果您需要更多信息,请告诉我,我会提供。

标签: c#wpfxamlmvvm

解决方案


静态 MVVM 变体

我认为你必须分解你的主视图模型。您的主视图模型包含由TabItems. 这个视图模型很快就会变得臃肿,并且不能很好地分离视图和关注点。相反,您应该为包含 的视图提供一个主视图模型,TabControl并为选项卡控件中的每个唯一视图提供一个单独的视图模型。

在您的示例中,一种视图模型类型就足够了,因为所有三个选项卡都包含相同的控件,并且仅在DataGrid. 此视图模型将公开要由DataGrid当前选择的 和 属性绑定的集合。您可以通过多种方式分配集合,例如通过主视图模型从外部设置它,通过构造函数传递集合或为此传递服务。

public class MessmittelViewModel : BindableBase
{
   private MessmittelModel _selected;
   private ObservableCollection<MessmittelViewModel> _messmittelModels;

   // ...constructor, initialization of properties, other properties.

   public MessmittelModel Selected
   {
      get => _selected;
      set => SetProperty(ref _selected, value);
   }

   public ObservableCollection<MessmittelModel> MessmittelDisplayCollection
   {
      get => _messmittelModels;
      set { SetProperty(ref _messmittelModels, value);
   }
}

在您的主视图模型中,您可以为每个选项卡公开一个视图模型属性。

public class MainViewModel: BindableBase
{
   private MessmittelViewModel _selectedViewModel;
   private MechanischeMessmittel _mechanischeMessmittelViewModel;

   // ...contructor, initialize properties, other code.

   public MessmittelViewModel SelectedViewModel
   {
      get => _selectedViewModel;
      set => SetProperty(ref _selectedViewModel, value);
   }

   public MechanischeMessmittelViewModel
   {
      get => _mechanischeMessmittelViewModel;
      private set => SetProperty(ref _mechanischeMessmittelViewModel, value);
   }
}

然后在您的 XAML 中绑定选项卡SelectedItem的 theTabControlDataContexts。

<TabControl SelectedItem="{Binding SelectedViewModel}">
   <!-- ...other content. -->
   <TabItem Header="Mechanik"
            DataContext={Binding MechanischeMessmittelViewModel}">
      <DataGrid ItemsSource="{Binding MessmittelDisplayCollection}">
         <!-- ...data grid content. -->
      </DataGrid>
   </TabItem>
</TabControl>

现在您可以将命令参数绑定到Selected属性...

<Button Content="Edit"
        Command="{Binding ExecuteEditMessmittelCommand}"
        CommandParameter="{Binding SelectedViewModel.Selected}"/>

...或访问您的选定视图模型MainViewModel并获取其Selected列表项。

var parameter = SelectedViewModel.Selected;

动态 MVVM 变体

当您考虑创建可能包含不同视图的附加选项卡时,公开三个静态属性不是很可扩展,因此我将向您展示如何以更动态的方式执行此操作。假设您已经创建了MessmittelViewModel上述内容和一个FoobarMessmittelViewModel. 像以前一样创建ObservableCollection一个基本类型,例如MessmittelViewModelBase和一个选定的属性。

public class MainViewModel: BindableBase
{
   private MessmittelViewModelBase _selectedViewModel;
   private ObservableCollection<MessmittelViewModelBase> _messmittelModels;

   public MainViewModel()
   {
      // ...other code.

      MessmittelViewModels = new ObservableCollection<MessmittelViewModelBase>();
      MessmittelViewModels.Add(new MessmittelViewModel(DatabaseDisplayModel.MessmittelCollection));
      // ...add view models for the other tabs.
   }

   // ...other code.

   public MessmittelViewModelBase SelectedViewModel
   {
      get => _selectedViewModel;
      set => SetProperty(ref _selectedViewModel, value);
   }

   public ObservableCollection<MessmittelViewModelBase> MessmittelViewModels
   {
      get => _messmittelModels;
      set { SetProperty(ref _messmittelModels, value);
   }
}

将 绑定ItemsSourceMessmittelViewModels集合并为每个具体的视图模型类型创建一个DataTemplate表示其视图的。

<TabControl ItemsSource="{Binding MessmittelViewModels}">
   <TabControl.Resources>
      <DataTemplate DataType="{x:Type MessmittelViewModel}">
         <DataGrid ItemsSource="{Binding MessmittelDisplayCollection}">
            <DataGrid.Columns>
               <DataGridTextColumn Header="ID"
                                   Binding="{Binding Path=Benutzer_ID}"
                <DataGridTextColumn Header="Seriennummer"
                                    Binding="{Binding Path=Seriennummer}"
                <DataGridTextColumn Header="MessmittelArt"
                                    Binding="{Binding Path=Vorname}"
            </DataGrid.Columns>
         </DataGrid>
      </DataTemplate>
      <!-- ...other data templates -->
   </TabControl.Resources>
</TabControl>

就是这样,TabControl现在将根据其类型为视图模型集合中的每个项目创建一个适当的视图。由于现在有不同的类型,您可以通过检查类型在代码中选择命令的参数(甚至可能为此选项卡启用它)。

if (SelectedViewModel is MessmittelViewModel messmittelViewModel)
{
   var parameter = messmittelViewModel.Selected;
}

使用转换器的 MVVM 变体

对于您当前的方法,您还可以使用多值转换器仅在 XAML 中解决问题。但是,这也是一个静态变体。此转换器将根据选项卡控件中的选定索引返回一个绑定值。

public class SelectedIndexBindingConverter : IMultiValueConverter
{
   public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
   {
      return values[(int)values[0] + 1];
   }

   public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
   {
      throw new InvalidOperationException();
   }
}

在 XAML 中,您必须CommandParameter像这样绑定。

<Window.Resources>
      <local:SelectedIndexBindingConverter x:Key="SelectedIndexBindingConverter"/>
</Window.Resources>

<Button Content="Edit"
        Command="{Binding CommandTest}">
   <Button.CommandParameter>
      <MultiBinding Converter="{StaticResource SelectedIndexBindingConverter}">
         <Binding Path="SelectedIndex" ElementName="MyTabControl"/>
         <Binding Path="SelectedItem" ElementName="Messmittel_Datagrid"/>
         <Binding Path="SelectedItem" ElementName="Mechanik_Datagrid"/>
         <Binding Path="SelectedItem" ElementName="Pruefhilfsmittel_Datagrid"/>
      </MultiBinding>
   </Button.CommandParameter>
</Button>

<TabControl x:Name="MyTabControl">
   <!-- ...tab items as before. -->
</TabControl>

您还可以创建一个转换器来遍历可视化树并获取相应 的选定项DataGrid,而不绑定它,但这将假定一个可视结构并且此解决方案更健壮,因为您明确指定了元素。事实上,这个转换器更灵活,因为它允许您将命令参数绑定到具有选项卡项中任何属性的任何控件。

请注意,您也可以通过触发器在 XAML 中实现相同的目的,但我认为这会过多地干扰控件的样式,并且可能更难重用。


推荐阅读