Frage Wie erhalte ich WPF-Grid-Spalten, die mit Stern definiert sind, um Inhalte abzuschneiden?


Ich habe ein Grid-Steuerelement, das unter Verwendung eines Sterns, z.B.

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

Allerdings eine lange Zeit TextBlock In dem Raster, das überläuft, werden die Proportionen gestört. z.B.

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

Dies bewirkt, dass die zentrale Spalte mehr als doppelt so groß ist wie die anderen beiden. Wie pflege ich die angegebenen Proportionen? Ist es möglich, den Inhalt zu schneiden?

Ich habe gesetzt TextTrimming="CharacterEllipsis" auf der TextBlocks aber kein Glück.

Bearbeiten

Entscheidend scheint, dass das Grid in einem ist DataTemplate, fügen Sie Folgendes ein, um das Verhalten zu beobachten,

<!-- 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>

Der Grund, warum dies wichtig ist, ist, dass ich einen anderen habe Grid als ein Geschwister der ListBox Hier werden die 'Kopfzeilen' für die in der ListBox wie folgt,

<Grid>
    ... Headers and column definitions here
</Grid>

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

und deshalb ist es wichtig, dass die Spalten übereinstimmen.

Ich habe versucht, das zu binden ColumnDefinitions innerhalb der DataTemplate nach außen Grid  ColumnDefinitions aber ich kann nicht leicht einen verbindlichen Hinweis darauf bekommen.


5
2017-08-29 11:34


Ursprung


Antworten:


Dies ist eines der nervigsten Probleme mit WPF. Da der verfügbare Platz, der dem Vorlagengitter zurückgegeben wird, unendlich ist, nimmt der tatsächliche Inhalt so viel Platz in Anspruch, wie er möchte.

Der einfachste Weg besteht darin, eine bestimmte Breite an das Gitter anzupassen, aber das löst nur die Situationen, in denen es keine Größenanpassung gibt.

Während Sie die ListBox-Größe (Breite, im Besonderen) dehnen möchten, denke ich, dass es keine bessere Lösung außer einem benutzerdefinierten Konverter gibt.

Hier ist meine Lösung:

<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>

Und der Konverter:

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
2017-08-29 12:47



Auch wenn dies ein alter Beitrag ist, füge ich meine Ergebnisse hinzu, da sie für andere Leute relevant sein könnten, die diesen Beitrag lesen. Ich hatte ein ähnliches Problem (meine * Spalten teilten die Breite nicht gleichmäßig wie erwartet auf, sie maßen nur basierend auf dem Inhalt). Der Grund dafür war, dass ich eine ListView mit einer ItemsSource hatte, die mit einer List verknüpft war. Die ListView in WPF enthält einen ScrollViewer und ein ScrollViewer hat keine feste Breite. Ohne eine feste Breite kann ein Grid nicht richtig bestimmen, welche Breite einer * Spalte zugewiesen werden soll und zu einer anderen Größenmethode wechseln.

Lösung Ich verwende jetzt ein ItemsControl, das keinen ScrollViewer enthält und daher ist die Breite bekannt, die dem Grid erlaubt, seine Spalten richtig zu bemessen.

Für mehr Details darüber, wie genau das Grid die Größe behandelt, schlage ich vor, dass Sie die Grid-Klasse dekompilieren und sich die folgende Methode ansehen:

protected override Size MeasureOverride(Size constraint) 

Dies ist meine MainWindow.xaml aus meiner Testanwendung (aus der ListView kommentieren, um den Unterschied im Verhalten zu sehen):

 <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));
        }
    }
}

Erstes Viewmodel

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));
        }
    }
}

Zweites Ansichtsmodell

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
2018-02-12 08:54



einstellen

TextTrimming="CharacterEllipsis"

auf dem TextBlock.

Für mich geht das. Wie Sie definiert haben, sollte die mittlere Spalte doppelt so groß sein wie die andere.


0
2017-08-29 11:50