2015-03-11 17 views
5

Ist es möglich, die Breite aller TextBlocks im WrapPanel auf die Größe des größten TextBlocks innerhalb des WrapPanel anzupassen? Das Endergebnis sollte sein, dass die Breite des Steuerelements, das "einige Daten" enthält, dieselbe Breite wie das Steuerelement hat, das "noch mehr Daten als zuvor" enthält. Ich habe meinen anfänglichen Code als Ausgangspunkt verwendet. Ich habe Strings als Beispiel verwendet, aber die Sammlungsdaten und die Vorlage könnten alles sein, so dass ich mich nicht auf die Länge der Zeichenkette verlassen kann.WPF: Synchronisieren Sie die Breite aller Elemente in ItemsControl

<Window x:Class="WpfApplication1.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:System="clr-namespace:System;assembly=mscorlib" 
     xmlns:Collections="clr-namespace:System.Collections;assembly=mscorlib" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindow" Height="350" Width="525"> 
    <Window.Resources> 
     <Collections:ArrayList x:Key="data"> 
      <System:String>Some data</System:String> 
      <System:String>Some more data</System:String> 
      <System:String>Even more data than before</System:String> 
     </Collections:ArrayList> 
    </Window.Resources> 
    <ItemsControl ItemsSource="{StaticResource data}"> 
     <ItemsControl.ItemsPanel> 
      <ItemsPanelTemplate> 
       <WrapPanel></WrapPanel> 
      </ItemsPanelTemplate> 
     </ItemsControl.ItemsPanel> 
     <ItemsControl.ItemTemplate> 
      <DataTemplate> 
       <Border Margin="5" BorderThickness="1" BorderBrush="Black"> 
        <TextBlock Text="{Binding}"></TextBlock> 
       </Border> 
      </DataTemplate> 
     </ItemsControl.ItemTemplate> 
    </ItemsControl> 
</Window> 

Und ein Bild des gewünschten Ausgangs:

Desired output

Antwort

10

eine gemeinsame Größe Gitter verwenden:

<ItemsControl ItemsSource="{StaticResource data}" Grid.IsSharedSizeScope="True"> 
     <ItemsControl.ItemsPanel> 
      <ItemsPanelTemplate> 
       <WrapPanel></WrapPanel> 
      </ItemsPanelTemplate> 
     </ItemsControl.ItemsPanel> 
     <ItemsControl.ItemTemplate> 
      <DataTemplate> 
      <Grid> 
       <Grid.ColumnDefinitions> 
        <ColumnDefinition Width="Auto" SharedSizeGroup="ColumnSize" /> 
       </Grid.ColumnDefinitions> 
       <Border Margin="5" BorderThickness="1" BorderBrush="Black"> 
        <TextBlock Text="{Binding}"></TextBlock> 
       </Border> 
      </Grid> 
      </DataTemplate> 
     </ItemsControl.ItemTemplate> 
    </ItemsControl> 

Alle Spalten die gleiche Breite sind garantiert, da sie eine Größe Gruppe teilen. Da sie automatisch skaliert werden, werden sie auch auf die größte Instanz des Inhalts des Rasters skaliert.

+0

Ziemlich elegante Lösung. Dies löst mein unmittelbares Problem recht gut, ohne dass ich irgendeinen benutzerdefinierten Code schreiben muss. Gibt es irgendwelche Einschränkungen, die bei dieser Antwort sofort in den Sinn kommen? – mortalapeman

4

Sie haben eine eigene benutzerdefinierte Version eines WrapPanel erstellen, die von WrapPanel abgeleitet ist zu erreichen, was Sie wollen. Normalerweise für eine benutzerdefinierte Panel müssen Sie folgende zwei Methoden außer Kraft setzen:

  • MeasureOverride
  • ArrangeOverride

In Ihrem Fall Sie measueoverride müssen, wo man über die Elemente iteriert werden und herausfinden, welche ist die größte und verwenden Sie dann die gleiche Größe für alle Elemente in arrayoverride.

Einige weitere Informationen darüber, wie eine benutzerdefinierte Panel erstellen: http://www.wpftutorial.net/CustomLayoutPanel.html

+0

Eine benutzerdefinierte Panel mit den leicht modifizierten Eigenschaften der Wrap-Panel war, was ich dachte, würde ich mich damit zufrieden geben, aber das ist, bis ich die Antwort sah @TobyCrawford hat vorgeschlagen. – mortalapeman

2

Sie können Horizontalcontentalignment = Stretch hinzufügen und Uniform in Itemspanel hinzufügen.

<Window.Resources> 
    <Collections:ArrayList x:Key="data"> 
     <System:String>Some data</System:String> 
     <System:String>Some more data</System:String> 
     <System:String>Even more data than before</System:String> 
    </Collections:ArrayList> 
</Window.Resources> 
<ListBox ItemsSource="{StaticResource data}" HorizontalContentAlignment="Stretch"> 
    <ListBox.ItemsPanel> 
     <ItemsPanelTemplate> 
      <UniformGrid IsItemsHost="True"></UniformGrid> 
     </ItemsPanelTemplate> 
    </ListBox.ItemsPanel> 
    <ListBox.ItemTemplate> 
     <DataTemplate> 
      <Border Margin="5" BorderThickness="1" BorderBrush="Black" > 
       <TextBlock Text="{Binding}" Width="{Binding 
      RelativeSource={RelativeSource TemplatedParent}, 
      Path=Width}"></TextBlock> 
      </Border> 
     </DataTemplate> 
    </ListBox.ItemTemplate> 
</ListBox> 
+0

UniformGrid, die Lösung für mehr Probleme, als Sie erwarten würden. – Will

+0

IMHO entspricht nicht den Anforderungen des OP. Was ist, wenn er die Größe des Fensters ändert? –

+0

Ich hatte bis zu diesem Post nichts über UniformGrid gewusst, danke dafür. Leider muss ich @PhilipStuyck zustimmen, da dieses nicht die gleichen Größenmerkmale wie das Wrap-Panel hat. – mortalapeman

1

Dies ist eine Alternative zu Philip Stuycks ​​benutzerdefinierten Panel-Vorschlag, den Sie vielleicht einfacher für Ihr spezielles Szenario finden. (Es ist zugegebenermaßen ein bisschen ein Hack.)

Sie können die Länge eines Strings mit der Klasse FormattedText berechnen. Sie können also über Ihre Strings iterieren und die maximale Länge berechnen (vorausgesetzt, Sie kennen die Schriftfamilie und -größe). Dann binden Sie einfach die Breite Ihrer Textblöcke an die maximale Breite. Ich würde auf der übergeordneten Ebene des Breitenwert in einer einzigen Eigenschaft speichern, dann eine Relative mit Bindung:

<TextBlock Text="{Binding}" 
      Width="{Binding RelativeSource={RelativeSource AncestorType=ItemsControl}, 
          Path=MaximumWidth}}" 
     /> 

(Ein Nachteil dieses Ansatzes besteht darin, dass, wenn die Elemente Sammlung Änderungen, werden Sie MaximumWidth neu zu berechnen haben.)

+0

Leider kann ich die Länge der Saite nicht berechnen, da mein eigentliches Problem nichts mit Saiten zu tun hat, sondern nur als Beispiel. Ich hatte eine Art von Ahnenbindung in Betracht gezogen, aber ich hoffte auf eine elegantere Lösung. Es ist jedoch immer noch schön zu wissen, dass es so gemacht werden kann. – mortalapeman

Verwandte Themen