I want to accomplish the following:
- Have a badge on the right side of the
Grid
element. - The badge should have a max height of 33px.
- If
Grid
height itself is less than 33px, the badge should take whole available space and be the same size as theGrid
. - If
Grid
height itself is more than 33px, the badge should be pinned to bottom and be 33px tall.
Here is the code that I have at this moment:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<!-- ... -->
<Grid Grid.Column="2">
<Border Width="60"
MaxHeight="33"
CornerRadius="0 0 3 0"
Background="#FFD20B0B">
<TextBlock FontSize="13"
FontWeight="Bold"
VerticalAlignment="Center"
HorizontalAlignment="Center"
FontFamily="Arial"
Foreground="#FFFFFFFF">
<Span>Broken</Span>
</TextBlock>
</Border>
</Grid>
</Grid>
This accomplished all requirements except the last one. Whatever I do, the badge is being pinned to the center if the parent Grid
size is larger than 33px. How can I pin it to bottom in this case?
P.S. If I use VerticalAlignment="Bottom"
, I'm losing the third requirement - badge does not become Grid
's height when Grid
's size is less than 33px (e.g. Grid
size = 20px).
CodePudding user response:
Unfortunately, WPF does not have a built-in XAML syntax for doing comparisons like greater than. What you could do is create a custom value converter for that, e.g. returning true
if a value is greater than a given parmeter and false
otherwise.
public class IsGreaterThanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is double doubleValue) ||
!(parameter is string parameterText) ||
!double.TryParse(parameterText, out var doubleParameter))
return Binding.DoNothing;
return doubleValue > doubleParameter;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return new InvalidOperationException();
}
}
The idea is to bind the ActualHeight
of the parent Grid
in a DataTrigger
that checks if it is greater than a threshold (here 33
) using the converter above. Depending on that, it sets the VerticalAlignment
and the Height
of the Border
. The default value of VerticalAlignment
is Stretch
, which is set if the actual height is less than or equal to 33
(converter returns false
).
<Grid>
<Grid.Resources>
<local:IsGreaterThanConverter x:Key="IsGreaterThanConverter"/>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<!-- ... -->
<Grid Grid.Column="2">
<Border Width="60"
MaxHeight="33"
CornerRadius="0 0 3 0"
Background="#FFD20B0B">
<Border.Style>
<Style TargetType="{x:Type Border}">
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ActualHeight, RelativeSource={RelativeSource AncestorType={x:Type Grid}}, Converter={StaticResource IsGreaterThanConverter}, ConverterParameter=33}" Value="True">
<Setter Property="Height" Value="33"/>
<Setter Property="VerticalAlignment" Value="Bottom"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock FontSize="13"
FontWeight="Bold"
VerticalAlignment="Center"
HorizontalAlignment="Center"
FontFamily="Arial"
Foreground="#FFFFFFFF">
<Span>Broken</Span>
</TextBlock>
</Border>
</Grid>
</Grid>
Do not forget that the converter needs to be instantiated in a resource dictionary in scope, here in the Grid.Resources
. Of course you can extract the style to a resource dictionary, too, if you want.
CodePudding user response:
You can bind the Height of the Border to the ActualHeight of the Grid. That combined with your MaxHeight and VerticalAlignment will give you what you're looking for.
<Grid x:Name="RootGrid" Grid.Column="2">
<Border Width="60"
MaxHeight="33"
CornerRadius="0 0 3 0"
Background="#FFD20B0B"
VerticalAlignment="Bottom"
Height="{Binding ActualHeight, ElementName=RootGrid}">
<!-- ... -->
</Border>
</Grid>