Переоценка строк и столбцов UniformGrid WPF

Я пытаюсь создать видеоплеер, который может воспроизводить несколько видео одновременно. Я заставил игроков работать, и теперь я пытаюсь позволить пользователю добавлять дополнительные видео во время выполнения. Я отображаю видео в единой сетке, которая использует конвертер, чтобы решить, сколько строк и столбцов следует сгенерировать, основываясь на количестве видео. Он отлично работает, когда вы определяете, сколько игроков есть до запуска, однако, когда я добавляю игрока во время его работы, единая сетка не обновляет строки или столбцы. Это просто добавляет другое видео к любой структуре, которая у него была раньше. Есть ли в любом случае, что я могу заставить его пересмотреть строки / столбцы?

TL; DR: Могу ли я пересмотреть строки и столбцы единой сетки при просмотре? Как?

XAML для контроля ниже. Дополнительная информация, которая может быть полезна:
1. Игроки - это 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>

2 ответа

Решение

Проблема, как представляется, заключается в том, что ничто не уведомляет о привязке, что количество элементов в Players изменилось: привязка сидит в ожидании вашей модели представления, чтобы сказать, что есть новая Players коллекция.

Но с тех пор Players является ObservableCollectionподнимет PropertyChanged когда это Count изменения. Итак, простейший случай:

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

Следующее работает для меня, чтобы обновить количество строк и столбцов при добавлении элементов в Players, Арифметика в конвертере может потребовать небольшой работы, но дело в том, что привязки обновляются UniformGrid.Rows а также UniformGrid.Columns каждый раз Players изменения. Это работает, потому что ObservableCollection повышения PropertyChanged("Count") каждый раз, когда элемент добавляется или удаляется. Потому что я привязан к свойству ObservableCollectionпривязка подписывается на ObservableCollection"s PropertyChanged событие, поэтому он знает, чтобы обновить, когда 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>

Настройка Rows а также Columns не нужен вообще. Все будет работать без них тоже. UniformGrid поместит все равномерно внутри любого контейнера.

Если вы все еще хотите использовать Binding, тогда следующие работы для меня, Cols зафиксированы здесь. Converters не нужны.

    void Players_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        Rows = (int)Math.Ceiling(Players.Count / Cols);
    }
Другие вопросы по тегам