2

I can not figure out how to get button Razveljavi to stay next to Osnovno and Vstavi. I don't want it to be in toolbar but on right side so it is shown all the time no matter which tab you are on.

There is a picture with what I got and how I want it to look. If anyone could help me that would be great. Im sure its just a small problem but I just started with WPF.

Screenshot of the application

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="200"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="22"/>
        <RowDefinition Height="55"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="25"/>
    </Grid.RowDefinitions>

    <Menu Grid.Row="0" Grid.ColumnSpan="2">
        <MenuItem Header="Datoteka">
            <MenuItem Header="Odpri"/>
            <MenuItem Header="Shrani"/>
            <MenuItem Header="Izhod" Click="izhod"/>
        </MenuItem>
        <MenuItem Header="Uredi">
            
        </MenuItem>
        <MenuItem Header="Orodja">
            
        </MenuItem>
    </Menu>
    <TabControl Grid.Row="1" Grid.ColumnSpan="2">
        <TabItem Header="Osnovno">
            <ToolBarTray>
                <ToolBar>
                    <Button Content="Pisava"/>
                    <Button Content="Barva" Click="spreminjanjeBarve"/>
                </ToolBar>
                <ToolBar>
                    <Button Content="Kopiraj" Click="kopiraj">
                    </Button>
                    <Button Content="Izreži"/>
                    <Button Content="Prilepi" Click="prilepi"/>
                </ToolBar>
                <ToolBar>
                    <Button Content="Razveljavi"/>
                    <Button Content="Ponovi"/>
                </ToolBar>
            </ToolBarTray>
            
        </TabItem>
        <TabItem Header="Vstavi"/>
    </TabControl>
    <RichTextBox Name="box" Grid.Row="2" Background="LightBlue" GotFocus="RichTextBox_GotFocus" LostFocus="RichTextBox_LostFocus">
        <FlowDocument>
            <Paragraph Name="tekst">
                Vpiši tekst
                <Bold>tukaj</Bold> .
            </Paragraph>
        </FlowDocument>
    </RichTextBox>
    <StatusBar Grid.Row="3">
        <TextBlock Name="status"/>
    </StatusBar>
</Grid>
thatguy
  • 21,059
  • 6
  • 30
  • 40
programmer
  • 23
  • 4

2 Answers2

1

That is not as easy as it seems. What you essentially want to do is add controls and functionality to an existing control. In order o add the buttons to the tab strip, you have to create a custom control template, because that defines the appearance and visual states of a control. To add functionality to these buttons you would either have to create custom attached properties or create a custom control.

Personally I think that the cleaner way is to create a custom control for this scenario. To make it flexible, we create an area in the tab strip for additional content of any kind and even allow for defining a data template for it.

To start off, create a class that derives from TabControl and add dependendency properties for the content to be displayed and the optional data template for arbitary content.

public class MyTabControl : TabControl
{
   static MyTabControl()
   {
      DefaultStyleKeyProperty.OverrideMetadata(typeof(MyTabControl), new
         FrameworkPropertyMetadata(typeof(MyTabControl)));
   }

   public static readonly DependencyProperty AdditionalTabStripContentTemplateProperty = DependencyProperty.Register(
      nameof(AdditionalTabStripContentTemplate), typeof(DataTemplate), typeof(MyTabControl), new PropertyMetadata(null));

   public static readonly DependencyProperty AdditionalTabStripContentProperty = DependencyProperty.Register(
      nameof(AdditionalTabStripContent), typeof(object), typeof(MyTabControl), new PropertyMetadata(null));

   public DataTemplate AdditionalTabStripContentTemplate
   {
      get => (DataTemplate)GetValue(AdditionalTabStripContentTemplateProperty);
      set => SetValue(AdditionalTabStripContentTemplateProperty, value);
   }

   public object AdditionalTabStripContent
   {
      get => GetValue(AdditionalTabStripContentProperty);
      set => SetValue(AdditionalTabStripContentProperty, value);
   }
}

Create a style for the new tab control with the additional tab strip content by copying and adapting the default style of the TabControl, which can be extracted using Blend or Visual Studio.

