Gerhard Schreurs
Gerhard Schreurs

Reputation: 663

WPF / XAML / MVVM - Set state of checkboxes based on conditions

I am a traditional MVC programmer that just started using MVVM and I do not know how I would program the below scenario the MVVM-way. I probably need multi binding, but can someone please help me and write that code for me? I've spend hours trying to achieve this, but I just don't know how to do it...

Btw, I know how to set the values from my settings file in XAML, but don't know how to write the other logic, EG:

IsEnabled="{Binding Source={x:Static p:Settings.Default}, Path=Pref_QuickProcess}"

This is my scenario:

I have a simple preferences screen with two checkboxes:

□ Quick process (value is set from Settings.Default.Pref_QuickProcess)

□ Upload to youtube (value is set from Settings.Default.Pref_UploadToYoutube)

The following conditions apply:

These are the only options:

Examples

This is my XAML:

<Window x:Class="SchismRecorder.PreferencesWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Title="Preferences" Height="450" Width="800">

<Grid>
    <GroupBox Header="Debug settings" HorizontalAlignment="Left" Height="326" Margin="21,20,0,0" VerticalAlignment="Top" Width="733">
        <StackPanel>
            <CheckBox Content="Quick process" HorizontalAlignment="Left" x:Name="chkQuickProcess" />
            <CheckBox Content="Upload to Youtube" HorizontalAlignment="Left" x:Name="chkUploadToYoutube" />
        </StackPanel>
    </GroupBox>
</Grid>

This is my code behind:

public partial class PreferencesWindow : Window
{
    public PreferencesWindow()
    {
        InitializeComponent();

        chkQuickProcess.IsChecked = Settings.Default.Pref_QuickProcess;
        chkUploadToYoutube.IsChecked = Settings.Default.Pref_UploadToYoutube;

        ConfigureCheckboxes();

        chkQuickProcess.Click -= ChkQuickProcess_Click;
        chkQuickProcess.Click += ChkQuickProcess_Click;
    }

    private void ChkQuickProcess_Click(object sender, RoutedEventArgs e)
    {
        ConfigureCheckboxes();
    }

    void ConfigureCheckboxes()
    {
        if (chkQuickProcess.IsChecked.HasValue)
        {
            var isChecked = chkQuickProcess.IsChecked.Value;

            if (isChecked)
            {
                chkUploadToYoutube.IsChecked = false;
                chkUploadToYoutube.IsEnabled = false;
            }
            else
            {
                chkUploadToYoutube.IsEnabled = true;
            }
        }
    }

    protected override void OnClosing(CancelEventArgs e)
    {
        Settings.Default.Pref_QuickProcess = chkQuickProcess.IsChecked ?? false;
        Settings.Default.Pref_UploadToYoutube = chkUploadToYoutube.IsChecked ?? false;
        Settings.Default.Save();

        base.OnClosing(e);
    }
}

How do I get rid of my code behind, and get the same result in XAML with things like data triggers, converters, multi binding?

Edit: I think I do not necessarily need a viewmodel with setters to implement this logic, and do it with data triggers ? / multi binding ? instead. But maybe that is not possible?

Upvotes: 3

Views: 2297

Answers (1)

Clemens
Clemens

Reputation: 128061

You probably don't need a view model just to set a few properties in the Settings class that have a certain interdependence. The following XAML should do most or perhaps all of what you are describing.

When the first Checkbox is checked, the IsChecked and IsEnabled properties of the second Checkbox are set to false. However, the Settings.Default.Pref_UploadToYoutube property value is not changed. Not sure if this is strictly required.

By default, the second CheckBox's IsChecked property is bound to Pref_UploadToYoutube via a Style Setter. A DataTrigger on the Pref_QuickProcess property replaces the Binding and sets IsChecked and IsEnabled to false.

Also note the new Binding Path syntax for binding to static properties.

<CheckBox Content="Quick process"
          IsChecked="{Binding Path=(p:Settings.Default).Pref_QuickProcess}"/>

<CheckBox Content="Upload to Youtube">
    <CheckBox.Style>
        <Style TargetType="CheckBox">
            <Setter Property="IsChecked"
                    Value="{Binding Path=(p:Settings.Default).Pref_UploadToYoutube}"/>
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=(p:Settings.Default).Pref_QuickProcess}"
                             Value="True">
                    <Setter Property="IsChecked" Value="False"/>
                    <Setter Property="IsEnabled" Value="False"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </CheckBox.Style>
</CheckBox>

You may also simplify the Settings properties binding paths by assigning the Settings.Default instance once to the DataContext of the StackPanel parent of the CheckBoxes:

<StackPanel DataContext="{Binding Path=(p:Settings.Default)}">

    <CheckBox Content="Quick process" IsChecked="{Binding Pref_QuickProcess}"/>

    <CheckBox Content="Upload to Youtube">
        <CheckBox.Style>
            <Style TargetType="CheckBox">
                <Setter Property="IsChecked" Value="{Binding Pref_UploadToYoutube}"/>
                <Style.Triggers>
                    <DataTrigger Binding="{Binding Pref_QuickProcess}" Value="True">
                        <Setter Property="IsChecked" Value="False"/>
                        <Setter Property="IsEnabled" Value="False"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </CheckBox.Style>
    </CheckBox>
</StackPanel>

Upvotes: 3

Related Questions