Search code examples
c#wpfxamltabcontrolwpf-style

TabControl - ToggleButtons as TabSeletors (XAML-Only Logic)


Currently I have a set of ToggleButtons. I would like to show a different Tab of my TabControl depending on which button is checked. Basically the same bahaviour like when a differnet Tab is selected. Not sure if my needs are nonsense but anyways. I want the SelectedTab to change depending on which button is clicked. Moreover my ToggleButtons are RadioButtons stlyed to Togglebuttons (I only want one to be checked at a time). I want to try to achieve my needs only in XAML (if even possible).

So here's part of my XAML:

<Window.Resources>
    <sys:Int32 x:Key="CurrentTab"></sys:Int32>
    <Style TargetType="RadioButton" BasedOn="{StaticResource {x:Type ToggleButton}}">
        <Style.Triggers>
            <Trigger Property="IsChecked" Value="True">
                <Setter Property="Background" Value="Aqua"></Setter>
            </Trigger>
        </Style.Triggers>
        <Setter Property="Background" Value="Transparent"></Setter>
    </Style>
</Window.Resources>

    <TabControl Grid.Row="1"
                Grid.Column="1"
                Grid.ColumnSpan="2"
                SelectedIndex="{StaticResource CurrentTab}">
        <TabItem Visibility="Collapsed"></TabItem>
        <TabItem Visibility="Collapsed"></TabItem>
    </TabControl>

What I was thinking of would be something like (pseudoCode):

<Setter Target="{StaticResource CurrentTab}" Value="{ButtonsToolTip}></Setter>

Basically is it even possible to assign values to variables in XAML and if it is - how ?

As an example on why and what I try to achieve is something like this GUI:

http://cache.filehippo.com/img/ex/3049__ccleaner1.png


Solution

    1. You cannot change value of a primitive type declared as resource using xaml. But you can use a property of an object to act as your variable. Eg;

      <sys:Int32 x:Key="IntKey">12</sys:Int32>
      

      is non-modifiable using XAML. But, Value property of DictionaryEntry (shown below) is modifiable, despite the fact that like int(IntKey), DEKey is non-modifiable too.

      <coll:DictionaryEntry x:Key="DEKey" Key="TagKey" Value="100"/>
      

    If I try to change integer(IntKey) via binding , it won't allow. Eg; <TextBox Text="{Binding Mode=OneWay,Source={StaticResource IntKey}}"/> , Mode must be OneWay. TwoWay, OneWayToSource values are not allowed.

    But I can write

    <TextBox  Text="{Binding Value,Source={StaticResource DEKey}}"/> 
    

    and any textbox value will be updated in Value of DictionaryEntry(DEKey). Note, two-way binding won't work as DictionaryEntry is not a DependencyObject. But you can now change your variable (Value property) the way you like. But only concern is : changes to Value property will not be reflected back in bounded control.

    1. Yes, you can make use of above information to show Tabs w.r.t. radiobuttons with approach given below. For binding to work properly both ways, we need a DependencyObject and a DependencyProperty, so we use FrameworkElement and its Tag property.

    This Tag property now mimics your Variable in question.

        <Window.Resources>
            <FrameworkElement x:Key="rbTagHolder" Tag="0"/>
        </Window.Resources>
        ...
        <ItemsControl x:Name="RadioButtonList">
            <ItemsControl.ItemTemplate>
               <DataTemplate>
                  <RadioButton Content="{Binding TabName}" Tag="{Binding TagValue}" GroupName="Choice">
                  <i:Interaction.Triggers>
                      <i:EventTrigger EventName="Click">
                        <ei:ChangePropertyAction TargetObject="{DynamicResource rbTagHolder}" PropertyName="Tag" Value="{Binding Tag, RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1, AncestorType={x:Type RadioButton} }}"/>
                      </i:EventTrigger>
                  </i:Interaction.Triggers>
                  </RadioButton>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                   <StackPanel Orientation="Horizontal"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
        ...
        <TabControl x:Name="TabCtrl" SelectedIndex="{Binding Tag, Source={StaticResource rbTagHolder}}"> ... </TabControl>
    

    Code-behind

    RadioButtonList.ItemsSource = new[] { new { TabName = "Tab1", TagValue = "0" }, new { TabName = "Tab2", TagValue = "1" },
                    new { TabName = "Tab3", TagValue = "2" }, new { TabName = "Tab4", TagValue = "3" }};
    

    Just in case you don't know how to use Blend Behaviors.

    A. Include following namespaces :

    xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" 
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
    

    B. Add references to : Microsoft.Expression.Interactions, System.Windows.Interactivity On my system these are found in

    C:\Program Files (x86)\Microsoft SDKs\Expression\Blend.NETFramework\v4.0\Libraries