Home > Software design >  Make element with MaxHeight take the whole space in the Grid if it's less than it's MaxHei
Make element with MaxHeight take the whole space in the Grid if it's less than it's MaxHei

Time:10-13

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 the Grid.
  • 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>
  • Related