locked
ThreeState property on Radiobutton in combination with groupname not working as expected? RRS feed

  • Question

  •  Hi,

    I'm trying to accomplish the following:
    I have a custom grid (pivot) with a header.
    Now I want to have three states on the header, namely: None, Ascending and descending.
    The other restriction is that only one column can be selected at a time.

    I am using the radiobutton for this solution with the property ThreeState to True and the groupName property set.
    I expected that when I switch one radiobutton to the other that the IsChecked would go to x:Null when in ThreeState mode.
    But now I can select both radiobuttons when the state is x:Null.

    For this purpose I have created a custom control with the code as shown below.
    How can I possibly solve this problem??

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

        protected override void OnToggle()
        {
            
    if (this.IsChecked == true)
            {
                
    this.IsChecked = this.IsThreeState ? null : ((bool?)false);
            }
            else
            {
                
    this.IsChecked = new bool?(this.IsChecked.HasValue);
            }
        }
    }



    <Style TargetType="{x:Type local:ThreeStateRadioButton}" BasedOn="{StaticResource {x:Type ToggleButton}}">
        
    <Setter Property="Template">
            
    <Setter.Value>
                
    <ControlTemplate TargetType="{x:Type ToggleButton}">
                    
    <Border x:Name="Border" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" Background="{TemplateBinding Background}">
                        
    <StackPanel Orientation="Horizontal">
                            <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" RecognizesAccessKey="True"/>
                            
    <TextBlock x:Name="OrderText" />
                        
    </StackPanel>
                    
    </Border>
                
    <ControlTemplate.Triggers>
                    
    <Trigger Property="IsChecked" Value="true">
                            
    <Setter Property="Background" TargetName="Border" Value="Green" />
                             
    <Setter Property="Text" TargetName="OrderText" Value="Asc" />
                    
    </Trigger>
                    
    <Trigger Property="IsChecked" Value="false">
                        
    <Setter Property="Background" TargetName="Border" Value="Red" />
                        
    <Setter Property="Text" TargetName="OrderText" Value="Desc" />
                    
    </Trigger>
                    
    <Trigger Property="IsChecked" Value="{x:Null}">
                        
    <Setter Property="Background" TargetName="Border" Value="Transparent" />
                        <Setter Property="Text" TargetName="OrderText" Value="" />
                    
    </Trigger>
                    
    <Trigger Property="IsEnabled" Value="false">
                        
    <Setter Property="Foreground" Value="#ADADAD"/>
                    
    </Trigger>
                </ControlTemplate.Triggers>
            
    </ControlTemplate>
    </Setter.Value>
    </Setter>
    </Style>


    fds
    Friday, July 11, 2008 12:40 PM

