6

I'm trying to write a program that uses tabs to hold different usercontrols. What I currently want to happen is the user clicks a find button, a new tab is created, and a find screen appears inside it. Using the find screen the user can select clients and these then open in their own new tabs allowing the user to edit them. So if the user went in and selected three clients, the screen would have four tabs, one for the find screen and three client tabs. It will also need to close the tab when the user clicks an exit button on the usercontrol in that tab.

My problem is that I'm not sure how to set this up in my program. I've created a TabControl and bound the ItemsSource to a collection of viewmodels (that I can add to whenever a user adds a new screen). I can use a DataTemplateSelector to select the DataTemplate that contains the right view, but I don't know how to set the resource of the view to my viewmodel.

I'm doing this in WPF and I'm currently using Bxf to put my viewmodels into the views, and this normally works but I'm unsure how it fits in with the TabControl.

I'm trying to stick to MVVM so having a list of views in my viewmodel is out.

Has anyone done something similar to this before?

MadmanDan
  • 91
  • 1
  • 6

2 Answers2

19

I would make my main ViewModel look like this:

  • ObservableCollection<ViewModelBase> OpenTabs
  • ICommand AddTabCommand
  • ICommand CloseTabCommand

In the Constructor, a new SearchViewModel is created and added to OpenTabs, and it's Search method gets hooks up to some method in the MainViewModel

The method in the MainViewModel that handles the Search command would create a new CustomerViewModel with the specified customer, set it's CloseCommand, and then add it to the OpenTabs

var vm = new CustomerViewModel(customer);
vm.CloseCommand = this.CloseTabCommand;
OpenTabs.Add(vm);

You could also use an event system such as PRISM's EventAggregator or Galasoft's Messenger to pass around AddTab/CloseTab events instead of hooking up the commands from the MainViewModel

And of course, you'd use DataTemplates to define how each OpenTab object would get displayed in the View

<TabControl ItemsSource="{Binding OpenTabs}">
    <TabControl.Resources>
        <DataTemplate DataType="{x:Type local:SearchViewModel}">
            <local:SearchView />
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:CustomerViewModel}">
            <local:CustomerView />
        </DataTemplate>
    </TabControl.Resources>
</TabControl>
Rachel
  • 130,264
  • 66
  • 304
  • 490
  • I'm using this method to display different views into my TabControl which works great (thanks!). I now however wished to ensure the TabControl draws all tabs at the same size (ie. the largest width/height of all the tabs it will render). I've found a couple of way of standardizing the size, but I would need to override the ContentTemplate of the tab items, which in turn removes the automatic rendering of the view. Is there a way to have the ContentTemplate content bind to the desired view, while modifying other properties too? – Zepee Jul 07 '15 at 10:37
  • @Zepee It would probably be best to open a new question on that subject. A TabControl only loads items that are visible, so non-visible tabs are not loaded and the size not known immediately. You'd probably have to write something custom to determine the size of the other tabs at first load :) – Rachel Jul 07 '15 at 12:12
  • Thanks Rachel. I did open one an hour ago http://stackoverflow.com/questions/31267946/wpf-tabcontrol-take-size-of-largest-tab , let's see if it gets some traction :) – Zepee Jul 07 '15 at 13:03
0

I've just answered my own question.

The tabitems that get created dynamically are set up with a datacontext of the individual item from the tabcontrols itemsource property, in this case one of my viewmodels.

The datatemplate I used correctly picks up the correct view for the viewmodel type and displays this.

However my view set the datacontext of the grid on the view to my resource and so nothing was showing up. I've changed this to use the datacontext instead of the resource and now everything is working.

So my main problem was having my views run off of resources and not the datacontext. I still would prefer to use resources but as datacontext works I'm going to have to go with that.

MadmanDan
  • 91
  • 1
  • 6