I have a UserControl
ParentView containing a DataGrid
and a catel:TabControl
, in which each TabItem
is a tiny UserControl
and whose ViewModel is a subclass of a BaseTabViewModel.
This BaseTabViewModel
contains a ObservableCollection
of a custom class, whose properties will generate the columns of the DataGrid
.
Since I have only one DataGrid for all tabs, how can I fill it with the ObservableCollection of selected tab's viewmodel automatically?
Right now, I just unload tabs (LoadTabItems="SingleUnloadOthers"
) and, since every tab's viewmodel regenerate its collection when instanciated, I use InterestedIn and OnViewModelPropertyChanged to get it; yet this unloading behavior isn't completely reliable, and I often find myself with the DataGrid still filled by the previous tab instead of being cleaned when changing tab.
To note, the fact that it gets cleaned isn't a desired behavior, I just have no other choice right now. Having each tab remaining loaded is my goal.
I know inheritance is pretty frowned upon when it comes to viewmodels, but I honestly can't think of a better way to do it.
ParentView
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<catel:TabControl LoadTabItems="SingleUnloadOthers">
<TabItem Header="FIRST">
<local:FirstView/>
</TabItem>
<TabItem Header="SECOND">
<local:SecondView/>
</TabItem>
<TabItem Header="THIRD">
<local:ThirdView/>
</TabItem>
<TabItem Header="FOURTH">
<local:FourthView/>
</TabItem>
</catel:TabControl>
<DataGrid Grid.Row="1" IsReadOnly="True" ItemsSource="{Binding Fields}"/>
</Grid>
ParentViewModel
[InterestedIn(typeof(FirstViewModel))]
[InterestedIn(typeof(SecondViewModel))]
[InterestedIn(typeof(ThirdViewModel))]
[InterestedIn(typeof(FourthViewModel))]
public class ParentViewModel : ViewModelBase
{
public ObservableCollection<Field> Fields
{
get { return GetValue<ObservableCollection<Field>>(FieldsProperty); }
set { SetValue(FieldsProperty, value); }
}
public static readonly PropertyData FieldsProperty = RegisterProperty("Fields", typeof(ObservableCollection<Field>));
protected override void OnViewModelPropertyChanged(IViewModel viewModel, string propertyName)
{
if (propertyName.Equals("Fields"))
{
BaseParserViewModel p = viewModel as BaseParserViewModel;
Fields = p != null ?? p.Fields;
}
base.OnViewModelPropertyChanged(viewModel, propertyName);
}
}
BaseTabViewModel
public abstract class BaseTabViewModel : ViewModelBase
{
protected BaseParserViewModel()
{
Fields = new ObservableCollection<Field>();
}
public ObservableCollection<Field> Fields
{
get { return GetValue<ObservableCollection<Field>>(FieldsProperty); }
set { SetValue(FieldsProperty, value); }
public static readonly PropertyData FieldsProperty = RegisterProperty("Fields", typeof(ObservableCollection<MappedField>));
}
BaseTabViewModel's childs have nothing of interest, as they don't interact with their parent, except for some [ViewModelToModel]
properties.
If you need to manage state across several view models, I always recommend introducing a service. A view model is something that can live for a short or long amount of time, but the best communication between view models is still services that hold the state (even when the vm's are gone).
Below is my preference of communication:
I know a lot of people fix their architectural issues with 3, but it's a hack, not a solid solution. Just implement a service (that gets injected into all vm's) like this:
Now you can easily handle all the changes without needing the attributes. Then you can even restore state when all view models are gone.