重新评估 UniformGrid 行和列 WPF

Reevaluating UniformGrid rows and Columns WPF

我正在尝试创建一个可以同时播放多个视频的视频播放器。我已经让播放器开始工作,现在我正在尝试允许用户在 运行 时间添加额外的视频。我在一个统一的网格中显示视频,它使用一个转换器来决定它应该根据视频的数量生成多少行和多少列。当您在 运行ning 之前定义有多少玩家时,它工作正常,但是当我添加一个玩家时,它的 运行ning 统一网格不会更新行或列。它只是将另一个视频添加到它之前的任何结构中。无论如何我可以强制它重新评估 rows/columns?

TL;DR:我可以在查看统一网格时重新评估它的行和列吗?怎么样?

控件的xaml如下。 可能有用的其他信息:
1. Players 是一个 ObservableCollection
2. 控件显示为 TabControl

<UserControl x:Class="Views.AllVideos"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-Views"
             mc:Ignorable="d">
  <ItemsControl ItemsSource="{Binding Players}"
                x:Name="AllVideosControl">
    <ItemsControl.ItemsPanel>
      <ItemsPanelTemplate>
        <UniformGrid Columns="{Binding Players, Converter={StaticResource CountToColumns}, Mode=OneWay}"
                     Rows="{Binding Players, Converter={StaticResource CountToRows}, Mode=OneWay}"
                     VerticalAlignment="Stretch"
                     HorizontalAlignment="Stretch"
                     IsItemsHost="True"/>
      </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
      <DataTemplate>
        <Border BorderBrush="{DynamicResource AccentBrush}"
                BorderThickness="1"
                Margin="5">
          <ls:PanelPreview DataContext="{Binding}"/>
        </Border>
      </DataTemplate>
    </ItemsControl.ItemTemplate>
  </ItemsControl>
</UserControl>

问题似乎是没有任何东西通知绑定 Players 中的项目数量已更改:绑定正坐在那里等待您的视图模型告诉它有一个新的 Players collection。

但是由于 Players 是一个 ObservableCollection,它会在 Count 变化时提高 PropertyChanged。所以,最简单的情况:

<UniformGrid
    Rows="{Binding Players.Count}"
    ...
    />

当项目添加到 Players 时,我可以通过以下方式更新行数和列数。转换器中的算法可能需要一些工作,但关键是绑定会在每次 Players 更改时更新 UniformGrid.RowsUniformGrid.Columns。这是有效的,因为每次添加或删除项目时 ObservableCollection 都会引发 PropertyChanged("Count")。因为我绑定到 ObservableCollection 的 属性,绑定订阅了 ObservableCollectionPropertyChanged 事件,所以它知道在 Count 时更新变化。

C#

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void AddButton_Click(object sender, RoutedEventArgs e)
    {
        (DataContext as ViewModel).Players.Add($"Player {(DataContext as ViewModel).Players.Count + 1}");
    }

    private void RemoveButton_Click(object sender, RoutedEventArgs e)
    {
        if ((DataContext as ViewModel).Players.Count > 0)
            (DataContext as ViewModel).Players.RemoveAt(0);
    }
}

public class ViewModel
{
    private ObservableCollection<String> _players = new ObservableCollection<string>();
    public ObservableCollection<String> Players
    {
        get { return _players; }
    }
}

public class RowsColumnsConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return Math.Ceiling(Math.Sqrt((int)value));
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

XAML

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

    <ItemsControl
        Grid.Row="1"
        ItemsSource="{Binding Players}"
        >
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <UniformGrid
                    Rows="{Binding Players.Count, Converter={StaticResource RowsColumns}}"
                    Columns="{Binding Players.Count, Converter={StaticResource RowsColumns}}"
                    />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>

    <StackPanel Grid.Row="0" Orientation="Horizontal">
        <Button 
            Content="Add Player" 
            Click="AddButton_Click" 
            Margin="2" 
            Width="100" 
            />
        <Button 
            Content="Remove Player" 
            Click="RemoveButton_Click"
            Margin="2" 
            Width="100" 
            />
    </StackPanel>
</Grid>

根本不需要设置 RowsColumns。没有它们,一切都会正常进行。 UniformGrid 将所有内容统一放置在任何容器中。

如果你仍然想使用 Binding ,那么以下对我有用, Cols 在这里固定。 Converters不需要。

    void Players_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        Rows = (int)Math.Ceiling(Players.Count / Cols);
    }