¿Cómo hago para que una plantilla de datos WPF llene todo el ancho del cuadro de lista?

StackOverflow https://stackoverflow.com/questions/135375

Pregunta

Tengo un ListBox DataTemplate en WPF. Quiero que un elemento esté apretado contra el lado izquierdo del ListBox y otro que esté apretado contra el lado derecho, pero no puedo averiguar cómo hacerlo.

Hasta ahora tengo un Grid con tres columnas, la izquierda y la derecha tienen contenido y el centro es un marcador de posición con el ancho establecido en " * " ;. ¿A dónde me voy mal?

Aquí está el código:

<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>
¿Fue útil?

Solución

También tuve que configurar:

HorizontalContentAlignment="Stretch"

en el ListBox que contiene.

Otros consejos

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

Ok, esto es lo que tienes:

Columna 0: WrapPanel
Columna 1: Nada
Columna 2: ListBox

Parece que quiere WrapPanel en el borde izquierdo, ListBox en el borde derecho y espacio para ocupar lo que queda en el centro.

La forma más sencilla de hacerlo es utilizar un DockPanel , no un Grid .

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

Esto debería dejar un espacio vacío entre el WrapPanel y el ListBox .

Extendiendo la respuesta de Taeke, configurando el ScrollViewer.HorizontalScrollBarVisibility = " Oculto " para un ListBox permite al control secundario tomar el ancho del padre y no mostrar la barra de desplazamiento arriba.

<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 >

El Grid debería ocupar por defecto todo el ancho del ListBox porque el ItemsPanel predeterminado para él es un VirtualizingStackPanel . Supongo que usted no cambió ListBox.ItemsPanel .

Quizás si te deshiciste de la ColumnDefinition del medio (los otros son por defecto " * " ), y coloca HorizontalAlignment = " Izquierda " en su WrapPanel y HorizontalAlignment = " Derecha " en el ListBox para obtener los números de teléfono. Es posible que tenga que modificar ese ListBox un poco para que los números de teléfono estén aún más alineados a la derecha, como crear un DataTemplate para ellos.

Si desea utilizar un Grid , debe cambiar sus ColumnDefinition s para que sean:

    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>

Si no necesita usar un Grid , entonces podría usar un 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>

Observe el TextBlock al final. Cualquier control sin " DockPanel.Dock " definido llenará el espacio restante.

La respuesta de Taeke funciona bien y, según la respuesta de vancutterromney, puede desactivar la barra de desplazamiento horizontal para deshacerse de la falta de coincidencia de tamaño molesto. Sin embargo, si desea lo mejor de ambos mundos: eliminar la barra de desplazamiento cuando no sea necesaria, pero habilitarla automáticamente cuando el ListBox sea demasiado pequeño, puede usar el siguiente conversor:

/// <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();
    }
}

Luego, defínalo en XAML de acuerdo con los valores máximos / mínimos deseados, así como un desplazamiento para lidiar con esa incómoda falta de coincidencia de tamaño de 2 píxeles como se menciona en las otras respuestas:

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

Luego use el convertidor en el enlace de ancho:

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

El método en la respuesta de Taeke fuerza una barra de desplazamiento horizontal. Esto se puede solucionar agregando un convertidor para reducir el ancho de la cuadrícula en el ancho del control de la barra de desplazamiento vertical.

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());
        }
    }
}

Agregue un espacio de nombres al nodo raíz de su XAML.

xmlns:converters="clr-namespace:Converters"

Y actualiza el ancho de la cuadrícula para usar el convertidor.

<Grid.Width>
    <Binding Path="ActualWidth" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type ScrollContentPresenter}}" Converter="{converters:ListBoxItemWidthConverter}"/>
</Grid.Width>
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top