WPF CheckBox With Confirmation Message

19 November 2012 - .NET, C#, WPF

During BASTA Austria I have been asked a WPF question. How can I implement a confirmation question that is asked whenever a user checks a CheckBox? Imagine the following dialog:

 CheckBox Confirmation Sample Dialog

Our goal is to display an "Are you sure?" message if the user checks "Delete everything!" or hits the "Delete Everything (in Code)" button. There should not be a message box if the user unchecks the "Delete everything!" check box.

XAML Source (Without Confirmation Message)

Here you see the XAML source with which we start:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"  
        xmlns:local="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <CheckBox Content="Delete everything!" Name="DeleteEverythingCheckbox" IsChecked="{Binding Path=CheckboxChecked}"
                  Margin="5">
        </CheckBox>
        <Button Command="{Binding Path=SetDeleteEverythingCommand}" Margin="5">Delete Everything (in Code)</Button>
        
        <TextBox IsEnabled="{Binding ElementName=DeleteEverythingCheckbox, Path=IsChecked}" Text="Here is some text to edit"
                 Margin="5"/>
    </StackPanel>
</Window>

The code behind file just connects to the view model:

using System.Windows;

namespace WpfApplication1
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = new MainWindowViewModel();
        }
    }
}

And finally here is the view model (I use Prism's NotificationObject class as the base class to get an implementation of INotifyPropertyChanged):

using Microsoft.Practices.Prism.Commands;
using Microsoft.Practices.Prism.ViewModel;
using System.Windows.Input;

namespace WpfApplication1
{
    public class MainWindowViewModel : NotificationObject
    {
        public MainWindowViewModel()
        {
            this.SetDeleteEverythingCommand = new DelegateCommand(() =>
                {
                    this.CheckboxChecked = true;
                },
                () => !this.CheckboxChecked);
        }

        private bool CheckboxCheckedValue = default(bool);
        public bool CheckboxChecked
        {
            get { return this.CheckboxCheckedValue; }
            set
            {
                if (this.CheckboxCheckedValue != value)
                {
                    this.CheckboxCheckedValue = value;
                    this.RaisePropertyChanged(() => this.CheckboxChecked);
                }
            }
        }
       
        public ICommand SetDeleteEverythingCommand { get; private set; }
    }
}

Add a TriggerAction for Showing Confirmation Message

System.Windows.Interactivity defines a class TriggerAction<T> which we can use to provide a mechanism for adding the confirmation message in XAML. The code is quite simple:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interactivity;

namespace WpfApplication1
{
    public class ConfirmInvokeCommandAction : TriggerAction<DependencyObject>
    {
        public static readonly DependencyProperty MessageProperty =
            DependencyProperty.Register("Message", typeof(string), typeof(ConfirmInvokeCommandAction), new PropertyMetadata("Are you sure?"));

        public string Message
        {
            get { return (string)GetValue(MessageProperty); }
            set { SetValue(MessageProperty, value); }
        }

        protected override void Invoke(object parameter)
        {
            var checkBox = this.AssociatedObject as CheckBox;
            if (checkBox != null)
            {
                if (MessageBox.Show(this.Message, "Alert", MessageBoxButton.YesNo) == MessageBoxResult.No)
                {
                    checkBox.IsChecked = false;
                }
            }
        }
    }
}

We use an event trigger (mechanism defined in the Expression SDK) to access our TriggerAction in XAML:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"  
        xmlns:local="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <CheckBox Content="Delete everything!" Name="DeleteEverythingCheckbox" IsChecked="{Binding Path=CheckboxChecked}"
                  Margin="5">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="Checked">
                    <local:ConfirmInvokeCommandAction Message="Are you sure?" />
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </CheckBox>
        <Button Command="{Binding Path=SetDeleteEverythingCommand}" Margin="5">Delete Everything (in Code)</Button>
        
        <TextBox IsEnabled="{Binding ElementName=DeleteEverythingCheckbox, Path=IsChecked}" Text="Here is some text to edit"
                 Margin="5"/>
    </StackPanel>
</Window>

Result

This does the trick:

If you want to try the solution, download the source code.