1

This is a first project using WPF and Styles. Development tool is VS2010.

I have the following style definitions:

<!-- Base Style Setting-->
<Style TargetType="{x:Type TabControl}" x:Key="TabControlBase">
    <Setter Property="Height" Value="Auto" />
    <Setter Property="Width"  Value="Auto" />
</Style>

<Style TargetType="{x:Type TabItem}" x:Key="TabItemBase">
    <Setter Property="Cursor" Value="Hand" />
</Style>

<!-- Sub Style Setting-->
<Style TargetType="{x:Type TabItem}" x:Key="LTabEquipment" BasedOn="{StaticResource TabItemBase}">
    <Setter Property="Header">
        <Setter.Value>
            <StackPanel Orientation="Vertical">
                <Image Height="36" Source="pack://application:,,,/Images/gears.png" Width="36" Margin="0,5,0,0" />
                <TextBlock Text="Equipment" Margin="2,0,0,0" VerticalAlignment="Bottom" HorizontalAlignment="Center" />
            </StackPanel>
        </Setter.Value>
    </Setter>
</Style>

<Style TargetType="{x:Type TabItem}" x:Key="LTabHistory" BasedOn="{StaticResource TabItemBase}">
    <Setter Property="Header">
        <Setter.Value>
            <StackPanel Orientation="Vertical">
                <Image Height="36" Source="pack://application:,,,/Images/history.png" Width="36" Margin="0,5,0,0" />
                <TextBlock Text="History" Margin="2,0,0,0" VerticalAlignment="Bottom" HorizontalAlignment="Center" />
            </StackPanel>
        </Setter.Value>
    </Setter>
</Style>

<Style TargetType="{x:Type TabItem}" x:Key="HTabDBase" BasedOn="{StaticResource TabItemBase}">
    <Setter Property="Header">
        <Setter.Value>
            <StackPanel Orientation="Horizontal">
                <Image Height="14" Source="pack://application:,,,/Images/dbase.png" Width="14" Margin="0,0,0,0" HorizontalAlignment="stretch" VerticalAlignment="stretch" />
                <TextBlock Text="grid view" Margin="2,0,0,0" VerticalAlignment="center" HorizontalAlignment="center" />
            </StackPanel>
        </Setter.Value>
    </Setter>    
</Style>

Then in my MainWindow.xaml includes:

    <TabControl Grid.Row="2" Name="tabLeft" TabStripPlacement="Left">
        <TabItem x:Name="tabLeftEquipment" Style="{StaticResource LTabEquipment}">
            <TabControl Style="{StaticResource TabControlBase}" Name="tabTopEquipment" >
                <TabItem x:Name="tabTopEquipmentGridView" Style="{StaticResource HTabDBase}">
                </TabItem>
            </TabControl>
        </TabItem>
        <TabItem x:Name="tabLeftHistory" Style="{StaticResource LTabHistory}">
            <TabControl Height="Auto" Name="tabTopHistory" Width="Auto">
                <TabItem x:Name="tabTopHistoryGridView" Style="{StaticResource HTabDBase}" >
                </TabItem>
            </TabControl>
        </TabItem>

At design time the styles provide the appearance I want, but at runtime I get an exception:

'Set property 'System.Windows.FrameworkElement.Style' threw an exception.'

Which points to line:

                <TabItem x:Name="tabTopEquipmentGridView" Style="{StaticResource HTabDBase}">

Please help with finding the cause of the exception...Thanks!


I found the following in details, but still can't figure which element is causing the problem to correct.

   Message=Specified element is already the logical child of another element. Disconnect it first.
ashur668
  • 43
  • 9

3 Answers3

2

You can fix this by using x:Shared="False" in your styles containing the Images. By default, all resources have x:Shared="True" which means WPF creates only a single instance of each resource. So when any object requests the resource using StaticResource or whatever, that instance is returned instead of creating a new instance. This causes a problem in your case because here:

<TabItem x:Name="tabTopEquipmentGridView" Style="{StaticResource HTabDBase}">
</TabItem>

the style puts an instance of an image inside the header. Therefore the Image becomes a child of the TabItem. Now here:

<TabItem x:Name="tabTopHistoryGridView" Style="{StaticResource HTabDBase}" >
</TabItem>

you set the style again, which means you put the same instance of the image inside the tabItem's visual tree. But the image already has a parent => The exception is thrown because UIElements can have only one parent..

Styles are not meant to set the Content of controls. Styles are supposed to change it's properties. But x:Shared=False fixes it :)

Another, and more elegant way is set the HeaderTemplate instead of the Header. Since styles are supposed to change the properties, it can change the content template right? Yes it can. (Note ContentTemplate not Template which is the template of the control itself. This was what you set in your answers. You are supposed to set the ContentTemplate instead if you only want to "style" the content.) Example:

<Setter Property="HeaderTemplate">
    <Setter.Value>
        <DataTemplate>
            <StackPanel Orientation="Vertical">
                <Image Height="36" Width="36" Margin="0,5,0,0" />
                <TextBlock Text="History" Margin="2,0,0,0" VerticalAlignment="Bottom" HorizontalAlignment="Center" />
            </StackPanel>
        </DataTemplate>
    </Setter.Value>
