如何使WPF数据模板填满整个列表框的宽度?

66

我在WPF中有一个ListBox DataTemplate。 我想让一个项目紧贴着ListBox的左侧,另一个项目紧贴着右侧,但我不知道如何实现。

到目前为止,我使用了一个Grid,它有三列,左右两列都有内容,中间是一个占位符,宽度设置为“*”。我做错了什么?

以下是代码:

<DataTemplate x:Key="SmallCustomerListItem">
    <Grid HorizontalAlignment="Stretch">
        <Grid.RowDefinitions>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <WrapPanel HorizontalAlignment="Stretch" Margin="0">
            <!--Some content here-->
            <TextBlock Text="{Binding Path=LastName}" TextWrapping="Wrap" FontSize="24"/>
            <TextBlock Text=", " TextWrapping="Wrap" FontSize="24"/>
            <TextBlock Text="{Binding Path=FirstName}" TextWrapping="Wrap" FontSize="24"/>

        </WrapPanel>
        <ListBox ItemsSource="{Binding Path=PhoneNumbers}" Grid.Column="2" d:DesignWidth="100" d:DesignHeight="50"
     Margin="8,0" Background="Transparent" BorderBrush="Transparent" IsHitTestVisible="False" HorizontalAlignment="Stretch"/>
    </Grid>
</DataTemplate>

你能否发布你的XAML代码,以便清楚地了解你目前的进展情况? - 17 of 26
8个回答

151

我还需要设置:

HorizontalContentAlignment="Stretch"

在包含的ListBox上。


5
TA 拍了拍。搜索了一下帮助,这是第一个链接上的。不错 :) - Binary Worrier
@Eric Haskins 我也遇到了Windows Phone的Pivot控件相同的问题(<controls:Pivot.ItemTemplate><DataTemplate /></controls:Pivot.ItemTemplate>),但是我应该把这段代码放在哪里呢?我尝试了一些方法,但是还是无法弄清楚。 - SynerCoder
7
提示Windows Phone用户 - 这样做不起作用;该属性将被ListBox的ItemContainerStyle覆盖。要使其起作用,请参见Gabriel Mongeon在此处的回答:https://dev59.com/XnRA5IYBdhLWcg3wwwzD - Carlos P
这一定是最烦人的UI hack之一。为什么不默认设置呢?Smh. - Matthew S
值得一提的是,如果列表框项有自己的样式,您可能需要将其设置为 ListBoxItem 的样式: - sergeantKK

25
<Grid.Width>
    <Binding Path="ActualWidth" 
             RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type ScrollContentPresenter}}" />
</Grid.Width>

我喜欢这种方法的简单性。它不仅确保小项目填充宽度,还确保那些内容较宽的项目被限制在列表框宽度内。 - Mal Ross
正是我正在寻找的:当太小时填满宽度,当太大时限制宽度。 - Lensflare
1
我刚刚注意到这个解决方案会导致ListBox的宽度始终略小于项的宽度,这会使得项被切割一部分并在列表框底部激活水平滚动条。我通过明确禁用水平滚动来修复了这个问题。 - Lensflare

4

好的,以下是您需要翻译的内容:

第0列:WrapPanel
第1列:无
第2列:ListBox

看起来您想要将WrapPanel放在左侧边缘,将ListBox放在右侧边缘,并让中间占据剩余空间。

实现这个最简单的方法是使用DockPanel而不是Grid

<DockPanel>
    <WrapPanel DockPanel.Dock="Left"></WrapPanel>
    <ListBox DockPanel.Dock="Right"></ListBox>
</DockPanel>

这应该在WrapPanelListBox之间留出空白。


2

扩展Taeke的答案,为ListBox设置ScrollViewer.HorizontalScrollBarVisibility="Hidden"允许子控件使用父控件的宽度,而不显示滚动条。

<ListBox Width="100" ScrollViewer.HorizontalScrollBarVisibility="Hidden">                
    <Label Content="{Binding Path=., Mode=OneWay}" HorizontalContentAlignment="Stretch" Height="30" Margin="-4,0,0,0" BorderThickness="0.5" BorderBrush="Black" FontFamily="Calibri" >
        <Label.Width>
            <Binding Path="Width" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBox}}" />
        </Label.Width>
    </Label>
</ListBox >

1

Grid 默认应该占据整个 ListBox 的宽度,因为默认的 ItemsPanel 是一个 VirtualizingStackPanel。我假设您没有更改 ListBox.ItemsPanel

也许如果您去掉中间的 ColumnDefinition(其他的是默认的 "*"),并在电话号码的 WrapPanel 上放置 HorizontalAlignment="Left",在 ListBox 上放置 HorizontalAlignment="Right"。您可能需要稍微修改一下 ListBox,使电话号码更加右对齐,例如为它们创建一个 DataTemplate