<SolidColorBrush x:Key="TabItem.Selected.Background" Color="#FFFFFF"/>
<SolidColorBrush x:Key="TabItem.Selected.Border" Color="#ACACAC"/>
<Style x:Key="MyTabControlStyle" TargetType="{x:Type local:MyTabControl}">
   <Setter Property="Padding" Value="2"/>
   <Setter Property="HorizontalContentAlignment" Value="Center"/>
   <Setter Property="VerticalContentAlignment" Value="Center"/>
   <Setter Property="Background" Value="{StaticResource TabItem.Selected.Background}"/>
   <Setter Property="BorderBrush" Value="{StaticResource TabItem.Selected.Border}"/>
   <Setter Property="BorderThickness" Value="1"/>
   <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
   <Setter Property="Template">
      <Setter.Value>
         <ControlTemplate TargetType="{x:Type local:MyTabControl}">
            <Grid x:Name="templateRoot" ClipToBounds="true" SnapsToDevicePixels="true" KeyboardNavigation.TabNavigation="Local">
               <Grid.ColumnDefinitions>
                  <ColumnDefinition x:Name="ColumnDefinition0"/>
                  <ColumnDefinition x:Name="ColumnDefinition1" Width="0"/>
               </Grid.ColumnDefinitions>
               <Grid.RowDefinitions>
                  <RowDefinition x:Name="RowDefinition0" Height="Auto"/>
                  <RowDefinition x:Name="RowDefinition1" Height="*"/>
               </Grid.RowDefinitions>
               <StackPanel Orientation="Horizontal">
                  <TabPanel x:Name="headerPanel" Background="Transparent" IsItemsHost="true" Margin="2,2,2,0" Grid.Row="0" KeyboardNavigation.TabIndex="1" Panel.ZIndex="1"/>
                  <ContentControl Content="{TemplateBinding AdditionalTabStripContent}" ContentTemplate="{TemplateBinding AdditionalTabStripContentTemplate}" Height="{Binding Height, ElementName=headerPanel}" Margin="0, 2, 0, 0"/>
               </StackPanel>
               <Border x:Name="contentPanel" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" Grid.Column="0" KeyboardNavigation.DirectionalNavigation="Contained" Grid.Row="1" KeyboardNavigation.TabNavigation="Local" KeyboardNavigation.TabIndex="2">
                  <ContentPresenter x:Name="PART_SelectedContentHost" ContentSource="SelectedContent" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
               </Border>
            </Grid>
            <ControlTemplate.Triggers>
               <Trigger Property="TabStripPlacement" Value="Bottom">
                  <Setter Property="Grid.Row" TargetName="headerPanel" Value="1"/>
                  <Setter Property="Grid.Row" TargetName="contentPanel" Value="0"/>
                  <Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
                  <Setter Property="Height" TargetName="RowDefinition1" Value="Auto"/>
                  <Setter Property="Margin" TargetName="headerPanel" Value="2,0,2,2"/>
               </Trigger>
               <Trigger Property="TabStripPlacement" Value="Left">
                  <Setter Property="Grid.Row" TargetName="headerPanel" Value="0"/>
                  <Setter Property="Grid.Row" TargetName="contentPanel" Value="0"/>
                  <Setter Property="Grid.Column" TargetName="headerPanel" Value="0"/>
                  <Setter Property="Grid.Column" TargetName="contentPanel" Value="1"/>
                  <Setter Property="Width" TargetName="ColumnDefinition0" Value="Auto"/>
                  <Setter Property="Width" TargetName="ColumnDefinition1" Value="*"/>
                  <Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
                  <Setter Property="Height" TargetName="RowDefinition1" Value="0"/>
                  <Setter Property="Margin" TargetName="headerPanel" Value="2,2,0,2"/>
               </Trigger>
               <Trigger Property="TabStripPlacement" Value="Right">
                  <Setter Property="Grid.Row" TargetName="headerPanel" Value="0"/>
                  <Setter Property="Grid.Row" TargetName="contentPanel" Value="0"/>
                  <Setter Property="Grid.Column" TargetName="headerPanel" Value="1"/>
                  <Setter Property="Grid.Column" TargetName="contentPanel" Value="0"/>
                  <Setter Property="Width" TargetName="ColumnDefinition0" Value="*"/>
                  <Setter Property="Width" TargetName="ColumnDefinition1" Value="Auto"/>
                  <Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
                  <Setter Property="Height" TargetName="RowDefinition1" Value="0"/>
                  <Setter Property="Margin" TargetName="headerPanel" Value="0,2,2,2"/>
               </Trigger>
               <Trigger Property="IsEnabled" Value="false">
                  <Setter Property="TextElement.Foreground" TargetName="templateRoot" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
               </Trigger>
            </ControlTemplate.Triggers>
         </ControlTemplate>
      </Setter.Value>
   </Setter>
