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

Item4 свернут (обратите внимание на смещение следующих элементов):

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





вы можете использовать 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, независимо от того, сколько предметов в ...
Я думаю, ваш элемент управления должен содержать две панели стека, содержащиеся в сетке. Код внутри элемента управления может назначить его содержимое одной из панелей.