0
votes

1 - I have the TabControl. Its items source is collection of Tabs with different types. I need to have a different XAML for each type. How to form TabItem header and content depends on ViewModel type?
2 - What is the best solution to encapsulate the XAML for each type of ViewModel? Should I have one UserControl for each type or there is a better solution?

HumanTabViewModel and InvaderTabViewModel are children of BaseViewModel class.

<TabControl ItemsSource="{Binding Tabs}">
</TabControl>

class PanelViewModel : BaseViewModel
{
    private readonly ObservableCollection<BaseViewModel> _tabs = new ObservableCollection<BaseViewModel>();

    public ObservableCollection<BaseViewModel> Tabs
    {
        get { return _tabs; }
    }

    private void InitTabs()
    {
        // Fill Tabs collection with some logic

        var tab1 = new HumanTabViewModel ();
        _tabs.Add(tab1);

        var tab2 = new InvaderTabViewModel ();
        _tabs.Add(tab2);
    }
}
1

1 Answers

1
votes

With the use of DataTemplates you can define a different looks for your types :

A DataTemplate is used to give a logical entity (.cs) a visual representation , once you assign your logical object (in your case invader/human vm's) as a Content , the framework will traverse up the logical tree looking for a DataTemplate for your type.

if it does not find any , it would just show the "ToString()" of your type.

In your case you have 2 Contents the TabItem.Content , and Header where can be assigned a DataTemplate via HeaderTemplate.

HumanView and InvaderView are UserControls.

CS :

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = this;
        Items.Add(new HumanViewModel());
        Items.Add(new InvaderViewModel());
    }

    private ObservableCollection<BaseViewModel> items;
    public ObservableCollection<BaseViewModel> Items
    {
        get
        {
            if (items == null)
                items = new ObservableCollection<BaseViewModel>();
            return items;
        }
    }
}

public class BaseViewModel : INotifyPropertyChanged
{
    public virtual string Header
    {
        get { return "BaseViewModel"; }
    }


    protected void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    public event PropertyChangedEventHandler PropertyChanged = delegate { };
}

public class HumanViewModel : BaseViewModel
{

    public override string Header
    {
        get
        {
            return "HumanViewModel";
        }
    }
}

public class InvaderViewModel : BaseViewModel
{

    public override string Header
    {
        get
        {
            return "InvaderViewModel";
        }
    }
}

XAML :

<Window>
   <Window.Resources>
      <DataTemplate DataType="{x:Type local:HumanViewModel}">
          <local:HumanView />
      </DataTemplate>
      <DataTemplate DataType="{x:Type local:InvaderViewModel}">
          <local:InvaderView />
      </DataTemplate>

       <Style TargetType="TabItem">
          <Setter Property="HeaderTemplate">
              <Setter.Value>
                  <DataTemplate>
                      <TextBlock Text="{Binding Header,Mode=OneWay}" FontSize="18" FontWeight="Bold" Foreground="DarkBlue" Width="Auto"/>
                  </DataTemplate>
              </Setter.Value>
          </Setter>
      </Style>

  </Window.Resources>


  <Grid>
      <TabControl ItemsSource="{Binding Items, Mode=OneWay}" />    
  </Grid>

</Window>