</Setter>

So you don't need x:Shared anymore. Templates do not suffer the same problem because it is not shared. A new instance of the template is created for each element.

wingerse
  • 3,670
  • 1
  • 29
  • 61
  • Fantastic! Using the HeaderTemplate worked great and you explaination ofx:Shared was excellent too. Thank you very much! – ashur668 Apr 20 '16 at 18:58
1

Didn't grasp the solution first time I read it, but found the final clue in:

WPF TabItem Header Styling

Using the Template property and ControlTemplate, instead of applying the style to the Header property, prevented the problem.

Revised Style Code

<!-- Base Style Setting-->
<Style TargetType="{x:Type TabControl}" x:Key="TabControlBase">
    <Setter Property="Height" Value="Auto" />
    <Setter Property="Width"  Value="Auto" />
</Style>

<Style TargetType="{x:Type TabItem}" x:Key="TabItemBase">
    <Setter Property="Cursor" Value="Hand" />
</Style>

<Style TargetType="{x:Type Image}" x:Key="VTabImage">
    <Setter Property="Height" Value="36" />
    <Setter Property="Width" Value="36" />
    <Setter Property="Margin" Value="0,5,0,0" />
</Style>

<!-- Sub Style Setting-->
<Style TargetType="{x:Type TabItem}" x:Key="LTabEquipment" BasedOn="{StaticResource TabItemBase}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TabItem}">
                <StackPanel Orientation="Vertical">
                    <Image Height="36" Source="pack://application:,,,/Images/gears.png" Width="36" Margin="0,5,0,0" />
                    <TextBlock Text="Equipment" Margin="2,0,0,0" VerticalAlignment="Bottom" HorizontalAlignment="Center" />
                </StackPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<Style TargetType="{x:Type TabItem}" x:Key="LTabHistory" BasedOn="{StaticResource TabItemBase}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TabItem}">
                <StackPanel Orientation="Vertical">
                    <Image Height="36" Source="pack://application:,,,/Images/history.png" Width="36" Margin="0,5,0,0" />
                    <TextBlock Text="History" Margin="2,0,0,0" VerticalAlignment="Bottom" HorizontalAlignment="Center" />
                </StackPanel>
            </ControlTemplate>                    
        </Setter.Value>
    </Setter>
</Style>

<Style TargetType="{x:Type TabItem}" x:Key="HTabDBase" BasedOn="{StaticResource TabItemBase}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TabItem}">  
                <StackPanel Orientation="Horizontal">
                    <Image Height="14" Source="pack://application:,,,/Images/dbase.png" Width="14" Margin="0,0,0,0" HorizontalAlignment="stretch" VerticalAlignment="stretch" />
                    <TextBlock Text="Grid View" Margin="2,0,0,0" VerticalAlignment="center" HorizontalAlignment="center" />
                </StackPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>    
</Style>

This however is not an ideal solution; as using ControlTemplate removes all default style and operation of the original control.

Still looking for better solution.

Community
  • 1
  • 1
ashur668
  • 43
  • 9
0

I found another way to not trigger the exception, but not fond of this either.

The exception seems to occur when HTabDBase style is applied to more than one TabItem within the application.

To prove this I copy/paste two copies and changed their keys to HTabDBase1 and HTabDBase2. I then applied each separately. Other than the key, the style definition is identical.

<Style TargetType="{x:Type TabItem}" x:Key="HTabDBase2" BasedOn="{StaticResource TabItemBase}">
    <Setter Property="Header">
        <Setter.Value>
            <StackPanel Orientation="Horizontal">
                <Image Height="14" Source="pack://application:,,,/Images/dbase.png" Width="14" Margin="0,0,0,0" HorizontalAlignment="stretch" VerticalAlignment="stretch" />
                <TextBlock Text="grid view" Margin="2,0,0,0" VerticalAlignment="center" HorizontalAlignment="center" />
            </StackPanel>
        </Setter.Value>
    </Setter>
</Style>

Then in MainWindow.xaml:

<TabControl Grid.Row="2" Name="tabLeft" TabStripPlacement="Left">
    <TabItem x:Name="tabLeftEquipment" Style="{StaticResource LTabEquipment}">
        <TabControl Style="{StaticResource TabControlBase}" Name="tabTopEquipment" >
            <TabItem x:Name="tabTopEquipmentGridView" Style="{StaticResource HTabDBase1}">
            </TabItem>
        </TabControl>
    </TabItem>
    <TabItem x:Name="tabLeftHistory" Style="{StaticResource LTabHistory}">
        <TabControl Height="Auto" Name="tabTopHistory" Width="Auto">
            <TabItem x:Name="tabTopHistoryGridView" Style="{StaticResource HTabDBase2}" >
            </TabItem>
        </TabControl>
    </TabItem>

But this solution seems to defeat much of the purpose of creating the style to begin with; i.e. I create the style to be able to reuse it in any location, but using it in more than one location causes a run time error.

ashur668
  • 43
  • 9