2009-08-13 7 views
21

Ich versuche, ein einfaches WPF-Lernprojekt zu schreiben, das eine Reihe von Schaltflächen in einem veränderbaren Hauptfenster erstellt. Pro Eintrag in einer Auflistung muss ein Button vorhanden sein, und der Inhalt dieser Auflistung kann während der Laufzeit variieren. Ich möchte, dass die Tasten das gesamte Fenster ausfüllen (z. B. 1 Taste @ 100% Breite, 2 Tasten @ 50% Breite, 3 Tasten @ 33% Breite usw. alle auf 100% Höhe). Eine vereinfachte Version von dem, was ich bisher geschrieben habe, ist:Stretching-Steuerelemente zum Füllen von ItemsControl

<ItemsControl x:Name="itemscontrolButtons" ItemsSource="{Binding}"> 
    <ItemsControl.ItemTemplate> 
    <DataTemplate> 
     <Button Tag="{Binding}"> 
     <TextBlock Text="{Binding}" /> 
     </Button> 
    </DataTemplate> 
    </ItemsControl.ItemTemplate> 
    <ItemsControl.ItemsPanel> 
    <ItemsPanelTemplate> 
     <StackPanel Orientation="Horizontal" /> 
    </ItemsPanelTemplate> 
    </ItemsControl.ItemsPanel> 
</ItemsControl> 
...  
List<string> listButtonTexts = new List<string>() { "Button1", "Button2" }; 
... 
itemscontrolButtons.DataContext = listButtonTexts; 

Dies ergibt:

alt text

ich nicht in der Lage gewesen, die Tasten zu machen strecken die Breite und meine Versuche passen einen Grid statt StackPanel zu verwenden waren fruchtlos. Als eine optionale Verbesserung würde ich mich über Vorschläge freuen, wie man es so einstellen kann, dass es, wenn es so viele Knöpfe gibt, dass sie nicht richtig in eine Linie passen oder schmaler als ein Schwellenwert sind, in eine neue Linie eingewickelt wird (wodurch die Knopfhöhen halbiert werden, wenn von 1 bis 2 Reihen gegangen wird).

Ich möchte betonen, dass ich würde gerne wissen, wie dies das WPF Weg zu tun. Mir ist klar, dass ich Fenstergrößenänderungsanforderungen und Größenänderungen der Steuerelemente explizit ausführen kann, und das hätte ich mit MFC oder WinForms gemacht, aber aus dem, was ich gelesen habe, ist nicht, wie solche Dinge mit WPF getan werden sollten.

Antwort

48

Ersetzen der Panels Artikel Steuerung mit einem Uniform, wird diese Größe alle Kinder so alles paßt genau:

<ItemsControl> 
    <ItemsControl.ItemsPanel> 
     <ItemsPanelTemplate> 
      <UniformGrid Rows="1"/> 
     </ItemsPanelTemplate> 
    </ItemsControl.ItemsPanel> 
</ItemsControl> 
+0

Das war genau das, was ich für mein primäres Ziel brauchte, danke. – Gregyski

+0

Dank Nirs Antwort konnte ich das optionale Kriterium erfüllen. Details sind in der Antwort, die ich auf diese Frage gepostet habe. Es wurde auf Meta empfohlen, dass ich diese Situation auf diese Weise behandle. http://meta.stackexchange.com/questions/14306/my-improved-answer-based-on-anothers-accepted-answer-for-my-own-question – Gregyski

+0

Vielen Dank! Dies ist genau das, was mir fehlte, wenn ich Elemente und ihr Panel gleichmäßig in einer Listbox platzierte. –

0
<!-- width must be explicitly set for this example --> 
<StackPanel 
    Name="MyStack" 
    Orientation="Horizontal" 
    Width="250" 
    Load="Window_Loaded"> 
    <Button/> 
    <Button/> 
    <Button/> 
</StackPanel> 

