J'ai un ListBox
DataTemplate
dans WPF. Je veux qu'un élément soit serré contre le côté gauche du ListBox
et un autre élément soit serré contre le côté droit, mais je ne sais pas comment faire cela.
Jusqu'à présent, j'ai un Grid
avec trois colonnes, les gauche et droite ont du contenu et le centre est un espace réservé avec sa largeur définie sur "*". Où vais-je mal?
Voici le code:
<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>
J'ai également dû définir:
HorizontalContentAlignment="Stretch"
sur le contenant ListBox
.
<Grid.Width>
<Binding Path="ActualWidth"
RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type ScrollContentPresenter}}" />
</Grid.Width>
Ok, voici ce que vous avez:
Colonne 0: WrapPanel
Colonne 1: Rien
Colonne 2: ListBox
Il semble que vous souhaitiez WrapPanel
sur le bord gauche, ListBox
sur le bord droit, et de l'espace pour occuper ce qui reste au milieu.
La manière la plus simple de le faire est en fait d'utiliser un DockPanel
, pas un Grid
.
<DockPanel>
<WrapPanel DockPanel.Dock="Left"></WrapPanel>
<ListBox DockPanel.Dock="Right"></ListBox>
</DockPanel>
Cela devrait laisser un espace vide entre le WrapPanel
et le ListBox
.
Extension de la réponse de Taeke, définition du ScrollViewer.HorizontalScrollBarVisibility="Hidden"
pour un ListBox
permet au contrôle enfant de prendre la largeur du parent et de ne pas afficher la barre de défilement.
<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 >
Le Grid
doit par défaut occuper toute la largeur du ListBox
car le ItemsPanel
par défaut est pour lui un VirtualizingStackPanel
. Je suppose que vous avez pas changé ListBox.ItemsPanel
.
Peut-être que si vous vous êtes débarrassé du milieu ColumnDefinition
(les autres sont par défaut "*"
), et met HorizontalAlignment="Left"
sur votre WrapPanel
et HorizontalAlignment="Right"
sur le ListBox
pour les numéros de téléphone. Vous devrez peut-être modifier légèrement ce ListBox
pour obtenir des numéros de téléphone encore plus alignés à droite, par exemple en créant un DataTemplate
pour eux.
Si vous souhaitez utiliser un Grid
, vous devez changer votre ColumnDefinition
s pour qu'il soit:
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
Si vous n'avez pas besoin d'utiliser un Grid
, vous pouvez utiliser 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>
Remarquez le TextBlock
à la fin. Tout contrôle sans "DockPanel.Dock"
defined remplira l'espace restant.
La méthode dans la réponse de Taeke force une barre de défilement horizontale. Cela peut être résolu en ajoutant un convertisseur pour réduire la largeur de la grille par la largeur du contrôle de barre de défilement verticale.
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());
}
}
}
Ajoutez un espace de noms au nœud racine de votre XAML.
xmlns:converters="clr-namespace:Converters"
Et mettez à jour la largeur de la grille pour utiliser le convertisseur.
<Grid.Width>
<Binding Path="ActualWidth" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type ScrollContentPresenter}}" Converter="{converters:ListBoxItemWidthConverter}"/>
</Grid.Width>
La réponse de Taeke fonctionne bien, et selon la réponse de vancutterromney, vous pouvez désactiver la barre de défilement horizontale pour vous débarrasser de l'inadéquation de taille gênante. Cependant, si vous voulez le meilleur des deux mondes - pour supprimer la barre de défilement lorsqu'elle n'est pas nécessaire, mais l'activer automatiquement lorsque la ListBox devient trop petite, vous pouvez utiliser le convertisseur suivant:
/// <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();
}
}
Ensuite, définissez-le en XAML en fonction des valeurs max/min souhaitées, ainsi qu'un décalage pour faire face à cette incompatibilité de taille de 2 pixels gênante, comme mentionné dans les autres réponses:
<ListBox.Resources>
<con:DoubleLimiterConverter x:Key="conDoubleLimiter" Min="450" Offset="-2"/>
</ListBox.Resources>
Utilisez ensuite le convertisseur dans la liaison Largeur:
<Grid.Width>
<Binding Path="ActualWidth" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type ScrollContentPresenter}}" Converter="{StaticResource conDoubleLimiter}" />
</Grid.Width>