Комбинация WPF между сеткой и стековой панелью

В настоящее время я пытаюсь найти / реализовать элемент управления WPF, который представляет собой комбинацию Grid и StackPanel. Это означает, что мне нравится иметь две колонки с несколькими элементами. Элемент - это другой элемент управления (например, одиночный Label, TextBox с меткой на панели, ...).

Если элемент свернут, пустое пространство должно быть заполнено следующим элементом, это означает, что в элементе управления всегда используется как можно меньше места (без промежутков между отдельными элементами).

Я прикрепил два изображения, как это должно выглядеть.

Исходный: Комбинация WPF между сеткой и стековой панелью

Item4 свернут (обратите внимание на смещение следующих элементов): Комбинация WPF между сеткой и стековой панелью

Есть ли у кого-нибудь идея или опыт, как сделать что-то подобное?

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

Robin Bennett 18.12.2018 11:39

Вы пробовали использовать сетку с двумя стековыми панелями?

colosso 18.12.2018 11:39

Вы всегда можете создать свою собственную панель MyPanel: Panel и настроить расположение дочерних элементов, переопределив MeasureOverride и ArrangeOverride. Это довольно просто, попробуйте. Сворачивать сложно, поскольку вы не предоставили никакого решения, как именно это должно выглядеть. Кликнув? Что, если дочерние элементы - это TextBox или другой настраиваемый / редактируемый элемент управления? Как выполняется разворачивание?

Sinatr 18.12.2018 11:40

@RobinBennett, вы пропускаете смещающуюся часть после того, как что-то свернуто, для чего вам нужно набрать прикрепленные свойства отслеживания дочерних элементов, что означает, что ваш составной макет будет непростым.

Sinatr 18.12.2018 11:44

Как насчет WrapPanel, который будет обрабатывать сворачивающиеся дочерние элементы управления, а затем рисовать два прямоугольника поверх?

Robin Bennett 18.12.2018 11:54

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

Andy 18.12.2018 11:58
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
6
1 328
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

вы можете использовать ListView, который использует StackPanel или DockPanel как ItemTemplate. Это пример (упрощенный) того, что я использую для отображения наблюдаемой коллекции объектов в списке с помощью Grid и DockPannel:

                   <ListView x:Name = "lbxDroppedDatas" SelectionMode = "Single" AllowDrop = "True" BorderThickness = "0" Padding = "0" Margin = "0" ScrollViewer.HorizontalScrollBarVisibility = "Disabled" SelectionChanged = "lbxDroppedDatas_SelectionChanged" PreviewKeyDown = "lbxDroppedDatas_PreviewKeyDown" PreviewMouseRightButtonDown = "lbxDroppedDatas_PreviewMouseRightButtonDown" >
                        <ListView.ItemTemplate>
                            <DataTemplate>
                                <DockPanel Grid.Row = "0" Width = "{Binding Path=ActualWidth, ElementName=Container}" Height = "40" Margin = "1" LastChildFill = "True">
                                        <CheckBox DockPanel.Dock = "Left" IsChecked = "{Binding IsChecked}" VerticalAlignment = "Center" Checked = "CheckBox_Checked" Unchecked = "CheckBox_Unchecked" />
                                        <Label Content = "{Binding Name}" Foreground = "{Binding LineBrush}" FontWeight = "Bold" FontSize = "12" Margin = "5" HorizontalAlignment = "Left" VerticalAlignment = "Center" Padding = "0" MouseEnter = "Label_MouseEnter" MouseLeave = "Label_MouseLeave"/>
                                        <TextBox DockPanel.Dock = "Right" Width = "60" MaxLength = "14" Text = "{Binding CurrentValue, StringFormat=N}" Margin = "0,0,33,0" Background = "Transparent" Foreground = "{Binding LineBrush}" BorderBrush = "{Binding LineBrush}" BorderThickness = "1" VerticalAlignment = "Center" HorizontalAlignment = "Right" IsEnabled = "False"/>
                                    </DockPanel>
                             </DataTemplate>
                        </ListView.ItemTemplate>
                        <ListView.ItemsPanel>
                            <ItemsPanelTemplate>
                                <WrapPanel Orientation = "Vertical" VerticalAlignment = "Stretch" Margin = "0"/>
                            </ItemsPanelTemplate>
                        </ListView.ItemsPanel>
                    </ListView>

Вы можете привязать видимость элемента списка к свернутому или видимому свойству.

Я бы решил эту проблему следующим образом:

  • начните с создания сетки с двумя панелями стека (сетка находится внутри UserControl или любого другого заполнителя):

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width = "*" />
            <ColumnDefinition Width = "*" />
        </Grid.ColumnDefinitions>
        <StackPanel x:Name = "LeftPanel" x:FieldModifier = "private" Orientation = "Vertical" />
        <StackPanel x:Name = "RightPanel" x:FieldModifier = "private" Grid.Column = "1" Orientation = "Vertical" />
    </Grid>
    
  • чтобы управлять своими элементами, создайте список элементов, желательно в коде вашего UserControl:
    private List<FrameworkElement> m_items = new List<FrameworkElement>();

  • для элементов, которые вы не хотите видеть, установите Visibility на Hidden или Collapsed.

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

    public void SortItems()
    {
        this.LeftPanel.Children.RemoveRange(0, this.LeftPanel.Children.Count);
        this.RightPanel.Children.RemoveRange(0, this.RightPanel.Children.Count);
        this.m_items.Where(i => i.Visibility == Visibility.Visible).ToList().ForEach((i) => { (this.LeftPanel.Children.Count == this.RightPanel.Children.Count ? this.LeftPanel : this.RightPanel).Children.Add(i); } );
    }
    

Метод просто удаляет все дочерние элементы панелей стека, а затем просматривает список элементов, выбирая только те, которые являются Visible, и добавляет один к левой панели, если обе панели имеют одинаковое количество элементов в них, и к правой панели в противном случае.

Если вам нужен другой способ выбора панели, в которую следует добавить следующий элемент (например, ActualHeight), просто измените условие выбора панелей.

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

Ответ принят как подходящий

Большое спасибо за разные решения. В конце концов я решил это с помощью предложения Sinatr. Итак, я создал свою собственную панель и переопределил ArrangeOverride:

protected override Size ArrangeOverride(Size finalSize)
    {
        List<UIElement> visibleItems = new List<UIElement>();
        double xColumn1 = 0;
        double xColumn2 = (finalSize.Width / 2) + (WIDTH_COLUMN_SEPERATOR / 2);
        double y = 0;

        double columnWidth = (finalSize.Width - WIDTH_COLUMN_SEPERATOR) / 2;

        for (int i = 0; i < InternalChildren.Count; i++)
        {
            UIElement child = InternalChildren[i];
            if (child.Visibility != Visibility.Collapsed)
            {
                visibleItems.Add(child);
            }
        }

        for (int i = 0; i < visibleItems.Count; i++)
        {
            if (i >= (visibleItems.Count - 1))
            {
                visibleItems[i].Arrange(new Rect(xColumn1, y, columnWidth, visibleItems[i].DesiredSize.Height));
            }
            else
            {
                UIElement leftItem = visibleItems[i];
                UIElement rightItem = visibleItems[i + 1];

                double rowHeight = leftItem.DesiredSize.Height > rightItem.DesiredSize.Height ? leftItem.DesiredSize.Height : rightItem.DesiredSize.Height;

                leftItem.Arrange(new Rect(xColumn1, y, columnWidth, rowHeight));
                rightItem.Arrange(new Rect(xColumn2, y, columnWidth, rowHeight));

                y += rowHeight;

                i++;
            }
        }

        return finalSize;
    }

Я также создал свой собственный UserControl для Items:

<Grid DataContext = "{Binding RelativeSource = {RelativeSource Mode=FindAncestor, AncestorType = {x:Type UserControl}}}">
    <Grid.RowDefinitions>
        <RowDefinition Height = "Auto"/>
        <RowDefinition Height = "Auto"/>
    </Grid.RowDefinitions>
    <Label x:Name = "captionLabel" Grid.Row = "0" Content = "{Binding Caption}"/>
    <ContentPresenter x:Name = "inputMask" Grid.Row = "1" Content = "{Binding InputMask, ElementName=userControlBRAIN2AttributePanelItem}" />
</Grid>

Итак, я получаю следующий результат (из моего тестового приложения):

введите описание изображения здесь

со свернутым элементом 3:

введите описание изображения здесь

Я удивлен, что никто не предложил использовать UniformGrid.

Все, что вам нужно сделать, это объявить UniformGrid, установить для Columns значение 2 и добавить свои элементы. Элементы, которые свернуты, должны иметь значение Visibility для Collapsed (да).

Вот пример:

<UniformGrid Columns = "2"
             VerticalAlignment = "Center"
             HorizontalAlignment = "Center">

    <TextBlock Text = "Item 1 " />
    <TextBlock Text = "Item 2 " />
    <TextBlock Text = "Item 3 " />
    <TextBlock Text = "Item 4 " />
</UniformGrid>

Дадим следующий результат:

И когда Visibility второго TextBlock установлен на Collapsed, вот как это выглядит:

Спасибо за это решение "appa yip yip", единственное, что здесь не работает для меня, это то, что элементы не имеют фиксированной высоты в UniformGrid, когда несколько элементов свернуты, другие элементы получают большую высоту, потому что они всегда растягиваются до высоты UniformGrip, независимо от того, сколько предметов в ...

M Sauter 02.01.2019 11:11

Другие вопросы по теме