1
如果您想使用一个Grid,那么您需要将您的ColumnDefinition更改为:
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>

如果您不需要使用 Grid,那么可以使用 DockPanel
    <DockPanel>
        <WrapPanel DockPanel.Dock="Left">
            <!--Some content here-->
            <TextBlock Text="{Binding Path=LastName}" TextWrapping="Wrap" FontSize="24"/>
            <TextBlock Text=", " TextWrapping="Wrap" FontSize="24"/>
            <TextBlock Text="{Binding Path=FirstName}" TextWrapping="Wrap" FontSize="24"/>
        </WrapPanel>
        <ListBox DockPanel.Dock="Right" ItemsSource="{Binding Path=PhoneNumbers}" 
 Margin="8,0" Background="Transparent" BorderBrush="Transparent" IsHitTestVisible="False"/>
        <TextBlock />
    </DockPanel>

请注意末尾的TextBlock。任何没有定义"DockPanel.Dock"的控件都将填充剩余空间。

4
这并不意味着数据模板会填满列表框的宽度。 - Kevin Berridge
2
你需要在listbox上使用HorizontalContentAlignment="Stretch",如下所述。 - cbeuker

0
Taeke的答案中的方法会强制出现水平滚动条。可以通过添加转换器来解决此问题,将网格的宽度减去垂直滚动条控件的宽度即可。
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;

namespace Converters
{
    public class ListBoxItemWidthConverter : MarkupExtension, IValueConverter
    {
        private static ListBoxItemWidthConverter _instance;

        #region IValueConverter Members

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return System.Convert.ToInt32(value) - SystemParameters.VerticalScrollBarWidth;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        #endregion

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return _instance ?? (_instance = new ListBoxItemWidthConverter());
        }
    }
}

在你的 XAML 的根节点中添加一个命名空间。
xmlns:converters="clr-namespace:Converters"

并使用转换器更新网格宽度。

<Grid.Width>
    <Binding Path="ActualWidth" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type ScrollContentPresenter}}" Converter="{converters:ListBoxItemWidthConverter}"/>
</Grid.Width>

这应该使用HorizontalContentAlignment完成。 - 15ee8f99-57ff-4f92-890c-b56153
@EdPlunkett 你是指ListBox上的HorizontalContentAlignment,就像Eric Haskins所接受的答案中所述吗?如果是这样,我无法让它起作用,这就是为什么我转而使用这种更糟糕的方法的原因。 - Kevin Hilt
哎呀,这个页面上甚至没有正确的通用答案。它在这个答案里:https://dev59.com/XnRA5IYBdhLWcg3wwwzD#2924249 -- 在列表框内的列表框控件上设置HorizontalContentAlignment="Stretch",你可以通过ListBox.ItemContainerStyle来实现。 - 15ee8f99-57ff-4f92-890c-b56153
@EdPlunkett 我看到了那个答案,但除非我一直忽略了某些简单的东西,否则它对我也不起作用。这可能是因为我正在使用数据模板。 - Kevin Hilt

0
Taeke的答案很好,根据vancutterromney的答案,您可以禁用水平滚动条以消除烦人的大小不匹配。但是,如果您确实想要兼顾两全--在不需要滚动条时删除它,但当ListBox变得太小时自动启用它,您可以使用以下转换器:
/// <summary>
/// Value converter that adjusts the value of a double according to min and max limiting values, as well as an offset. These values are set by object configuration, handled in XAML resource definition.
/// </summary>
[ValueConversion(typeof(double), typeof(double))]
public sealed class DoubleLimiterConverter : IValueConverter
{
    /// <summary>
    /// Minimum value, if set. If not set, there is no minimum limit.
    /// </summary>
    public double? Min { get; set; }

    /// <summary>
    /// Maximum value, if set. If not set, there is no minimum limit.
    /// </summary>
    public double? Max { get; set; }

    /// <summary>
    /// Offset value to be applied after the limiting is done.
    /// </summary>
    public double Offset { get; set; }

    public static double _defaultFailureValue = 0;

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == null || !(value is double))
            return _defaultFailureValue;

        double dValue = (double)value;
        double minimum = Min.HasValue ? Min.Value : double.NegativeInfinity;
        double maximum = Max.HasValue ? Max.Value : double.PositiveInfinity;
        double retVal = dValue.LimitToRange(minimum, maximum) + Offset;
        return retVal;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

然后根据所需的最大/最小值以及偏移量在XAML中定义它,以处理其他答案中提到的恼人的2像素大小不匹配问题:

<ListBox.Resources>
    <con:DoubleLimiterConverter x:Key="conDoubleLimiter" Min="450" Offset="-2"/>
</ListBox.Resources>

然后在宽度绑定中使用转换器:

<Grid.Width>
    <Binding Path="ActualWidth" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type ScrollContentPresenter}}" Converter="{StaticResource conDoubleLimiter}"  />
</Grid.Width>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接