public void Window_Loaded(object sender, RoutedEventArgs e) 
{ 
    UIElementCollection elements = MyStack.Children; 
    int count = elements.Count; 

    foreach (UIElement element in elements) 
    { 
     if (element is Button) 
      ((Button)element).Width = MyStack.Width/count; 
    } 
} 
+0

Ich habe meinen Beitrag erst 20 Minuten vor der Antwort bearbeitet, um zu erklären, dass ich explizit die Breite einstellen wollte. Es tut mir leid, dass ich gedacht hätte, diese Anforderung zunächst aufzunehmen. Das explizite Verwalten der Dimensionen wie dieser (und das Bearbeiten von Größenänderungsnachrichten) kann für meine sekundäre Anforderung erforderlich sein, wenn WrapPanel verwendet werden soll. Vielen Dank. – Gregyski

2

ich in der Lage war, auf Nir Antwort zu bauen meine zu erfüllen optionales Kriterium, das WPF-verwaltetes Stretching mit mehreren Zeilen zulässt.

Zuerst müssen Sie in der Lage sein, die Eigenschaften der Vorlage UniformGrid zu ändern. Um dies zu tun, müssen Sie einen Verweis darauf speichern. Es gibt multiple methods for accomplishing this. Ich entschied mich, das Loaded Ereignis für das UniformGrid zu behandeln und eine Referenz zu dieser Zeit zu speichern.

<ItemsControl ItemsSource="{Binding}"> 
    <ItemsControl.ItemsPanel> 
    <ItemsPanelTemplate> 
     <UniformGrid Rows="1" Loaded="UniformGrid_Loaded" /> 
    </ItemsPanelTemplate> 
    </ItemsControl.ItemsPanel> 
</ItemsControl> 

Und in der Code-behind:

UniformGrid uniformgridButtons; 
private void UniformGrid_Loaded(object sender, RoutedEventArgs e) 
{ 
    uniformgridButtons = sender as UniformGrid; 
} 

Dann behandeln die Ereignis Sizechanged und die Zeilen Parameter einstellen nach unabhängig von Kriterien, die Sie wünschen.

private void Window_SizeChanged(object sender, SizeChangedEventArgs e) 
{ 
    if ((uniformgridButtons != null) && (this.Width > 0)) 
    { 
    // Set row count according to your requirements 
    uniformgridButtons.Rows = (int)(1 + ((this.MinWidth * iButtonCount)/this.Width)); 
    } 
} 

So ermöglicht diese WPF das Strecken wie in Nir Antwort zu verwalten, sondern ermöglicht es Ihnen ausdrücklich die Anzahl der Zeilen anzupassen.Der obige Code gibt dieses Ergebnis:

alt text

(Animated Probe here)


-Update 2009.08.28:

ich meine Lernanwendung so eingestellt wird, dass die ItemsControl wurde in einer DataTemplate in einer separaten ResourceDictionary. Ereignisse wie Loaded können jedoch nicht in einer externen ResourceDictionary XAML-Datei behandelt werden, da es (normalerweise) keinen Code-Behind hat. Daher musste ich den Verweis auf UniformGrid mithilfe einer anderen Technik zwischenspeichern.

Ich stattdessen Alan Haigh's FindItemsPanel method (10th reply). Zuerst habe ich ersetzt die ItemsControl mit:

<ContentPresenter x:Name="contentpresenterButtons" Content="{Binding obscolButtons}" /> 

Da ist in meinem ResourceDictionary ich die ItemsControl im DataTemplate setzen:

<DataTemplate DataType="{x:Type local:Buttons}"> 
    <ItemsControl ... 
</DataTemplate> 

Schließlich habe ich den Verweis auf die Templat-UniformGrid als so gespeichert:

private void Window_Loaded(object sender, RoutedEventArgs e) { 
    uniformgridButtons = FindItemsPanel<UniformGrid>(contentpresenterButtons); 
    // Also call the code in Window_SizeChanged which I put in another method 
} 

Dies funktioniert genauso wie meine vorherige Lösung, aber ohne einen Ereignishandler in der ResourceDictionary.

Verwandte Themen