A Static MVVM Variant
I think you have to decompose your main view model. Your main view model contains lists that are bound by data grids inside of TabItems. This view model gets bloated quickly and does not separate views and concerns well. Instead, you should have a main view model for the view containing the TabControl and a separate view model for each unique view in the tab control.
In your example, one view model type is enough, as all three tabs contain the same controls and display just a list in a DataGrid. This view model would expose the collection to be bound by the DataGrid and a property for the current selection. You could assign the collection in many ways, set it from outside e.g. through the main view model, pass the collection through the constructor or passing in a service for that.
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);
}
}
In your main view model, you could expose a view model property for each tab.
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);
}
}
Then in your XAML bind the SelectedItem of the TabControl and the DataContexts for the tabs.
<TabControl SelectedItem="{Binding SelectedViewModel}">
<!-- ...other content. -->
<TabItem Header="Mechanik"
DataContext={Binding MechanischeMessmittelViewModel}">
<DataGrid ItemsSource="{Binding MessmittelDisplayCollection}">
<!-- ...data grid content. -->
</DataGrid>
</TabItem>
</TabControl>
Now you can either bind the command parameter to the Selected property...
<Button Content="Edit"
Command="{Binding ExecuteEditMessmittelCommand}"
CommandParameter="{Binding SelectedViewModel.Selected}"/>
...or access the selected view model in your MainViewModel and get its Selected list item.
var parameter = SelectedViewModel.Selected;
A Dynamic MVVM Variant
Exposing three static properties is not very extensible when you think about creating additional tabs that may contain different views, so I show you how to do this in a more dynamic fashion. Let's suppose you have created the MessmittelViewModel as above and a FoobarMessmittelViewModel. Create an ObservableCollection of a base type, e.g. MessmittelViewModelBase and a selected property as before.
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);
}
}
Bind the ItemsSource to the MessmittelViewModels collection and create a DataTemplate that represents its view for each concrete view model type.
<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>
That's it, the TabControl will now create an appropriate view for each item in the view models collection based on its type. Since there are different types now, you would choose your paramter for the command (and maybe even if it is enabled for this tab) in code by checking the type.
if (SelectedViewModel is MessmittelViewModel messmittelViewModel)
{
var parameter = messmittelViewModel.Selected;
}
An MVVM Variant Using A Converter
For your current approach, you could also solve the issue in XAML only, using a multi-value converter. However, this is also a static variant. This converter will return a bound value based on the selected index in the tab control.
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();
}
}
In XAML, you have to bind the CommandParameter like this.
<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>
You could also create a converter to walk the visual tree and get the selected item of the corresponding DataGrid, without binding it, but that would assume a visual structure and this solution is more robust, as you specify the elements explicitly. In fact this converter is more flexible in that it allows you to bind the command parameter to any control with any property in a tab item.
Just as a note, you could achieve the same in XAML through triggers, too, but I think that would interfere too much with styling of controls and might be harder to reuse.