У меня ListBoxDataTemplate в WPF. Я хочу, чтобы один элемент плотно прилегал к левой стороне 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>





Grid по умолчанию должен занимать всю ширину ListBox, потому что ItemsPanel по умолчанию для него - VirtualizingStackPanel. Я предполагаю, что у вас нет поменял ListBox.ItemsPanel.
Возможно, если вы избавитесь от среднего ColumnDefinition (остальные по умолчанию - "*") и поместите HorizontalAlignment = "Left" на свой WrapPanel и HorizontalAlignment = "Right" на ListBox для телефонных номеров. Возможно, вам придется немного изменить этот ListBox, чтобы номера телефонов были еще более выровнены по правому краю, например, создав для них DataTemplate.
Хорошо, вот что у вас есть:
Столбец 0: WrapPanel
.
Столбец 1: Nothing
.
Столбец 2: ListBox.
Похоже, вам нужно WrapPanel на левом краю, ListBox на правом краю и место, чтобы занять то, что осталось посередине.
Самый простой способ сделать это - использовать DockPanel, а не Grid.
<DockPanel>
<WrapPanel DockPanel.Dock = "Left"></WrapPanel>
<ListBox DockPanel.Dock = "Right"></ListBox>
</DockPanel>
Это должно оставить пустое место между WrapPanel и ListBox.
Если вы хотите использовать 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" заполнит оставшееся пространство.
Это НЕ заставляет табличку данных заполнять всю ширину списка.
Вам необходимо использовать HorizontalContentAlignment = "Stretch" в списке, как указано ниже.
Еще мне пришлось установить:
HorizontalContentAlignment = "Stretch"
на содержащем ListBox.
Ta pal. Погуглил за помощью, и это было по первой ссылке. Хороший :)
@Eric Haskins. У меня такая же проблема с элементом управления Pivot для Windows Phone (<controls:Pivot.ItemTemplate><DataTemplate /></controls:Pivot.ItemTemplate>). Но где мне поместить этот код? Я пробовал, но не мог понять.
Примечание для пользователей Windows Phone - это не сработает; свойство переопределяется ItemContainerStyle ListBox. Чтобы заставить его работать, см. Ответ Габриэля Монжеона здесь: stackoverflow.com/questions/838828/…
Это должен быть один из самых раздражающих взломов пользовательского интерфейса. Почему это не установлено по умолчанию? Smh.
Также стоит упомянуть, что вам может потребоваться установить это в стиле ListBoxItem, если он есть: <ListView.ItemContainerStyle> <Style TargetType = "ListViewItem"> <Setter Property = "HorizontalContentAlignment" Value = "Stretch" /> </Style> </ListView.ItemContainerStyle>
<Grid.Width>
<Binding Path = "ActualWidth"
RelativeSource = "{RelativeSource Mode=FindAncestor, AncestorType = {x:Type ScrollContentPresenter}}" />
</Grid.Width>
Мне нравится простота этого подхода. Он не только гарантирует, что маленькие элементы заполняют всю ширину, но и гарантирует, что элементы, содержимое которых предпочло бы быть очень широкими, ограничены шириной списка.
Именно то, что я искал: заполнение ширины, когда она слишком мала, и ограничение ширины, когда она слишком велика.
Я только что заметил, что это решение приводит к тому, что ширина ListBox постоянно немного меньше ширины элемента, что немного обрезает элементы и активирует горизонтальную полосу прокрутки внизу списка. Я исправил это, явно отключив горизонтальную прокрутку.
Расширяя ответ Тэке, установка ScrollViewer.HorizontalScrollBarVisibility = "Hidden" для ListBox позволяет дочернему элементу управления принимать ширину родителя и не отображать полосу прокрутки.
<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 >
Ответ 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>
Метод в ответе Тэке заставляет горизонтальную полосу прокрутки. Это можно исправить, добавив конвертер, чтобы уменьшить ширину сетки на ширину элемента управления вертикальной полосы прокрутки.
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.
@EdPlunkett Вы имеете в виду HorizontalContentAlignment в ListBox, как в принятом ответе Эрика Хаскинса? Если так, я не мог заставить это работать, поэтому я перешел на этот худший метод.
OMG, правильного общего ответа даже нет на этой странице. Вот этот ответ: stackoverflow.com/a/2924249/424129 - установите HorizontalContentAlignment = "Stretch" в список. предмет контролирует в списке, что вы делаете через ListBox.ItemContainerStyle.
@EdPlunkett Я наткнулся на этот ответ, но если я не упущу из виду что-то простое, он также не работает для меня. Это может быть потому, что я использую шаблон данных.
Можете ли вы опубликовать свой XAML, чтобы было понятно, что у вас есть на данный момент?