Search code examples
wpfxamlradio-buttoncontroltemplatetabitem

Is there a way to template a RadioButton so it should TabItem styled


I've tried:

<UniformGrid DockPanel.Dock="Top" Columns="2" Rows="1">
  <UniformGrid.Resources>   
    <Style TargetType="RadioButton">
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="RadioButton">
            <TabItem IsSelected="{TemplateBinding IsChecked}">
              <TabItem.Header>
                <ContentPresenter Margin="5" />
              </TabItem.Header>                  
            </TabItem>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>
  </UniformGrid.Resources>
  <RadioButton Content="By company name" />
  <RadioButton Content="By contact name" Grid.Column="1"/>
</UniformGrid>

It actually works except it doesn't select/unselect the appropriate tab.
I would prefer RadioButtons of course, but my customer insist it should be Tabs.


Solution

  • I think the problem is that the TabItem is "eating" the mouseclick so the RadioButton never gets checked/unchecked. You could wrap the TabItem in a Border and set IsHitTestVisible="False". Doing this you'll lose the IsMouseOver trigger etc, so you'll have to re-add them from the Default template

    Try something like this

    <LinearGradientBrush x:Key="TabItemHotBackground" EndPoint="0,1" StartPoint="0,0">
        <GradientStop Color="#EAF6FD" Offset="0.15"/>
        <GradientStop Color="#D9F0FC" Offset=".5"/>
        <GradientStop Color="#BEE6FD" Offset=".5"/>
        <GradientStop Color="#A7D9F5" Offset="1"/>
    </LinearGradientBrush>
    <SolidColorBrush x:Key="TabItemSelectedBackground" Color="#F9F9F9"/>
    <SolidColorBrush x:Key="TabItemHotBorderBrush" Color="#3C7FB1"/>
    
    <Style x:Key="RadioButtonStyle1" TargetType="{x:Type RadioButton}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="RadioButton">
                    <Border BorderBrush="Transparent" BorderThickness="0" Background="Transparent" CornerRadius="0">
                        <TabItem x:Name="tabItem" IsSelected="{TemplateBinding IsChecked}" IsHitTestVisible="False">
                            <TabItem.Header>
                                <ContentPresenter Margin="5"/>
                            </TabItem.Header>
                        </TabItem>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="true">
                            <Setter Property="Background" TargetName="tabItem" Value="{StaticResource TabItemHotBackground}"/>
                        </Trigger>
                        <Trigger Property="IsChecked" Value="true">
                            <Setter Property="Panel.ZIndex" Value="1"/>
                            <Setter Property="Background" TargetName="tabItem" Value="{StaticResource TabItemSelectedBackground}"/>
                        </Trigger>
                        <MultiTrigger>
                            <MultiTrigger.Conditions>
                                <Condition Property="IsChecked" Value="false"/>
                                <Condition Property="IsMouseOver" Value="true"/>
                            </MultiTrigger.Conditions>
                            <Setter Property="BorderBrush" TargetName="tabItem" Value="{StaticResource TabItemHotBorderBrush}"/>
                        </MultiTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    

    Update

    <ControlTemplate TargetType="RadioButton">
        <Border BorderBrush="Transparent" BorderThickness="0" Background="Transparent" CornerRadius="0">
            <TabItem x:Name="tabItem" IsSelected="{TemplateBinding IsChecked}" IsHitTestVisible="False">
                <TabItem.Header>
                    <ContentPresenter Margin="5"/>
                </TabItem.Header>
            </TabItem>
        </Border>
    </ControlTemplate>
    

    Update 2
    You could use an Attached Behavior to raise the MouseDown event on the RadioButton when the TabItem gets clicked. Alternatively you could just set IsChecked="True" instead of raising the click-event

    <ControlTemplate TargetType="RadioButton">
        <TabItem x:Name="tabItem" IsSelected="{TemplateBinding IsChecked}"
                 local:LinkWithRadioButtonBehavior.LinkWithRadioButton="True">
            <TabItem.Header>
                <ContentPresenter Margin="5"/>
            </TabItem.Header>
        </TabItem>
    </ControlTemplate>
    

    LinkWithRadioButtonBehavior

    public static class LinkWithRadioButtonBehavior 
    {
        public static readonly DependencyProperty LinkWithRadioButtonProperty = 
            DependencyProperty.RegisterAttached 
            (
                "LinkWithRadioButton", 
                typeof(bool),
                typeof(LinkWithRadioButtonBehavior),
                new UIPropertyMetadata(false, OnLinkWithRadioButtonPropertyChanged) 
            );
        public static bool GetLinkWithRadioButton(DependencyObject obj) 
        {
            return (bool)obj.GetValue(LinkWithRadioButtonProperty); 
        }
        public static void SetLinkWithRadioButton(DependencyObject obj, bool value) 
        {
            obj.SetValue(LinkWithRadioButtonProperty, value); 
        }
        private static void OnLinkWithRadioButtonPropertyChanged(DependencyObject dpo, 
                                                                 DependencyPropertyChangedEventArgs e) 
        {
            Control control = dpo as Control;
            if (control != null) 
            { 
                if ((bool)e.NewValue == true) 
                {
                    control.PreviewMouseDown += OnMouseDown; 
                } 
                else 
                {
                    control.PreviewMouseDown -= OnMouseDown; 
                } 
            } 
        }
        static void OnMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            Control control = sender as Control;
            MouseButtonEventArgs routedEventArgs = new MouseButtonEventArgs(Mouse.PrimaryDevice, 0, MouseButton.Left);
            routedEventArgs.RoutedEvent = RadioButton.MouseDownEvent;
            routedEventArgs.Source = control;
            RadioButton clickedRadioButton = GetVisualParent<RadioButton>(control);
            clickedRadioButton.RaiseEvent(routedEventArgs);
        }
        public static T GetVisualParent<T>(object childObject) where T : Visual
        {
            DependencyObject child = childObject as DependencyObject;
            while ((child != null) && !(child is T))
            {
                child = VisualTreeHelper.GetParent(child);
            }
            return child as T;
        }
    }