Подтвердить что ты не робот

Элементы управления растяжкой для заполнения ItemsControl

Я пытаюсь написать простой проект обучения WPF, который создает набор кнопок внутри главного окна с изменяемым размером. Для каждой записи в коллекции должна быть одна Button, и содержимое этой коллекции может меняться во время выполнения. Я хочу, чтобы кнопки заполняли все окно (например, 1 кнопка @100% ширина, 2 кнопки с шириной 50%, 3 кнопки шириной 33% и т.д. Все на высоте 100%). Упрощенная версия того, что я написал до сих пор:

<ItemsControl x:Name="itemscontrolButtons" ItemsSource="{Binding}">
  <ItemsControl.ItemTemplate>
    <DataTemplate>
      <Button Tag="{Binding}">
        <TextBlock Text="{Binding}" />
      </Button>
    </DataTemplate>
  </ItemsControl.ItemTemplate>
  <ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
      <StackPanel Orientation="Horizontal" />
    </ItemsPanelTemplate>
  </ItemsControl.ItemsPanel>
</ItemsControl>
...    
List<string> listButtonTexts = new List<string>() { "Button1", "Button2" };
...
itemscontrolButtons.DataContext = listButtonTexts;

В результате получается следующее:

alt text

Мне не удалось заставить кнопки растягиваться в соответствии с шириной, и мои попытки использовать Grid вместо StackPanel оказались бесплодными.

Затем, в качестве дополнительного улучшения, я был бы признателен за советы по его настройке таким образом, чтобы, если есть так много кнопок, что они не могут правильно поместиться на линии или уже имеют пороговое значение, она будет перенесена на новую строку ( тем самым уменьшая половину высоты кнопок при переходе от 1 до 2 строк).

Я хотел бы подчеркнуть, что я хотел бы знать, как это сделать WPF. Я понимаю, что могу использовать сообщения об изменении размера окна и явно изменять размеры элементов управления, а также то, как я сделал это с помощью MFC или WinForms, но из того, что я прочитал, не так, как это должно быть сделано с WPF.

4b9b3361

Ответ 1

Замените панель управления элементами на UniformGrid, это позволит размеру всех детей, чтобы все соответствовало точно:

<ItemsControl>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <UniformGrid Rows="1"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>

Ответ 2

Мне удалось построить ответ на Nir, чтобы выполнить мой необязательный критерий, позволяющий растягивать WPF с несколькими строками.

Сначала вы должны иметь возможность изменять свойства шаблона UniformGrid. Для этого вам нужно сохранить ссылку на него. Есть несколько методов для этого. Я решил обработать событие Loaded для UniformGrid и сохранить ссылку в это время.

<ItemsControl ItemsSource="{Binding}">
  <ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
      <UniformGrid Rows="1" Loaded="UniformGrid_Loaded" />
    </ItemsPanelTemplate>
  </ItemsControl.ItemsPanel>
</ItemsControl>

И в коде позади:

UniformGrid uniformgridButtons;
private void UniformGrid_Loaded(object sender, RoutedEventArgs e)
{
  uniformgridButtons = sender as UniformGrid;
}

Затем обработайте событие SizeChanged и настройте параметр rows в соответствии с любыми критериями.

private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
  if ((uniformgridButtons != null) && (this.Width > 0))
  {
    // Set row count according to your requirements
    uniformgridButtons.Rows = (int)(1 + ((this.MinWidth * iButtonCount) / this.Width));
  }
}

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

alt text

(Анимированный образец здесь)


Обновление 2009/08/28:

Я настраивал приложение для обучения, так что ItemsControl находился в DataTemplate в отдельном ResourceDictionary. Однако такие события, как Loaded, не могут обрабатываться во внешнем файле ResourceDictionary XAML, поскольку он не имеет кода (обычно). Поэтому мне нужно было кэшировать ссылку на UniformGrid с помощью другой техники.

Вместо этого я использовал метод Алана Хейга FindItemsPanel (10-й ответ). Во-первых, я заменил ItemsControl на:

<ContentPresenter x:Name="contentpresenterButtons" Content="{Binding obscolButtons}" />

Тогда в моем ResourceDictionary я положил ItemsControl в DataTemplate:

<DataTemplate DataType="{x:Type local:Buttons}">
  <ItemsControl ... 
</DataTemplate>

Наконец, я сохранил ссылку на templated UniformGrid следующим образом:

private void Window_Loaded(object sender, RoutedEventArgs e) {
  uniformgridButtons = FindItemsPanel<UniformGrid>(contentpresenterButtons);
  // Also call the code in Window_SizeChanged which I put in another method
}

Это работает так же, как мое предыдущее решение, но не требует обработчика событий в ResourceDictionary.

Ответ 3

<!-- width must be explicitly set for this example -->
<StackPanel 
    Name="MyStack" 
    Orientation="Horizontal"
    Width="250"
    Load="Window_Loaded"> 
    <Button/>
    <Button/>
    <Button/>
</StackPanel>

public void Window_Loaded(object sender, RoutedEventArgs e)
{
    UIElementCollection elements = MyStack.Children;
    int count = elements.Count;

    foreach (UIElement element in elements)
    {
        if (element is Button)
            ((Button)element).Width = MyStack.Width / count;
    }
}