Answers

  • I don't think RadioButtons are very useful here. I suppose it should also be possible to click a header more than once to switch it from Asc to Desc? A RadioButton will never do that, because the only way to uncheck it is to check another RadioButton in the same group. (or did you manage to do this with your control?)

    So I think you'll be better off with 3-state ToggleButtons. You will have to manually do the unselect for the other columns, because they are not automatically mutually exclusive.

    hth,
    Marcel
    • Marked as answer by brass_nl Tuesday, July 15, 2008 8:17 AM
    Friday, July 11, 2008 2:10 PM
  • Based on DutchMarcel's suggestion above, I got a working example of how to enable this type of scenario as the following XAMLPad ready example shows:
    <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:s="clr-namespace:System;assembly=mscorlib">
    <
    Page.Resources>
    <
    x:Array Type="{x:Type s:String}" x:Key="data">
    <
    s:String>Option1</s:String>
    <
    s:String>Option2</s:String>
    <
    s:String>Option3</s:String>
    </
    x:Array>
    <
    Style TargetType="{x:Type ToggleButton}" x:Key="toggleStyle">
    <
    Setter Property="Template">
    <
    Setter.Value>
    <
    ControlTemplate TargetType="{x:Type ToggleButton}">
    <
    Border
    x:Name="Border"
    Width="{TemplateBinding Width}"
    Height="{TemplateBinding Height}"
    Background="{TemplateBinding Background}">
    <
    Grid>
    <
    Grid.ColumnDefinitions>
    <
    ColumnDefinition Width="Auto"/>
    <
    ColumnDefinition/>
    </
    Grid.ColumnDefinitions>
    <
    ContentPresenter
    Content="{TemplateBinding Content}"
    SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
    RecognizesAccessKey="True"
    HorizontalAlignment="Left"/>
    <
    TextBlock x:Name="OrderText" Grid.Column="1" HorizontalAlignment="Left"/>
    </
    Grid>
    </
    Border>
    <
    ControlTemplate.Triggers>
    <
    Trigger Property="IsChecked" Value="True">
    <
    Setter Property="Background" TargetName="Border" Value="Green" />
    <
    Setter Property="Text" TargetName="OrderText" Value="...(ASC)" />
    </
    Trigger>
    <
    Trigger Property="IsChecked" Value="False">
    <
    Setter Property="Background" TargetName="Border" Value="Transparent" />
    <
    Setter Property="Text" TargetName="OrderText" Value="" />
    </
    Trigger>
    <
    Trigger Property="IsChecked" Value="{x:Null}">
    <
    Setter Property="Background" TargetName="Border" Value="Red" />
    <
    Setter Property="Text" TargetName="OrderText" Value="...(DESC)" />
    </
    Trigger>
    <
    Trigger Property="IsEnabled" Value="False">
    <
    Setter Property="Foreground" Value="#ADADAD"/>
    </
    Trigger>
    </
    ControlTemplate.Triggers>
    </
    ControlTemplate>
    </
    Setter.Value>
    </
    Setter>
    </
    Style>
    </
    Page.Resources>
    <
    StackPanel DataContext="{StaticResource data}">
    <
    ListBox
    ItemsSource="{Binding}"
    IsSynchronizedWithCurrentItem="True"
    Width="240"
    Height="60"
    HorizontalAlignment="Left">
    <
    ListBox.Template>
    <
    ControlTemplate TargetType="{x:Type ListBox}">
    <
    Border
    BorderThickness="{TemplateBinding Border.BorderThickness}"
    Padding="1,1,1,1"
    BorderBrush="{TemplateBinding Border.BorderBrush}"
    Background="{TemplateBinding Panel.Background}"
    Name="Bd"
    SnapsToDevicePixels="True">
    <
    ItemsPresenter
    SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
    </
    Border>
    </
    ControlTemplate>
    </
    ListBox.Template>
    <
    ListBox.ItemContainerStyle>
    <
    Style TargetType="{x:Type ListBoxItem}">
    <
    Setter Property="Template">
    <
    Setter.Value>
    <
    ControlTemplate TargetType="{x:Type ListBoxItem}">
    <
    ToggleButton
    IsThreeState="True"
    Style="{StaticResource toggleStyle}"
    IsChecked="{Binding Path=IsSelected, RelativeSource={RelativeSource TemplatedParent}}"
    Content="{TemplateBinding Content}"/>
    </
    ControlTemplate>
    </
    Setter.Value>
    </
    Setter>
    </
    Style>
    </
    ListBox.ItemContainerStyle>
    </
    ListBox>
    </
    StackPanel>
    </
    Page>

    Hope this helps
    • Marked as answer by brass_nl Tuesday, July 15, 2008 8:16 AM
    Tuesday, July 15, 2008 5:27 AM

