I'm a WPF newbie, so pardon me in advance if this is a dumb question. I've got syntax for enabling a GroupBox if a checkbox is checked, which works fine:
IsEnabled="{Binding ElementName=cbIsDeceased, Path=IsChecked}"
But what I need is to flip the polarity. I need IsEnabled to be true when the checkbox is NOT checked and vice versa. Is there a declarative way to get that?
T开发者_如何学Gohanks.
You have to add a converter to invert the boolean value. In XAML, define the resource for the converter and add it to the binding:
IsEnabled="{Binding ElementName=cbIsDeceased, Path=IsChecked, Converter={StaticResource InverseBooleanConverter}"
And to spare you some time, I give you my version of the converter, which is extremely simple :)
/// <summary>
/// Converts a boolean to its opposite value
/// </summary>
[ValueConversion(typeof(bool), typeof(bool))]
public class InverseBooleanConverter: IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (targetType != typeof(bool))
throw new InvalidOperationException("The target must be a boolean");
return !(bool)value;
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
#endregion
}
To achieve this entirely in XAML (i.e., no converter), you can use keframe-based animations that are triggered by the CheckBox's Checked and Unchecked events. Note that this is not a Binding and nothing is "monitoring" the state of the CheckBox to determine whether or not the GroupBox should be enabled. This is why (as you'll see in the example below) the initial states of the GroupBox and the CheckBox must be set explicitly in order for the animation to work properly.
Here is a working example:
<Window ...>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<GroupBox
x:Name="targetGroupBox"
Grid.Row="0"
Header="My GroupBox"
IsEnabled="False"
>
<Button Content="Hello World" />
</GroupBox>
<CheckBox
Grid.Row="1"
Content="Toggle with animation"
IsChecked="True"
>
<CheckBox.Triggers>
<EventTrigger RoutedEvent="CheckBox.Checked">
<EventTrigger.Actions>
<StopStoryboard BeginStoryboardName="SetFalseStoryboard" />
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="CheckBox.Unchecked">
<EventTrigger.Actions>
<BeginStoryboard Name="SetFalseStoryboard">
<Storyboard>
<BooleanAnimationUsingKeyFrames
Storyboard.TargetName="targetGroupBox"
Storyboard.TargetProperty="IsEnabled"
AutoReverse="True"
BeginTime="0:0:0"
FillBehavior="HoldEnd"
>
<DiscreteBooleanKeyFrame Value="True" KeyTime="0:0:0" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</CheckBox.Triggers>
</CheckBox>
</Grid>
</Window>
When this view is displayed, the Button within the parent GroupBox is only enabled when the "animated" CheckBox is unchecked. Because this is not a binding, there may be performance issues to take into consideration, i.e., a binding converter being executed repeatedly may perform better than a keyframe-based animation being "played" repeatedly; I have not done any performance testing to verify.
I've employed animations like this sparingly in the past as I hate writing and referencing converters for functionality that I feel should be built into WPF out-of-the-box. However, most of the time, a converter is the best choice, this example is just to show that in some scenarios keyframe-based animations can be used to achieve the desired result entirely in XAML.
精彩评论