How to get WPF Grid columns specified by an asterisk for a clip?

I have a grid control that is proportional with a star, for example.

<Grid.ColumnDefinitions> <ColumnDefinition Width="50*" /> <ColumnDefinition Width="100*" /> <ColumnDefinition Width="50*" /> </Grid.ColumnDefinitions> 

However, placing a long String of TextBlock in a grid that overflows will cause out of proportion. eg.

 <TextBlock Text="Foo" Grid.Column="0" /> <TextBlock Text="Some long text here which overflows" Grid.Column="1" /> <TextBlock Text="Foo" Grid.Column="2" /> 

This causes the central column to more than double the other two. How to keep the specified proportions? Can I pin content?

I set TextTrimming="CharacterEllipsis" in TextBlocks but no luck.

Edit

Itโ€™s crucial that the Grid is inside the DataTemplate , insert the following to observe the behavior,

 <!-- FallbackValue is just a quick hack to get some rows to show at design-time --> <ListBox ItemsSource="{Binding Foo, FallbackValue=1234}" HorizontalContentAlignment="Stretch"> <ListBox.ItemTemplate> <DataTemplate> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="50*" /> <ColumnDefinition Width="100*" /> <ColumnDefinition Width="50*" /> </Grid.ColumnDefinitions> <TextBlock Text="Foo" Grid.Column="0" /> <TextBlock Text="Some long text here which overflows" TextTrimming="CharacterEllipsis" Grid.Column="1" /> <TextBlock Text="Foo" Grid.Column="2" /> </Grid> </DataTemplate> </ListBox.ItemTemplate> </ListBox> 

The reason this is important is because I have another Grid as a ListBox sibling that displays โ€œheadersโ€ for the columns shown in the ListBox as follows:

 <Grid> ... Headers and column definitions here </Grid> <ListBox ...> <ListBox.ItemTemplate> <DataTemplate> <Grid> ... Matching column definitions here </Grid> </DateTemplate> </ListBox.ItemTemplate> </ListBox> 

and therefore it is important that the columns match.

I tried to bind ColumnDefinitions inside a DataTemplate with an external Grid ColumnDefinitions , but I can not get the binding link to it.

+5
source share
3 answers

This is one of the most annoying issues with WPF. Since the available space provided by the template grid is infinite, the actual content will take up as much space as it wants.

The easiest way is to set a specific width in the Grid, but this only solves situations in which there is no resizing.

While you want to stretch the size of the ListBox (width, in specific), unfortunately, I think there is no better solution than a custom converter.

Here is my solution:

 <Window.Resources> <local:MyConv x:Key="cv1" /> </Window.Resources> <Grid> <ListBox ItemsSource="{Binding Foo, FallbackValue=1234}" HorizontalContentAlignment="Stretch" > <ListBox.ItemTemplate> <DataTemplate> <Grid Width="{Binding Path=ActualWidth, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListBox}, Converter={StaticResource cv1}}"> <Grid.ColumnDefinitions> <ColumnDefinition Width="50*" /> <ColumnDefinition Width="100*" /> <ColumnDefinition Width="50*" /> </Grid.ColumnDefinitions> <TextBlock Text="Foo" Grid.Column="0" /> <TextBlock Text="Some long text here which overflows" TextTrimming="CharacterEllipsis" Grid.Column="1" /> <TextBlock Text="Foo" Grid.Column="2" /> </Grid> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid> 

And the converter:

 class MyConv : IValueConverter { public object Convert( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture ) { return (double)value - 30.0; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } } 
+5
source

Despite the fact that this is an old post, I add my conclusions, as they may be relevant to other people reading this post. I had a similar problem (my * columns did not share the width evenly, as expected, they just determined based on the content). The main reason here was because I had a ListView with the ItemsSource element associated with the List. A ListView in WPF contains a ScrollViewer, while a ScrollViewer does not have a fixed width. Without a fixed width, the grid cannot correctly determine which width to pass to the column *, and switches to another calibration method.

Solution Now I use an ItemsControl element that does not contain a ScrollViewer, and thus the width is known, allowing the Grid to sort the columns correctly.

For more details on how exactly the Grid handles its size, I suggest you decompile the Grid class and look at the following method:

 protected override Size MeasureOverride(Size constraint) 

