网格和堆栈面板之间的 WPF 组合
WPF combination between grid and stackpanel
我目前正在尝试 find/implement WPF 控件,它是 Grid
和 StackPanel
的组合。这意味着,我喜欢有两个包含多个项目的列。一个项目是另一个控件(例如,面板中带有标签的单个 Label
、TextBox
,...)。
如果一个项目被折叠,空的 space 应该被下一个项目填充,这意味着我总是尽可能少地在控件中使用 space (单个项目之间没有间隙).
我附上了两张图片应该是什么样子。
初始:
Item4 已折叠(注意后续项目的移动):
有没有人有想法或经验如何做这样的事情?
您可以使用将 StackPanel 或 DockPanel 用作 ItemTemplate 的 ListView。
这是我使用 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
。
您需要一些方法来 select 可见项目并将它们交替放置在左右面板上。我想出了一些您需要在每次列表更改时调用的快速方法(添加或删除项目,更改项目的可见性):
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
的项目,如果两个面板中的项目数量相同,则向左侧面板添加一个他们和右面板否则。
如果您需要其他方式 select 将以下项目添加到哪个面板(例如 ActualHeight
),只需更改 select 面板的条件即可。
如果你想让这个方法更优雅,你可以添加一些事件或依赖属性来自动调用 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;
}
我还为项目创建了自己的用户控件:
<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>
所以我得到以下结果(来自我的测试应用程序):
enter image description here
第 3 项已折叠:
enter image description here
我很惊讶没有人建议使用 UniformGrid
。
您所要做的就是声明一个 UniformGrid
,将 Columns
设置为 2 并添加您的项目。折叠的项目应将其 Visibility
设置为 Collapsed
(duh)。
这是一个例子:
<UniformGrid Columns="2"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<TextBlock Text="Item 1 " />
<TextBlock Text="Item 2 " />
<TextBlock Text="Item 3 " />
<TextBlock Text="Item 4 " />
</UniformGrid>
将产生以下结果:
当第二个 TextBlock
的 Visibility
设置为 Collapsed
时,如下所示:
我目前正在尝试 find/implement WPF 控件,它是 Grid
和 StackPanel
的组合。这意味着,我喜欢有两个包含多个项目的列。一个项目是另一个控件(例如,面板中带有标签的单个 Label
、TextBox
,...)。
如果一个项目被折叠,空的 space 应该被下一个项目填充,这意味着我总是尽可能少地在控件中使用 space (单个项目之间没有间隙).
我附上了两张图片应该是什么样子。
初始:
Item4 已折叠(注意后续项目的移动):
有没有人有想法或经验如何做这样的事情?
您可以使用将 StackPanel 或 DockPanel 用作 ItemTemplate 的 ListView。 这是我使用 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
。您需要一些方法来 select 可见项目并将它们交替放置在左右面板上。我想出了一些您需要在每次列表更改时调用的快速方法(添加或删除项目,更改项目的可见性):
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
的项目,如果两个面板中的项目数量相同,则向左侧面板添加一个他们和右面板否则。
如果您需要其他方式 select 将以下项目添加到哪个面板(例如 ActualHeight
),只需更改 select 面板的条件即可。
如果你想让这个方法更优雅,你可以添加一些事件或依赖属性来自动调用 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;
}
我还为项目创建了自己的用户控件:
<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>
所以我得到以下结果(来自我的测试应用程序):
enter image description here
第 3 项已折叠:
enter image description here
我很惊讶没有人建议使用 UniformGrid
。
您所要做的就是声明一个 UniformGrid
,将 Columns
设置为 2 并添加您的项目。折叠的项目应将其 Visibility
设置为 Collapsed
(duh)。
这是一个例子:
<UniformGrid Columns="2"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<TextBlock Text="Item 1 " />
<TextBlock Text="Item 2 " />
<TextBlock Text="Item 3 " />
<TextBlock Text="Item 4 " />
</UniformGrid>
将产生以下结果:
当第二个 TextBlock
的 Visibility
设置为 Collapsed
时,如下所示: