I have a WPF page containing controls that have to be enabled and disabled under complex conditions. Rather that creating an IsXXXXEnabled property for each one and try to remember to update them all as conditions change, I want to embed that functionality in XAML to separate it from the business logic in the code behind.
I came up with a ComputeConverter. This idea is so obvious that I'm sure I'm like the 10,000th person to think of it. The idea is to create a MultiValue converter that takes a parameter, substitutes the values, and uses DataTable.Compute to evaluate it, returning the result. Similar to String.Format, I use {n} as place holders. So to multiply two numbers the parameter would be "{}{0} * {1}". The starting {} tells the XAML parser that {0} is not a markup extension. String literals need to be delimited so to concatenate two strings with a hyphen the parameter would be "'{0}' + '-' + '{1}'". This parameter does not need a {} because it does not start with a { character.
In this example I have a legal document and an access mode. A witness is only needed for Affidavits and Wills and can only be entered when the access mode is Editable. At all other times, the witness name text box must be disabled.
Start a new C# Visual Studio WPF Framework project and call it ComputeConverter.
Add a new class called Converters and replace the code with this. All it does is try to substitute each value into the parameter and then evaluate the result. There is no code to look for missing or excess values, etc. If the developer gets the parameter wrong, weird things will happen.
public class ComputeConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
String Expression = System.Convert.ToString(parameter);
throw new NotImplementedException();
}
}
Now we can see the converter being used. Change the contents of MainWindow.xml to look like this. You can see we have defined a style to highlight the IsEnabled state of the text box. We have two combo boxes to allow selection of document type and access mode. The fun part is the parameter of the compute converter.
ConverterParameter="('{0}'='Affidavit' OR '{0}'='Will') AND '{1}'='Editable'">
This will return True if the first parameter is Affidavit or Will AND the second parameter is Editable. This is extremely powerful, although it's a bit slow.
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"
xmlns:local="clr-namespace:ComputeConverter"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="Compute Converter" Height="450" Width="800">
<Window.Resources>
<local:ComputeConverter x:Key="ComputeConverter"/>
<Style TargetType="TextBox">
<Setter Property="Background" Value="LightBlue"/>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="True">
<Setter Property="Background" Value="White"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<ComboBox Grid.Row="0" Grid.Column="1" ItemsSource="{Binding DocumentTypes}" SelectedItem="{Binding SelectedDocumentType}"/>
<ComboBox Grid.Row="1" Grid.Column="1" ItemsSource="{Binding AccessModes}" SelectedItem="{Binding SelectedAccessMode}"/>
<TextBox Grid.Row="3" Grid.Column="1" Text="{Binding WitnessName}">
<TextBox.IsEnabled>
<MultiBinding Converter="{StaticResource ComputeConverter}" ConverterParameter="('{0}'='Affidavit' OR '{0}'='Will') AND '{1}'='Editable'">
<Binding Path="SelectedDocumentType"/>
<Binding Path="SelectedAccessMode"/>
</MultiBinding>
</TextBox.IsEnabled>
</TextBox>
</Grid>
</Window>
Time for MainWindow.xaml.cs, which contains no surprises.
namespace ComputeConverter
public partial class MainWindow : Window, INotifyPropertyChanged
{
public List<String> DocumentTypes { get; set; } = new List<string> { "Affidavit","Contract","Deed","Lien","Will" };
private String _SelectedAccessMode = "Locked";
get { return _SelectedAccessMode; }
private String _SelectedDocumentType = "Deed";
get { return _SelectedDocumentType; }
public String WitnessName { get;set;}
public MainWindow()
InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged;
if (!Object.Equals(storage, value))
storage = value;
if (PropertyChanged != null)
}
}
}
The result is that the text box is only enabled under the required conditions