I can't be sure exactly what you're trying to do without looking at your code in-depth, but I think I have a vague idea of your scenario. I have constructed an example for you, illustrating something similar to this. Rather than build a new control, I have placed all of the code in a single Window
, for ease of demonstration. For starters, let's look at the XAML for the window:
<Window x:Class="TestWpfApplication.DataBoundFlags"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestWpfApplication"
Title="DataBoundFlags" Height="300" Width="300"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListBox ItemsSource="{Binding AvailableOptions}" Grid.Row="0">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding}" CommandParameter="{Binding}"
Command="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Window}}, Path=SelectCommand}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBlock Text="{Binding SelectedOptions}" Grid.Row="1"/>
</Grid>
The window's DataContext
is set to its own code-behind, so I can bind to properties there. I have a handful of properties- AvailableOptions
is all the options you can choose from. SelectedOptions
are the options that the user has currently selected. SelectCommand
is a RelayCommand
that is used to either add a flag to the SelectedOptions
or remove one.
The rest of the XAML should be very straightforward. The ListBox
is bound to all of the available options, and each option is represented as a single CheckBox
. Pay careful attention to the CommandParameter
, which is bound to the option item itself. Now let's take a look at the code-behind, where the magic happens:
[Flags()]
public enum Options
{
Plain = 0,
Ketchup = 1,
Mustard = 2,
Mayo = 4,
HotSauce = 8
}
public partial class DataBoundFlags : Window
{
public static readonly DependencyProperty SelectedOptionsProperty =
DependencyProperty.Register("SelectedOptions", typeof(Options), typeof(DataBoundFlags));
public Options SelectedOptions
{
get { return (Options)GetValue(SelectedOptionsProperty); }
set { SetValue(SelectedOptionsProperty, value); }
}
public List<Options> AvailableOptions
{
get
{
return new List<Options>()
{
Options.Ketchup,
Options.Mustard,
Options.Mayo,
Options.HotSauce
};
}
}
public ICommand SelectCommand
{
get;
private set;
}
/// <summary>
/// If the option is selected, unselect it.
/// Otherwise, select it.
/// </summary>
private void OnSelect(Options option)
{
if ((SelectedOptions & option) == option)
SelectedOptions = SelectedOptions & ~option;
else
SelectedOptions |= option;
}
public DataBoundFlags()
{
SelectCommand = new RelayCommand((o) => OnSelect((Options)o));
InitializeComponent();
}
}
Beginning from the top, we have the enum declaration, followed by the SelectedOptions
dependency property, and the AvailableOptions
property (which can be a standard CLR property since it will never change). We then have our command, and the handler which will be executed for the command (whenever an option is checked or unchecked). First notice how the command is wired up- we create a new RelayCommand
and tell it to run OnSelect
, passing in the command parameter. Remember this is the same command parameter that was bound in the XAML- that means it is the current option being checked or unchecked. We compare that option to the SelectedOptions
using bitwise operators. If the option exists, that means we are unchecking it and we need to clear it off using a bitwise AND. If it doesn't exist, we add it to selected using a bitwise OR.
When that happens, the SelectedOptions
dependency property is automatically updated, which updates the TextBlock
binding in the XAML. Here is the final result: