试图让ListView显示固定数量的项目的布局问题

时间:2016-11-13 18:45:29

标签: c# xaml listview layout uwp

在UWP项目中,我有ListView绑定到ObservableCollection<T>Person个对象。我使用DataTemplatePerson中显示ListView对象。集合(人员)最多只能包含由_maxPeopleCount设置的一定数量的人。

我希望发生的是ListView

  1. 根本不滚动。
  2. 显示Person定义的所有_maxPeopleCount个对象。
  3. 完全显示每个Person个对象,而不是部分显示。也就是说,显示每个项目,以便ListViewItem.Height =(来自ItemsPanel的可用高度)/ _maxPeopleCount
  4. 当开始时少于_maxPeopleCount个项目时,仍会显示添加的项目,其中包含(3)中定义的高度。
  5. 将每个Person的FirstName和LastName显示为尽可能大。
  6. 目前我无法让我的项目做到这一点。

    任何人都可以帮忙解决这个问题或建议其他方法吗?

    以下是显示问题的示例代码。只需按下按钮7次以上,因为这是代码中的_maxPeopleCount。你会看到7人没有出现。为可怕的UI道歉。它用最小的XAML显示问题,但与我的真实项目类似。

    非常感谢您的任何帮助。

    背后的代码:

    public sealed partial class MainPage : Page
    {
        ObservableCollection<Person> _people = new ObservableCollection<Person>();
        int _personCount = 0;
        int _maxPeopleCount = 7;
        public MainPage()
        {
            this.InitializeComponent();
            this.DataContext = _people;
    
        }
    
        private void btnAddPerson_Click(object sender, RoutedEventArgs e)
        {
            if (_people.Count == _maxPeopleCount)
            {
                _people.RemoveAt(_people.Count - 1);
            }
            _personCount += 1;
            _people.Insert(0, new Person($"FirstName {_personCount}", $"LastName {_personCount}"));
        }
    

    XAML:

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
    
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
    
        <Button Grid.Row="0" Grid.Column="0" x:Name="btnAddPerson" Click="btnAddPerson_Click" Content="Add Person" VerticalAlignment="Center" HorizontalAlignment="Center"/>
    
        <ListView BorderBrush="Black"  BorderThickness="5" Margin="10" Grid.Row="1" Grid.Column="0" ItemsSource="{Binding}" ScrollViewer.VerticalScrollMode="Disabled" ScrollViewer.VerticalScrollBarVisibility="Hidden" ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.HorizontalScrollMode="Disabled" VerticalAlignment="Stretch"  VerticalContentAlignment="Stretch" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch">
            <ListView.ItemContainerStyle>
                <Style TargetType="ListViewItem">
                    <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                </Style>
            </ListView.ItemContainerStyle>
            <ListView.ItemsPanel>
                <ItemsPanelTemplate>
                    <ItemsStackPanel HorizontalAlignment="Stretch"/>
                </ItemsPanelTemplate>
            </ListView.ItemsPanel>
            <ListView.ItemTemplate>
                <DataTemplate>
                    <Grid x:Name="grdPerson">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="*"/>
                        </Grid.ColumnDefinitions>
                        <Border Grid.Column="0" Background="Green" HorizontalAlignment="Stretch">
                            <Viewbox>
                                <TextBlock Text="{Binding FirstName}" HorizontalAlignment="Stretch"/>
                            </Viewbox>
                        </Border>
                        <Border Grid.Column="1" Background="Yellow" HorizontalAlignment="Stretch">
                            <Viewbox>
                                <TextBlock Text="{Binding LastName}" HorizontalAlignment="Stretch" />
                            </Viewbox>
                        </Border>
                    </Grid>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>
    

    PERSON CLASS:

      public class Person : INotifyPropertyChanged
    {
    
        public Person(string firstName, string lastName)
        {
             FirstName = firstName;
            LastName = lastName;
        }
    
    
        private string _firstName = string.Empty;
        public string FirstName
        {
            get { return _firstName; }
            set
            {
                _firstName = value;
                OnPropertyChanged();
            }
        }
    
    
        private string _lastName = string.Empty;
        public string LastName
        {
            get { return _lastName; }
            set
            {
                _lastName = value;
                OnPropertyChanged();
            }
        }
    
        protected void OnPropertyChanged([CallerMemberName] string caller = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(caller));
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
    
    }
    

3 个答案:

答案 0 :(得分:1)

  

根本不滚动。

为此,您已经完成了ScrollViewer.VerticalScrollMode="Disabled" ScrollViewer.VerticalScrollBarVisibility="Hidden"

  

显示_maxPeopleCount定义的所有Person对象。

实际上,您已将_maxPeopleCount个人记录绑定到ListView,但受窗口大小的限制,您无法全部看到它们。那里存在不可见的记录。如果通过手动拖动拉伸应用程序窗口大小以使高度更大,您将看到剩余的记录。如果您希望始终显示所有记录,您可能需要自己计算ListViewItem高度。

  

完全显示每个Person对象,而不是部分显示。也就是说,显示每个项目以便ListViewItem.Height =(来自ItemsPanel的可用高度)/ _maxPeopleCount。

在您的方案中,ListViewItem的高度会自动计算,因为您没有为其设置修复高度。正如您在此处显示的公式,我们可以在代码后面动态绑定ListViewItem的{​​{3}}类的高度。请注意窗口大小更改会影响ListView的实际大小,因此我们还需要在应用窗口大小更改时调整ListViewItem的大小。当ListViewItem的高度设置为小于44时,它将不会生效,因为ViewTreeHelper的默认MinHeight44,需要重置。有关实施此要求的更多设置,请参阅下面的演示。

  

当小于_maxPeopleCount的项目开始时,仍会显示添加的项目,其中包含(3)中定义的高度。

计算完成后,ListViewItem的所有高度都相同。

  

将每个Person的FirstName和LastName显示为尽可能大。

为此,您<Setter Property="HorizontalContentAlignment" Value="Stretch"/>要展开水平内容,请同时按<Setter Property="VerticalContentAlignment" Value="Stretch"/>拉伸垂直内容。 padding ListviewItem需要设置为0

更新了以下代码,现在可以满足您的所有要求。

XAML

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" x:Name="gridroot">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Button Grid.Row="0" Grid.Column="0" x:Name="btnAddPerson" Click="btnAddPerson_Click" Content="Add Person" VerticalAlignment="Center" HorizontalAlignment="Center"/>
    <ListView x:Name="listperson"  BorderBrush="Black"  BorderThickness="5" Margin="10" Grid.Row="1" Grid.Column="0" ItemsSource="{Binding}" ScrollViewer.VerticalScrollMode="Disabled" ScrollViewer.VerticalScrollBarVisibility="Hidden" ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.HorizontalScrollMode="Disabled" VerticalAlignment="Stretch"  VerticalContentAlignment="Stretch" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch">
        <ListView.ItemContainerStyle>
            <Style TargetType="ListViewItem">
                <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                <Setter Property="VerticalContentAlignment" Value="Stretch"/>
                <Setter Property="MinHeight" Value="20"/>
                <Setter Property="Padding" Value="0"/>
            </Style>
        </ListView.ItemContainerStyle>
        <ListView.ItemsPanel>
            <ItemsPanelTemplate>
                <ItemsStackPanel HorizontalAlignment="Stretch"/>
            </ItemsPanelTemplate>
        </ListView.ItemsPanel>
        <ListView.ItemTemplate>
            <DataTemplate>
                <Grid x:Name="grdPerson" Loaded="grdPerson_Loaded" >
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="*"/>
                    </Grid.ColumnDefinitions>
                    <Border Grid.Column="0" Background="Green" HorizontalAlignment="Stretch" >
                        <Viewbox>
                            <TextBlock Text="{Binding FirstName}" HorizontalAlignment="Stretch" />
                        </Viewbox>
                    </Border>
                    <Border Grid.Column="1" Background="Yellow" HorizontalAlignment="Stretch">
                        <Viewbox>
                            <TextBlock Text="{Binding LastName}" HorizontalAlignment="Stretch"  />
                        </Viewbox>
                    </Border>
                </Grid>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</Grid>

背后的代码

public sealed partial class MainPage : Page
{
    ObservableCollection<Person> _people = new ObservableCollection<Person>();
    int _personCount = 0;
    int _maxPeopleCount = 7;
    public MainPage()
    {
        this.InitializeComponent();
        this.DataContext = _people;
        Window.Current.SizeChanged += Current_SizeChanged;
    } 
    public void resize()
    { 
        var listpersonheight = listperson.ActualHeight; 
        IEnumerable<ListViewItem> items = FindVisualChildren<ListViewItem>(listperson);
        for (int i = 0; i < items.Count(); i++)
        {
            foreach (ListViewItem item in items)
            {
                item.Height = (listpersonheight - 10) / _maxPeopleCount;// BorderThickness size need to be minus.
                item.Width = listperson.ActualWidth - 10; //Width also need resize.
            }
        }
    }
    private void Current_SizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
    {
        resize();
    }
    private void grdPerson_Loaded(object sender, RoutedEventArgs e)
    {
        resize();
    }
    private void btnAddPerson_Click(object sender, RoutedEventArgs e)
    { 
        if (_people.Count == _maxPeopleCount)
        {
            _people.RemoveAt(_people.Count - 1);
        }
        _personCount += 1; 
        _people.Insert(0, new Person($"FirstName {_personCount}", $"LastName {_personCount}")); 
    }

    private static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
    {
        if (depObj != null)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
                if (child != null && child is T)
                {
                    yield return (T)child;
                }

                foreach (T childOfChild in FindVisualChildren<T>(child))
                {
                    yield return childOfChild;
                }
            }
        }
    }  
}

答案 1 :(得分:1)

您可以随时编写自己的Panel,这很容易。这是我写的UniformStackPanel,它像StackPanel一样工作,但它会拉伸每个项目以填充可用空间(无论它有多少项目)。

Ctrl + PageDown

将它与ItemsControl一起使用,如下所示:

public class UniformStackPanel : StackPanel
{
    protected override Size MeasureOverride(Size availableSize)
    {
        var childSize = Orientation == Orientation.Horizontal ?
            new Size(availableSize.Width / Children.Count, availableSize.Height) :
            new Size(availableSize.Width, availableSize.Height / Children.Count);

        double alongAxis = 0;
        double crossAxis = 0;

        foreach (var child in Children)
        {
            child.Measure(childSize);

            if (Orientation == Orientation.Horizontal)
            {
                alongAxis += child.DesiredSize.Width;
                crossAxis = Math.Max(crossAxis, child.DesiredSize.Height);
            }
            else
            {
                alongAxis += child.DesiredSize.Height;
                crossAxis = Math.Max(crossAxis, child.DesiredSize.Width);
            }
        }

        return Orientation == Orientation.Horizontal ?
            new Size(alongAxis, crossAxis) :
            new Size(crossAxis, alongAxis);
    }

    protected override Size ArrangeOverride(Size finalSize)
    {
        var childSize = Orientation == Orientation.Horizontal ?
            new Size(finalSize.Width / Children.Count, finalSize.Height) :
            new Size(finalSize.Width, finalSize.Height / Children.Count);

        double alongAxis = 0;

        foreach (var child in Children)
        {
            if (Orientation == Orientation.Horizontal)
            {
                child.Arrange(new Rect(alongAxis, 0, childSize.Width, childSize.Height));
                alongAxis += childSize.Width;
            }
            else
            {
                child.Arrange(new Rect(0, alongAxis, childSize.Width, childSize.Height));
                alongAxis += childSize.Height;
            }
        }

        return finalSize;
    }
}

Screenshot

答案 2 :(得分:0)

好的,我想我已经破解了!我将ItemsPanelTemplate更改为hereUniformGrid面板。它是WPF具有的UWP版本,因此您可以设置行数和列数。对于我上面的例子,Rows = 7,Columns = 1。

之后我改变的是在Setter中添加以下额外的ItemContainerStyle

<Setter Property="VerticalContentAlignment" Value="Stretch"/>

修复了ListViewItems内容未被最大化的问题。

希望这有助于他人,并为那些在帮助下回复的人欢呼。