views:

1324

answers:

1

Hello,

I'm writing an application with WPF and part of it involves managing for the user various files which are used configure custom, in-house devices. I need to be able to manipulate different types of configurations in tabs in the same TabControl, meaning that the content of the TabItems must be dynamically generated. I'd like to do this with ControlTemplates, but I haven't been successful in getting a working template yet. I have a ControlTemplate called "pendantConfigurationTabItemTemplate" defined in my Window resources, and I use the following code to apply the template (which contains a named item I need to access) to the TabItems and add them to their parent TabControl :

<ControlTemplate x:Key="pendantConfigurationTabItemTemplate" TargetType="TabItem">
    <StackPanel Orientation="Vertical">
        <my:PendantConfigurationFileEditor x:Name="configurationEditor"/>
        <StackPanel Style="{StaticResource defaultOkCancelButtonsContainerStyle}">
            <Button Style="{StaticResource defaultOkCancelButtonStyle}"/>
            <Button Style="{StaticResource defaultOkCancelButtonStyle}" Click="OkButton_Click"/>
        </StackPanel>
    </StackPanel>
</ControlTemplate>

Code behind :

TabItem ConfigTab = new TabItem();

switch (ConfigFile.Device)
{
  case DeviceType.PENDANT:
{
  ControlTemplate TabTemplate = Resources["pendantConfigurationTabItemTemplate"] as ControlTemplate;

  ConfigTab.Template = TabTemplate;
  ConfigTab.ApplyTemplate();

  object Editor = TabTemplate.FindName("configurationEditor", ConfigTab);

  PendantConfigurationFileEditor ConfigFileEditor = Editor as PendantConfigurationFileEditor;

  ConfigFileEditor.PendantConfiguration = DeviceConfig;

  break;
}
default:
  /* snipped */
  return;
}

ConfigTab.Header = ConfigFile.ConfigurationName;

this.EditorTabs.Items.Add(ConfigTab);
this.EditorTabs.SelectedIndex = this.EditorTabs.Items.Count - 1;

However, whenever I run the program, no tabs get added to the tab control, instead the tab control (seemingly) gets replaced or covered by the content of the template. Can somebody please help me out with this ?

Effectively, what I want to do is use the WPF templates as TabItem factories

+2  A: 

TabControl.ItemsSource plus DataTemplates is effectively the "templates as factories" solution you are asking for, but it demands a slightly different approach to your current one.

Rather than writing procedural code to create and template TabItems and calling Items.Add, use the ItemsSource property and data binding. This will cause WPF to create a TabItem for each object in the ItemsSource. You can then use ContentTemplateSelector to select appropriate templates for the object displayed on this tab, according to whatever criteria are appropriate (e.g. the Device property) -- though in this case you will be using DataTemplates rather than ControlTemplates.

Your selector will look something like this:

public class DeviceTypeSelector : DataTemplateSelector
{
  public DataTemplate PendantTemplate { get; set; }
  public DataTemplate DefaultTemplate { get; set; }

  public override SelectTemplate(object item, DependencyObject container)
  {
    ConfigFile cf = (ConfigFile)item;
    switch (cf.Device)
    {
      case DeviceType.Pendant: return PendantTemplate;
      default: return DefaultTemplate;
    }
  }
}

and will be instantiated in XAML like this:

<local:DeviceTypeSelector x:Key="dts"
                          PendantTemplate="{StaticResource pt}"
                          DefaultTemplate="{StaticResource dt}" />

(where pt and dt are suitable DataTemplates defined elsewhere in the resources).

Finally, your TabControl will look like this:

<TabControl Name="EditorTabs"
            ContentTemplateSelector="{StaticResource dts}" />

and you set it up as EditorTabs.ItemsSource = myConfigFiles; (or better still let it acquire the ItemsSource in XAML from the DataContext).

You'll also want to set up the headers of the TabItems: to do this, use TabControl.ItemContainerStyle, with a Setter for the Header property. I think this would look something like this:

<TabControl ...>
  <TabControl.ItemContainerStyle>
    <Style TargetType="TabItem">
      <Setter Property="Header" Value="{Binding ConfigurationName}" />
    </Style>
  </TabControl.ItemContainerStyle>
</TabControl>

(You can also inline the ContentTemplateSelector, by the way: I broke it out into a resource mostly so as to show things in smaller chunks.)

itowlson
That's a great answer, I'll give it a shot. However, I thought DataTemplates were more intended for displaying data and ControlTemplates were intended for use as re-usable controls in order to reduce the number of UserControls that need to be created ? I need to be able to edit the objects that are being displayed / databound in the tabs
Alex Marshall
Fear not, DataTemplates are intended for displaying *and editing* data. They can absolutely contain UI elements that update the object being displayed -- just make sure the element binding in the DataTemplate is two-way (this is the default for text boxes, but not for all controls). ControlTemplates are more about changing the UI of controls while leveraging their existing behaviour, e.g. making a Button appear as a standalone graphic without the usual button chrome, or a TabControl as a carousel, rolodex or (paper) notebook.
itowlson
Your suggestion worked, and quite well. Thank you so much, I really appreciate it :)
Alex Marshall