0
votes

I want to create a group of togglebuttons that zero or one toggle button checked at a time.

If i use a radiobutton with togglebutton style then atleast one togglebutton will be checke at a specifiec time and I want to allow the option that no togglebutton will be checked.

I created the following style to a togglebutton and the following togglebuttons but for some reason the togglebutton doesn't change the ischecked property when invoked.

<Window x:Class="ClearMvvmDeltaItem.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Model="clr-namespace:ClearMvvmDeltaItem.Model" Title="MainWindow"
    Height="300"
    Width="300"
    DataContext="{Binding Main, Source={StaticResource Locator}}">
<Window.Resources>
    <Model:NotConverter x:Key="NotConverter" />
    <Style TargetType="{x:Type CheckBox}">
        <Style.Triggers>
            <DataTrigger Value="False">
                <DataTrigger.Binding>
                    <MultiBinding Converter="{StaticResource NotConverter}" UpdateSourceTrigger="PropertyChanged">
                        <Binding RelativeSource="{RelativeSource Mode=Self}" Path="Name"></Binding>
                        <Binding Path="CurrentCheckedItem"></Binding>
                    </MultiBinding>
                </DataTrigger.Binding>
                <DataTrigger.Setters>
                    <Setter Property="Background" Value="Red"></Setter>
                    <Setter Property="IsChecked" Value="False"></Setter>
                </DataTrigger.Setters>
            </DataTrigger>
        </Style.Triggers>
    </Style>
</Window.Resources>
<Grid x:Name="LayoutRoot">
    <Grid.RowDefinitions>
        <RowDefinition></RowDefinition>
        <RowDefinition></RowDefinition>
        <RowDefinition></RowDefinition>
    </Grid.RowDefinitions>
    <CheckBox x:Name="first"  Content="First"
                  Command="{Binding FirstCheckedChanged}"  CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Name}" 
                  IsChecked="{Binding IsFirstChecked }" 
                  >

    </CheckBox>
    <CheckBox x:Name="second" Command="{Binding SecondCheckedChanged}"  CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Name}" 
                  IsChecked="{Binding IsSecondChecked}"
                  Content="Second" 
                  Grid.Row="1"></CheckBox>
        <CheckBox x:Name="third" Content="Third" Grid.Row="2"
                  Command="{Binding ThirdCheckedChanged}"  CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Name}" 
                  IsChecked="{Binding IsThirdChecked}"
                  ></CheckBox>
</Grid>

Viewmodel command code:

FirstCheckedChanged = new RelayCommand<string>(newName =>
        {
            if (_isFirstChecked)
                CurrentCheckedItem = newName;
        });
        SecondCheckedChanged = new RelayCommand<string>(newName =>
        {
            if (_isSecondChecked)
                CurrentCheckedItem = newName;
        });
        ThirdCheckedChanged = new RelayCommand<string>(newName =>
        {
            if (_isThirdChecked)
                CurrentCheckedItem = newName;
        });

The datatrigger is invoked because i can see that the background color of the relevant togglebuttons changed to red but the ischecked property isn't affected at all.

Thanks...

3

3 Answers

2
votes

Personally I would use a ListBox and style it so the items are ToggleButtons

<Style x:Key="ToggleButtonListBoxStyle" TargetType="{x:Type ListBox}">
    <Setter Property="BorderBrush" Value="Transparent"/>
    <Setter Property="KeyboardNavigation.DirectionalNavigation" Value="Cycle" />
    <Setter Property="ItemContainerStyle">
        <Setter.Value>
            <Style TargetType="{x:Type ListBoxItem}" >
                <Setter Property="Margin" Value="2, 2, 2, 0" />
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate>
                            <Border Background="Transparent">
                                <ToggleButton Content="{TemplateBinding ContentPresenter.Content}"  
                                    IsChecked="{Binding Path=IsSelected,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}"/>
                            </Border>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </Setter.Value>
    </Setter>
</Style>

<ListBox ItemsSource="{Binding MyOptions}"
         SelectedItem="{Binding CurrentCheckedItem}"
         Style="{StaticResource ToggleButtonListBoxStyle}"/>

This will only allow one button to be toggled on at a time, and you can make CurrentCheckedItem equal to null by unchecking the selected ToggleButton.

It would also clean up your code a bit because you wouldn't need to track 3 separate RelayCommands to track when the selection changes.

As a side note, your original code isn't working because a property set in the <Tag> of a control takes precedence over a property that is set in a DataTrigger. To make it work, you'd have to set the default value (the binding) as a setter in the <Style>. You can learn more about Dependency Property Precedence here.

1
votes

Programming strict MVVM is not always possible.. nothing wrong with a little workaround here and there. This is how I solved it (without using radiobuttons or a listbox). This is called from a shared togglebutton click event, passing in the button name:

    private void ToggleToolBarButtons(string button)
    {
        foreach (Object o in this.stkToolBar.Children)
        {
            if (o.GetType() == typeof(ToggleButton))
            {
                ToggleButton t = o as ToggleButton;
                if (t.Name != button)
                    t.IsChecked = false;
                else
                    t.IsChecked = true;
            }
        }
    }
0
votes

I believe that if you apply a specific value to the IsChecked property of your checkbox in its definition (which you have done through that binding), then the value you try to apply through your style is ignored.

Given that your checkboxes are all bound to view model properties, it would probably be easiest to update them simply by updating those view model properties rather than using a trigger. Thus setting FirstCheckboxChecked property to true, would also update SecondCheckboxChanged and ThirdCheckboxChanged properties to false, and that would be passed through to the checkboxes themselves.