This is my MainWindow.xaml from my test application (comment out the ListView to see the difference in behavior):

  <Window x:Class="WPFSO.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:wpfso="clr-namespace:WPFSO" Title="MainWindow" Height="150" Width="525"> <Window.DataContext> <wpfso:SharedSizeScopeViewModel /> </Window.DataContext> <Window.Resources> <DataTemplate DataType="{x:Type wpfso:TestViewModel}"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" x:Name="SecondColumn" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" x:Name="FourthColumn" /> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Text="{Binding Name}" /> <TextBlock Grid.Column="1" Background="LightGray" Text="{Binding Name2}"/> <TextBlock Grid.Column="2" Text="{Binding Name3}"/> <TextBlock Grid.Column="3" Background="Orange" Text="{Binding Name4}"/> <!--<TextBlock Grid.Column="1" Background="Blue" HorizontalAlignment="Stretch" /> <TextBlock Grid.Column="3" Background="Orange" HorizontalAlignment="Stretch" />--> </Grid> </DataTemplate> <DataTemplate x:Key="MainDataTemplate" DataType="wpfso:SharedSizeScopeViewModel" > <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <CheckBox Grid.Row="0" Grid.ColumnSpan="4" HorizontalAlignment="Left" FlowDirection="RightToLeft" Margin="0,0,0,25"> <TextBlock FlowDirection="LeftToRight" Text="Show differences" Style="{StaticResource LabelStyle}" /> </CheckBox> <TextBlock Grid.Row="1" Grid.Column="0" Text="PropertyName" Style="{StaticResource LabelStyle}" /> <TextBlock Grid.Row="1" Grid.Column="1" Text="Previous value" Style="{StaticResource LabelStyle}" /> <TextBlock Grid.Row="1" Grid.Column="3" Text="Current value" Style="{StaticResource LabelStyle}" /> <ListView Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="4" ItemsSource="{Binding Entries}" HorizontalAlignment="Stretch" Margin="0" HorizontalContentAlignment="Stretch"/> </Grid> </DataTemplate> </Window.Resources> <Grid Name="RootGrid"> <ItemsControl ItemsSource="{Binding Entries}" /> <!--<ListView ItemsSource="{Binding Entries}" />--> </Grid> </Window> The ViewModels used during this test: using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.CompilerServices; namespace WPFSO { public class SharedSizeScopeViewModel : INotifyPropertyChanged { public SharedSizeScopeViewModel() { var testEntries = new ObservableCollection<TestViewModel>(); testEntries.Add(new TestViewModel { Name = "Test", Name2 = "Looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong test", Name3 = "Short test", Name4 = "Nothing" }); Entries = testEntries; } private ObservableCollection<TestViewModel> _entries; public ObservableCollection<TestViewModel> Entries { get { return _entries; } set { _entries = value; OnPropertyChanged(); } } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } } 

First view model

 using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.CompilerServices; namespace WPFSO { public class SharedSizeScopeViewModel : INotifyPropertyChanged { public SharedSizeScopeViewModel() { var testEntries = new ObservableCollection<TestViewModel>(); testEntries.Add(new TestViewModel { Name = "Test", Name2 = "Looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong test", Name3 = "Short test", Name4 = "Nothing" }); Entries = testEntries; } private ObservableCollection<TestViewModel> _entries; public ObservableCollection<TestViewModel> Entries { get { return _entries; } set { _entries = value; OnPropertyChanged(); } } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } } 

Second view model

 using System.ComponentModel; using System.Runtime.CompilerServices; namespace WPFSO { public class TestViewModel : INotifyPropertyChanged { private string _name; private string _name2; private string _name3; private string _name4; public string Name { get { return _name; } set { _name = value; OnPropertyChanged(); } } public string Name2 { get { return _name2; } set { _name2 = value; OnPropertyChanged(); } } public string Name3 { get { return _name3; } set { _name3 = value; OnPropertyChanged(); } } public string Name4 { get { return _name4; } set { _name4 = value; OnPropertyChanged(); } } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } } 
+1
source

Set

 TextTrimming="CharacterEllipsis" 

in the text block.

This works for me. As you have determined, the middle column should be twice as large as the other.

0
source

Source: https://habr.com/ru/post/1201407/


All Articles