Reputation: 65
I'm trying to build a WPF UserControl that I can reuse throughout my project. It's basically this:
When the window resizes and is too small, I only show the bottom line to save space. So I'd like to have these options:
I'm using three dependency properties to indirectly set the visibility of each line:
Binding directly to the dependency properties works. Indirectly does not.
Now the question is whether I need to use:
I attempted all four options above, but I don't understand it well enough yet to get one working.
I created normal properties that return a calculated value from the three dependency properties. These only use the default value set with DependencyProperty.Register, they don't update or even use the value set in the parent user control.
<StackPanel>
<Border Height="1"
Background="Black"
Margin="0 10 0 0"
Visibility="{Binding ShowTopLine, ElementName=Root, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="{Binding Text, ElementName=Root, Mode=TwoWay, FallbackValue=Heading}"
Margin="0 10 10 0" />
<Border Grid.Column="1"
Height="1"
Margin="0 10 0 0"
Background="Black"
VerticalAlignment="Center"
Visibility="{Binding ShowBottomLine, ElementName=Root, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"/>
</Grid>
</StackPanel>
Relevant part of code-behind, showing one of three dependency properties:
internal partial class Heading
{
public bool ShowBottomLine => ShowLine && (!CanCompress || ShowCompressed);
public static readonly DependencyProperty CanCompressProperty = GetRegisterProperty("CanCompress", typeof(bool), true);
public bool CanCompress
{
get => (bool) GetValue(CanCompressProperty);
set => SetValue(CanCompressProperty, value);
}
// Same for other two properties
public static DependencyProperty GetRegisterProperty(string name, Type type, object defaultValue)
{
return DependencyProperty.Register(name, type, typeof(Heading), new FrameworkPropertyMetadata(defaultValue));
}
}
How I use the heading in another user control:
<local:Heading Text="{Binding HeaderText}"
CanCompress="False"
ShowLine="False"/>
How can I proceed from here? I know ditching the calculated properties would be simple, but that would mean that I need to calculate their state somewhere else. I'd like to do that all in the Heading.
The main problem seems to be that the calculated properties don't get forced to refresh. Is that a fair summary?
Upvotes: 3
Views: 184
Reputation: 28958
You can implement this logic by defining a ControlTemplate
and simple triggers:
<UserControl x:Class="ExampleControl">
<UserControl.Template>
<ControlTemplate TargetType="ExampleControl">
<StackPanel>
<Border x:Name="TopLine"
Background="Black"
Height="1"
Margin="0 10 0 0" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Text, FallbackValue=Heading}"
Margin="0 10 10 0" />
<Border x:Name="BottomLine"
Grid.Column="1"
Height="1"
Margin="0 10 0 0"
Background="Black"
VerticalAlignment="Center" />
</Grid>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="ShowLines" Value="False">
<Setter TargetName="TopLine" Property="Visibility" Value="Collapsed" />
<Setter TargetName="BottomLine" Property="Visibility" Value="Collapsed" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="CanCompress" Value="True" />
<Condition Property="ShowCompressed" Value="True" />
</MultiTrigger.Conditions>
<Setter TargetName="TopLine"
Property="Visibility"
Value="Collapsed" />
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="CanCompress" Value="True" />
<Condition Property="ShowCompressed" Value="False" />
</MultiTrigger.Conditions>
<Setter TargetName="BottomLine"
Property="Visibility"
Value="Collapsed" />
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</UserControl.Template>
</UserControl>
Alternatively (and recommended) use a custom control (extend Control
).
Upvotes: 1