使用 Xaml 水平对齐按钮并均匀分布

问题描述 投票:0回答:3

我有一个 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,因为我永远不会拥有比这更多的列数。

谢谢。

wpf xaml windows-phone-7 windows-phone-8
3个回答
14
投票

但理想情况下我想使用网格,以便我可以指定在其中 设置按钮的栏。

在任何 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 水平内容对齐


2
投票

我最终在网上找到的一篇文章中找到了问题的答案。

您可以在此处查看:在 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>

0
投票

我能够通过网格内的堆栈面板来完成此任务,完全避免使用列。 如果将 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>
© www.soinside.com 2019 - 2024. All rights reserved.