我有一个 ObservableCollection,其中包含 ViewModel,而 ViewModel 又定义了我的按钮定义。
我已经研究了几个小时,阅读了一篇又一篇文章,但毫无结果。我尝试过使用列表框,这是我最接近的。我的按钮正在水平构建,但假设我显示 3 个按钮,我希望一个显示在左侧,一个显示在中间,一个显示在右侧。
<ListBox Grid.Row="2" ItemsSource="{Binding Links}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<StackPanel Background="Beige" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Button Grid.Column="{Binding Column}"
Grid.Row="0"
Width="90"
Height="90">
<ContentControl>
<StackPanel Orientation="Vertical">
<Image Source="{Binding Image}" Width="36" Height="36"
Margin="5" Stretch="UniformToFill"
HorizontalAlignment="Center"/>
<TextBlock Text="{Binding Description}"
Foreground="#0F558E"
FontSize="18"
HorizontalAlignment="Center"/>
</StackPanel>
</ContentControl>
</Button>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
如您所见,我使用 ViewModel 中的属性动态设置列,但我不再有网格,因为我无法让它工作,但理想情况下我想使用网格,以便我可以在哪一列设置按钮。
当我使用 StackPanel 时,它可以工作,但按钮彼此相邻,而不是在屏幕的整个宽度上均匀分割。
我已经使用
ItemsControl
和网格完成了与上面类似的操作,我可以看到每个按钮都被添加,但它们在第 0 行、第 0 列中相互重叠。如果我将行绑定到 Column 属性进行测试目的,它正确构建它,但我的按钮显示在不同的行上,这不是我想要的。我希望每个按钮水平对齐。
我怎样才能实现这个目标?我知道我可以硬编码 3 个按钮,只更改它们的图标和文本,并通过将相关按钮作为绑定参数传递来处理相关代码,但理想情况下,我想在网格中动态构建按钮并使用列定位它们.
请注意,列数将是固定的,即 3,因为我永远不会拥有比这更多的列数。
谢谢。
但理想情况下我想使用网格,以便我可以指定在其中 设置按钮的栏。
在任何 Xaml 变体中,为什么不只使用
Grid
,如下所示,其中 Grid
设置为 消耗所有水平空间?
然后将网格的中心列设置为星形大小,并在按钮 1 和按钮 3 之后将剩余的空间消耗,按钮 1 和按钮 3 会自动调整大小到自己的空间中:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0"/>
<Button Grid.Column="1" HorizontalAlignment="Center"/>
<Button Grid.Column="2"/>
</Grid>
如果失败,请将按钮 1 的
HorizontalAlignment
设置为 Left
,将按钮 3 的设置为 Right
。
顺便说一句,列表框可能不会拉伸到屏幕的整个水平尺寸。查看我对 WP8 尺寸问题的回答: ListBoxItem 水平内容对齐。
我最终在网上找到的一篇文章中找到了问题的答案。
您可以在此处查看:在 XAML 中使用带有 ItemsControl 的网格
简而言之,您需要对 itemsControl 进行子类化,并且需要覆盖 GetContainerForItemOverride 方法,该方法将负责将 ItemTemplate 的“数据”复制到 ContentPresenter。在本例中,是行和列,但对于我的要求,它只是列,因为我的行将始终为 0。
如果您不想查看完整的文章,它解决了使用 ItemsControl 在网格中水平设置控件的问题,那么这里是代码的核心部分,但请注意该文章也负责动态创建行和列,我'我对我的项目不感兴趣。
public class GridAwareItemsControl : ItemsControl
{
protected override DependencyObject GetContainerForItemOverride()
{
ContentPresenter container = (ContentPresenter)base.GetContainerForItemOverride();
if (ItemTemplate == null)
{
return container;
}
FrameworkElement content = (FrameworkElement)ItemTemplate.LoadContent();
BindingExpression rowBinding = content.GetBindingExpression(Grid.RowProperty);
BindingExpression columnBinding = content.GetBindingExpression(Grid.ColumnProperty);
if (rowBinding != null)
{
container.SetBinding(Grid.RowProperty, rowBinding.ParentBinding);
}
if (columnBinding != null)
{
container.SetBinding(Grid.ColumnProperty, columnBinding.ParentBinding);
}
return container;
}
}
最终的 xaml 如下所示:
<controls:GridAwareItemsControl ItemsSource="{Binding Links}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid Background="Pink">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button
Grid.Column="{Binding Column}"
Grid.Row="0"
Width="120" Height="120">
<ContentControl>
<StackPanel Orientation="Vertical">
<Image Source="{Binding Image}" Width="36" Height="36" Margin="5"
Stretch="UniformToFill" HorizontalAlignment="Center"/>
<TextBlock Text="{Binding Description}" Foreground="#0F558E"
FontSize="18" HorizontalAlignment="Center"/>
</StackPanel>
</ContentControl>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</controls:GridAwareItemsControl>
使用新控件后,我的按钮就被正确放置在网格内,因此当网格通过 ColumnDefinitions 处理这些问题时,它们的间距是均匀的。
如果有人知道如何实现相同的目标,而无需创建新的控件并覆盖该方法(换句话说,纯 XAML),请发布它,因为我非常有兴趣了解如何做到这一点。
感谢 Robert Garfoot 分享这段出色的代码!
PS:请注意,我简化了我的 xaml,以便创建按钮上没有任何样式的测试示例,因此如果您尝试基于此示例,这些示例将相当大。
更新:
小拼写错误更正,但我的网格列定义被定义为
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
但正如@OmegaMan建议的那样,为了均匀分布,它应该被定义为
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
我能够通过网格内的堆栈面板来完成此任务,完全避免使用列。 如果将 stackepanel 的 HorizontalAlignment 设置为“center”,它将在网格内居中,并随着按钮的添加而增长,仍然保持在网格内居中。 那么按钮等距的问题就只是边距的问题了:
<Grid>
<StackPanel
VerticalAlignment="Stretch"
HorizontalAlignment="Center"
Orientation="Horizontal"
>
<Button HorizontalAlignment="Center" Content="Add" Width="104" Height="32" Margin="24,0"/>
<Button HorizontalAlignment="Center" Content="Edit" Width="104" Height="32" Margin="24,0"/>
<Button HorizontalAlignment="Center" Content="Remove" Width="104" Height="32" Margin="24,0"/>
</StackPanel></Grid>