</Style>

This style is almost the same as the default style. The difference is in the lines below. We use a StackPanel to add a ContentControl to the right of the tab headers panel and bind its content and template to the properties of our custom control. We also bind the Height of the content control to the tab headers panel, so it looks clean.

<StackPanel Orientation="Horizontal">
   <TabPanel x:Name="headerPanel" Background="Transparent" IsItemsHost="true" Margin="2,2,2,0" Grid.Row="0" KeyboardNavigation.TabIndex="1" Panel.ZIndex="1"/>
   <ContentControl Content="{TemplateBinding AdditionalTabStripContent}" ContentTemplate="{TemplateBinding AdditionalTabStripContentTemplate}" Height="{Binding Height, ElementName=headerPanel}" Margin="0, 2, 0, 0"/>
</StackPanel>

To conclude the style, create an implicit style, so it is applied automatically to all MyTabControls in scope.

<Style TargetType="{x:Type local:MyTabControl}" BasedOn="{StaticResource MyTabControlStyle}"/>

Now replace the TabControl in your XAML with this one and add the tab strip content e.g.:

<local:MyTabControl Grid.Row="1" Grid.ColumnSpan="2">
   <local:MyTabControl.AdditionalTabStripContent>
      <StackPanel Orientation="Horizontal">
         <Button Content="Razveljavi"/>
         <Button Content="Ponovi"/>
      </StackPanel>
   </local:MyTabControl.AdditionalTabStripContent>
   <!-- ...your other tab items. -->
</local:MyTabControl>

The cool thing about this is that you can add any content here and even data template it. The result:

Result with StackPanel

The only downside of this template is that tab header collapsing into multiple rows is not possible. If you replace the StackPanel from above with this DockPanel, you get a more responsive version, but it will have the buttons on the right border aligned permanently.

<DockPanel LastChildFill="True">
   <ContentControl DockPanel.Dock="Right" Content="{TemplateBinding AdditionalTabStripContent}" ContentTemplate="{TemplateBinding AdditionalTabStripContentTemplate}" Height="{Binding Height, ElementName=headerPanel}" Margin="0, 2, 0, 0"/>
   <TabPanel DockPanel.Dock="Left" x:Name="headerPanel" Background="Transparent" IsItemsHost="true" Margin="2,2,2,0" Grid.Row="0" KeyboardNavigation.TabIndex="1" Panel.ZIndex="1"/>
</DockPanel>

The result in a normal sized window and a small window that forces a tab header collapse:

Result with DockPanel

Result with DockPanel small

thatguy
  • 21,059
  • 6
  • 30
  • 40
1

I would create an empty fake TabItem with Header which contains nothing but Buttons:

<TabControl>
    <TabItem Header="1 2 3 4 5 6">
        <Border Background="Aqua"/>
    </TabItem>

    <TabItem Header="A B C D E F">
        <Border Background="Bisque"/>
    </TabItem>

    <TabItem>
        <TabItem.Template>
            <ControlTemplate TargetType="TabItem">
                <ContentPresenter Content="{TemplateBinding Header}"/>
            </ControlTemplate>
        </TabItem.Template>

        <TabItem.Header>
            <StackPanel Orientation="Horizontal" Margin="5,0">
                <Button Content="C L I C K" Margin="2" Padding="10,3" />
                <Button Content="H E L L O" Margin="2" Padding="10,3"/>
            </StackPanel>
        </TabItem.Header>

        <Border Background="Chartreuse"/>
    </TabItem>
</TabControl>

Buttons intercept mouse clicks and tab selection is not triggered.

Header buttons

ASh
  • 34,632
  • 9
  • 60
  • 82