Программно генерируемые расширители с прокручиваемым содержимым, изменяющим размер окна - PullRequest
0 голосов
/ 14 декабря 2018

Я ищу лучший способ создать программно сгенерированное окно со следующим поведением .

То есть: список модулей расширения, которые можно программно инициализировать, каждый из которых содержитпрокручиваемый контент больше, чем может быть отображено в окне (в данном случае это сетка данных).Когда расширитель развернут, его содержимое ограничено доступным размером окна, позволяя просматривать и управлять всеми остальными расширителями.Кроме того, в любой момент времени может быть открыт только один расширитель.

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

XAML (удивительно просто):

<Window x:Class="ExpanderScrollExample.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="200">
<Grid>
    <ItemsControl ItemsSource="{Binding Dict}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Grid Loaded="GridLoaded" ScrollViewer.CanContentScroll="False"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Expander Header ="{Binding Key, Mode=OneWay}" Expanded="Expander_Expanded" Collapsed="Expander_Collapsed">
                    <DataGrid ItemsSource="{Binding Path=Value}"/>
                </Expander>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

Код позади (с большей частью магии):

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace ExpanderScrollExample
{
    public partial class MainWindow : Window
    {

        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = new MainWindowViewModel();
        }

        private void GridLoaded(object sender, RoutedEventArgs e)
        {
            Grid grid = sender as Grid;
            grid.LayoutUpdated += (s, e2) =>
            {
                var childCount = grid.Children.Count;
                int rowsToAdd = (childCount - grid.RowDefinitions.Count);
                for (int row = 0; row < rowsToAdd; row++)
                {
                    RowDefinition rowDefinition = new RowDefinition();
                    rowDefinition.Height = new GridLength(0, GridUnitType.Auto);
                    grid.RowDefinitions.Add(rowDefinition);
                }

                for (int i = 0; i < childCount; i++)
                {
                    var child = grid.Children[i] as FrameworkElement;
                    Grid.SetRow(child, i);
                }
            };
        }

        private void Expander_Expanded(object sender, RoutedEventArgs e)
        {
            ContentPresenter parentDataContext = VisualTreeHelper.GetParent(sender as DependencyObject) as ContentPresenter;
            Grid grid = VisualTreeHelper.GetParent(parentDataContext as DependencyObject) as Grid;
            grid.RowDefinitions[Grid.GetRow(parentDataContext)].Height = new GridLength(1, GridUnitType.Star);
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(grid); i++)
            {
                DependencyObject neighborDataContext = VisualTreeHelper.GetChild(grid, i);
                for (int j = 0; j < VisualTreeHelper.GetChildrenCount(neighborDataContext); j++)
                {
                    DependencyObject neighborExpander = VisualTreeHelper.GetChild(neighborDataContext, j);
                    if (neighborExpander is Expander && neighborExpander != sender)
                    {
                        ((Expander)neighborExpander).IsExpanded = false;
                        this.Collapse(neighborExpander as Expander);
                    }
                }
            }
        }

        private void Expander_Collapsed(object sender, RoutedEventArgs e)
        {
            this.Collapse(sender as Expander);
        }

        private void Collapse(Expander expander)
        {
            ContentPresenter parent = VisualTreeHelper.GetParent(expander as DependencyObject) as ContentPresenter;
            Grid grandparent = VisualTreeHelper.GetParent(parent as DependencyObject) as Grid;
            grandparent.RowDefinitions[Grid.GetRow(parent)].Height = new GridLength(0, GridUnitType.Auto);
        }
    }
}

ViewModel (только для генерации данных)

using System.Collections.Generic;

namespace ExpanderScrollExample
{
    class MainWindowViewModel
    {
        public Dictionary<string, List<MyClass>> Dict { get; }

        public MainWindowViewModel()
        {
            Dict = new Dictionary<string, List<MyClass>>();
            for ( int i = 0; i < 5; i++ )
            {
                string key = "Header " + i.ToString();
                Dict[key] = new List<MyClass>();
                for ( int j = 0; j < 100; j++)
                {
                    Dict[key].Add(new MyClass(j, i*100 + j));
                }
            }
        }

        public class MyClass
        {
            public int Column1 {get; set;}
            public int Column2 { get; set; }

            public MyClass( int column1, int column2)
            {
                Column1 = column1;
                Column2 = column2;
            }
        }
    }
}

Я также обычно пытаюсь подтвердить с шаблоном MVVM, но не смог сделать этого в этом случае.

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

Есть ли лучший способ сделать это?

1 Ответ

0 голосов
/ 15 декабря 2018

1.Только один Expander расширенный в данный момент

Чтобы убедиться, что только один Expander расширен в данный момент, вы можете создать Attached Behavior.Я реализовал один для справки.

ExpanderGroupBehavior (на основе RadioButton)

<Expander wpf:ExpanderGroupBehavior.GroupName="ExpanderGroup01" />

Это гарантирует, что только один Expander в пределах одной группы развернут.

2.Расширенный Expander заполнить доступное пространство

Для этого вы можете создать свой собственный Panel, который обрабатывает это для вас.См. Как получить элементы управления в WPF для заполнения доступного пространства? и https://docs.microsoft.com/en-us/dotnet/framework/wpf/controls/how-to-create-a-custom-panel-element

...