Comment créer un modèle de données WPF sur toute la largeur de la liste?
-
02-07-2019 - |
Question
J'ai un ListBox
DataTemplate
dans WPF. Je souhaite 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 vois pas comment faire.
Jusqu'à présent, j'ai une grille
à trois colonnes. Les colonnes gauche et droite ont un contenu et le centre est un espace réservé dont la largeur est 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>
La solution
J'ai également dû définir:
HorizontalContentAlignment="Stretch"
sur le ListBox contenant
.
Autres conseils
<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
On dirait que vous voulez WrapPanel
sur le bord gauche, ListBox
sur le bord droit et un espace pour occuper ce qui reste au milieu.
Le moyen le plus simple de procéder consiste à utiliser un DockPanel
et non une Grille
.
<DockPanel>
<WrapPanel DockPanel.Dock="Left"></WrapPanel>
<ListBox DockPanel.Dock="Right"></ListBox>
</DockPanel>
Cela devrait laisser un espace vide entre WrapPanel
et ListBox
.
Étendre la réponse de Taeke en définissant le ScrollViewer.HorizontalScrollBarVisibility = "Caché &
" pour un ListBox
permet au contrôle enfant de prendre la largeur du parent sans afficher la barre de défilement. up.
<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 >
La grille
doit par défaut occuper toute la largeur de la ListBox
car le ItemsPanel
par défaut est un VirtualizingStackPanel
. Je suppose que vous n'avez pas changé ListBox.ItemsPanel
.
Peut-être si vous vous êtes débarrassé du ColumnDefinition
du milieu (les autres sont par défaut "*"), et insérez
HorizontalAlignment = "Gauche". / code> sur votre
WrapPanel
et HorizontalAlignment = "Droit"
sur ListBox
pour les numéros de téléphone. Vous devrez peut-être modifier légèrement ListBox
pour que les numéros de téléphone soient encore plus alignés à droite, par exemple pour créer un DataTemplate
pour eux.
Si vous souhaitez utiliser une grille
, vous devez modifier votre ColumnDefinition
pour qu'il soit:
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
Si vous n'avez pas besoin d'utiliser une grille
, 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>
Notez le TextBlock
à la fin. Tout contrôle sans "DockPanel.Dock"
défini remplira l’espace restant.
La réponse de Taeke fonctionne bien et, conformément à la réponse de vancutterromney, vous pouvez désactiver la barre de défilement horizontale pour vous débarrasser de l’incompatibilité de taille gênante. Toutefois, si vous souhaitez que le meilleur des deux mondes - supprime la barre de défilement lorsqu'elle n'est pas utilisée, mais l'active 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 traiter cette incohérence gênante de taille de 2 pixels comme indiqué 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 de largeur:
<Grid.Width>
<Binding Path="ActualWidth" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type ScrollContentPresenter}}" Converter="{StaticResource conDoubleLimiter}" />
</Grid.Width>
La méthode employée dans la réponse de Taeke impose une barre de défilement horizontale. Ce problème peut être résolu en ajoutant un convertisseur permettant de réduire la largeur de la grille par la largeur du contrôle de la 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 nom 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>