All replies

  • I don't think RadioButtons are very useful here. I suppose it should also be possible to click a header more than once to switch it from Asc to Desc? A RadioButton will never do that, because the only way to uncheck it is to check another RadioButton in the same group. (or did you manage to do this with your control?)

    So I think you'll be better off with 3-state ToggleButtons. You will have to manually do the unselect for the other columns, because they are not automatically mutually exclusive.

    hth,
    Marcel
    • Marked as answer by brass_nl Tuesday, July 15, 2008 8:17 AM
    Friday, July 11, 2008 2:10 PM
  • Hi Marcel,

    My radio buttons work fine, you can switch between the three states by setting the out-of-the-box "IsThreeState" property to true.
    Then you have true, false and null.
    My radiobuttons are initialized to IsChecked="{x:Null}" and then I can swith between the states.
    I hoped that the default behavior would be that the radiobuttons would go back into the initial state when another radiobutton within the group has changed state. Because it now has three states the groupname does not have affect until you pass all three states.

    My radiobuttons are placed within a controltemplate, so I don't want to hard code the logic for the columns.
    Is there a way to iterate through all radiobuttons within a group from my custom radiobutton control, so I can adjust the default state myself.
    I used reflector to see how this is implemented in the radiobutton, but it uses inernal functionality I can not use.
    Any idea?

    Thx.




    fds
    Friday, July 11, 2008 3:30 PM
  • I think my description was a bit vague, so I've created a XamlPad version to visualize my problem.
    When you paste the code below in XamlPad you wil see a stack of radiobuttons.
    By switching from one to the other it changes from initial state (Gray) to ascending (Green).
    I want the possibility to click the same radiobutton again to change to descending (x:{Null} = Red).
    My custom control (see above) was able to do this, but then I loose the switch behavior of the group.

    I've used a radiobutton, but maybe there is a better solution. I'd like to hear it from you???


    <
    Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >

    <Page.Resources>

    <Style TargetType="{x:Type RadioButton}" BasedOn="{StaticResource {x:Type ToggleButton}}">

    <Setter Property="Template">

    <Setter.Value>

    <ControlTemplate TargetType="{x:Type ToggleButton}">

    <Border x:Name="Border" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" Background="{TemplateBinding Background}">

    <StackPanel Orientation="Horizontal">

    <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" RecognizesAccessKey="True"/>

    <TextBlock x:Name="OrderText" />

    </StackPanel>

    </Border>

    <ControlTemplate.Triggers>

    <Trigger Property="IsChecked" Value="true">

    <Setter Property="Background" TargetName="Border" Value="Green" />

    <Setter Property="Text" TargetName="OrderText" Value="...(ASC)" />

    </Trigger>

    <Trigger Property="IsChecked" Value="false">

    <Setter Property="Background" TargetName="Border" Value="Transparent" />

    <Setter Property="Text" TargetName="OrderText" Value="" />

    </Trigger>

    <Trigger Property="IsChecked" Value="{x:Null}">

    <Setter Property="Background" TargetName="Border" Value="Red" />

    <Setter Property="Text" TargetName="OrderText" Value="...(DESC)" />

    </Trigger>

    <Trigger Property="IsEnabled" Value="false">

    <Setter Property="Foreground" Value="#ADADAD"/>

    </Trigger>

    </ControlTemplate.Triggers>

    </ControlTemplate>

    </Setter.Value>

    </Setter>

    </Style>

    </Page.Resources>

    <Grid>

    <GroupBox>

    <StackPanel Background="Gray">

    <RadioButton IsThreeState="true" IsChecked="false">Radio1</RadioButton>

    <RadioButton IsThreeState="true" IsChecked="false">Radio2</RadioButton>

    <RadioButton IsThreeState="true" IsChecked="false">Radio3</RadioButton>

    <RadioButton IsThreeState="true" IsChecked="false">Radio4</RadioButton>

    <RadioButton IsThreeState="true" IsChecked="false">Radio5</RadioButton>

    </StackPanel>

    </GroupBox>

    </Grid>

    </Page>


    fds
    Saturday, July 12, 2008 8:47 AM
  • Based on DutchMarcel's suggestion above, I got a working example of how to enable this type of scenario as the following XAMLPad ready example shows:
    <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:s="clr-namespace:System;assembly=mscorlib">
    <
    Page.Resources>
    <
    x:Array Type="{x:Type s:String}" x:Key="data">
    <
    s:String>Option1</s:String>
    <
    s:String>Option2</s:String>
    <
    s:String>Option3</s:String>
    </
    x:Array>
    <
    Style TargetType="{x:Type ToggleButton}" x:Key="toggleStyle">
    <
    Setter Property="Template">
    <
    Setter.Value>
    <
    ControlTemplate TargetType="{x:Type ToggleButton}">
    <
    Border
    x:Name="Border"
    Width="{TemplateBinding Width}"
    Height="{TemplateBinding Height}"
    Background="{TemplateBinding Background}">
    <
    Grid>
    <
    Grid.ColumnDefinitions>
    <
    ColumnDefinition Width="Auto"/>
    <
    ColumnDefinition/>
    </
    Grid.ColumnDefinitions>
    <
    ContentPresenter
    Content="{TemplateBinding Content}"
    SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
    RecognizesAccessKey="True"
    HorizontalAlignment="Left"/>
    <
    TextBlock x:Name="OrderText" Grid.Column="1" HorizontalAlignment="Left"/>
    </
    Grid>
    </
    Border>
    <
    ControlTemplate.Triggers>
    <
    Trigger Property="IsChecked" Value="True">
    <
    Setter Property="Background" TargetName="Border" Value="Green" />
    <
    Setter Property="Text" TargetName="OrderText" Value="...(ASC)" />
    </
    Trigger>
    <
    Trigger Property="IsChecked" Value="False">
    <
    Setter Property="Background" TargetName="Border" Value="Transparent" />
    <
    Setter Property="Text" TargetName="OrderText" Value="" />
    </
    Trigger>
    <
    Trigger Property="IsChecked" Value="{x:Null}">
    <
    Setter Property="Background" TargetName="Border" Value="Red" />
    <
    Setter Property="Text" TargetName="OrderText" Value="...(DESC)" />
    </
    Trigger>
    <
    Trigger Property="IsEnabled" Value="False">
    <
    Setter Property="Foreground" Value="#ADADAD"/>
    </
    Trigger>
    </
    ControlTemplate.Triggers>
    </
    ControlTemplate>
    </
    Setter.Value>
    </
    Setter>
    </
    Style>
    </
    Page.Resources>
    <
    StackPanel DataContext="{StaticResource data}">
    <
    ListBox
    ItemsSource="{Binding}"
    IsSynchronizedWithCurrentItem="True"
    Width="240"
    Height="60"
    HorizontalAlignment="Left">
    <
    ListBox.Template>
    <
    ControlTemplate TargetType="{x:Type ListBox}">
    <
    Border
    BorderThickness="{TemplateBinding Border.BorderThickness}"
    Padding="1,1,1,1"
    BorderBrush="{TemplateBinding Border.BorderBrush}"
    Background="{TemplateBinding Panel.Background}"
    Name="Bd"
    SnapsToDevicePixels="True">
    <
    ItemsPresenter
    SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
    </
    Border>
    </
    ControlTemplate>
    </
    ListBox.Template>
    <
    ListBox.ItemContainerStyle>
    <
    Style TargetType="{x:Type ListBoxItem}">
    <
    Setter Property="Template">
    <
    Setter.Value>
    <
    ControlTemplate TargetType="{x:Type ListBoxItem}">
    <
    ToggleButton
    IsThreeState="True"
    Style="{StaticResource toggleStyle}"
    IsChecked="{Binding Path=IsSelected, RelativeSource={RelativeSource TemplatedParent}}"
    Content="{TemplateBinding Content}"/>
    </
    ControlTemplate>
    </
    Setter.Value>
    </
    Setter>
    </
    Style>
    </
    ListBox.ItemContainerStyle>
    </
    ListBox>
    </
    StackPanel>
    </
    Page>

    Hope this helps
    • Marked as answer by brass_nl Tuesday, July 15, 2008 8:16 AM
    Tuesday, July 15, 2008 5:27 AM
  • Hi Marco,

    Thanx!!!!!!!!!
    This helped a lot.


    fds
    Tuesday, July 15, 2008 8:16 AM