Reputation: 41
I have a WPF application with an expander where I want the IsExpanded property to change based on an enum value in my view model i.e. I want the expander to open or close based on this enum property. I am using data triggers in the xaml to update the IsExpanded property based on the value of my enum backing property.
<Expander Header="Information and Procedures"
BorderThickness="0"
Margin="5">
<Expander.Style>
<Style TargetType="{x:Type Expander}" BasedOn="{StaticResource MetroExpander}">
<Style.Triggers>
<DataTrigger Binding="{Binding DataContext.Item.Match, RelativeSource={RelativeSource AncestorType={x:Type UserControl}, Mode=FindAncestor}, Mode=TwoWay}"
Value="{x:Static enum:Match.Multiple}">
<Setter Property="IsExpanded" Value="False"/>
</DataTrigger>
<DataTrigger Binding="{Binding DataContext.Item.Match, RelativeSource={RelativeSource AncestorType={x:Type UserControl}, Mode=FindAncestor}, Mode=TwoWay}"
Value="{x:Static enum:Match.None}">
<Setter Property="IsExpanded" Value="True"/>
</DataTrigger>
<DataTrigger Binding="{Binding DataContext.Item.Match, RelativeSource={RelativeSource AncestorType={x:Type UserControl}, Mode=FindAncestor}, Mode=TwoWay}"
Value="{x:Static enum:Match.Exact}">
<Setter Property="IsExpanded" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Expander.Style>
<WrapPanel Orientation="Horizontal" Background="Transparent">
<view:InformationView Margin="0 0 20 0"/>
<view:ProceduresView Margin="0 0 0 0" />
</WrapPanel>
</Expander>
This seems to work until we manually expand the expander. After this it seems as if the data triggers are now ignored and the manually set IsExpanded property will not change until the user manually opens or closes the expander again.
I want the user to be able to manually expand or close it, but I want the data triggers to take priority over the user input.
Note: I don't want the value of Match to be changed by the IsExpanded property. Match is updated separately, but its value affects IsExpanded
Upvotes: 3
Views: 713
Reputation: 458
The view model should not depend on the view. That is, you should not add an IsExpanded property as suggested by Scroog's answer.
I resolved a very similar problem by a Behaviour class that changes IsExpanded depending on the IsEnabled property of the Expander. So this implemented in view only. The following class is just an example and may be enhanced by adding a dependency property that binds to any property of the view model that will be used instead of checking IsEnabled.
public class AutoExpandBehaviour
{
public static readonly DependencyProperty AutoExpandProperty =
DependencyProperty.RegisterAttached(
"AutoExpand",
typeof(bool),
typeof(AutoExpandBehaviour),
new UIPropertyMetadata(false, AutoExpandChanged));
public static bool GetAutoExpand(Expander expander)
{
return (bool)expander.GetValue(AutoExpandProperty);
}
public static void SetAutoExpand(Expander expander, bool value)
{
expander.SetValue(AutoExpandProperty, value);
}
static void AutoExpandChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
var expander = depObj as Expander;
if (expander == null)
{
return;
}
if (e.NewValue is bool == false)
return;
if ((bool)e.NewValue)
{
expander.IsEnabledChanged += Item_IsEnabledChanged;
}
else
{
expander.IsEnabledChanged -= Item_IsEnabledChanged;
}
}
private static void Item_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var expander = sender as Expander;
if (expander != null)
{
expander.IsExpanded = (bool)e.NewValue;
}
}
}
And using in XAML as
<Expander IsEnabled="{Binding IsFunctionAvailable}"
local:AutoExpandBehaviour.AutoExpand="True"
... >
...
</Expander>
Upvotes: 0
Reputation: 3579
This is because the binding is on the DataTrigger
, not the IsExpanded
property, so changing IsExpanded
has no effect no the DataTrigger
bound property.
Bind to the IsExpanded
property and use a custom IValueConverter
to translate in both directions between the source enum and the expander bool.
See: https://learn.microsoft.com/en-us/uwp/api/windows.ui.xaml.data.ivalueconverter
As a nice side effect, you'll also end up with shorter, easier to read XAML.
UPDATE
To achieve the desired behaviour, have a boolean property on the viewmodel for the expanded state, that is bound 2-way to the IsExpanded
property of the Expander
and change it in the viewmodel when the Match
property changes. That way both the user and the viewmodel can modify the state of the Expander
.
Something like:
public bool IsExpanded
{
get => _isExpanded;
set
{
_isExpanded = value;
OnPropertyChanged(nameof(IsExpanded));
}
}
public Match Match
{
get => _match;
set
{
_match = value;
switch (value)
{
case Exact:
case None:
IsExpanded = true;
break;
case Multiple:
IsExpanded = false;
break;
}
}
}
Upvotes: 2