Stretching controlli per riempire ItemsControl
-
13-09-2019 - |
Domanda
Sto tentando di scrivere un semplice progetto di apprendimento WPF che crea una serie di pulsanti all'interno di una finestra principale ridimensionabile. Ci deve essere uno Button
per ogni voce in una collezione, e il contenuto di tale raccolta può variare in fase di esecuzione. Voglio i pulsanti per riempire l'intera finestra (larghezza per esempio 1 pulsante @ 100%, 2 tasti @ larghezza 50%, 3 pulsanti @ larghezza 33%, ecc tutti al 100% altezza). Una versione semplificata di quello che ho scritto finora è:
<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;
Il risultato è questo:
Sono stato in grado di rendere i pulsanti si estendono in base alla larghezza ed i miei tentativi di usare un Grid
invece di StackPanel
erano inutili.
Poi, come un miglioramento optional, apprezzerei suggerimenti su come regolarlo in modo tale che se ci sono così tanti pulsanti che non possono adattano correttamente su una linea o che sono più stretto di una soglia, si avvolgono su una nuova linea ( dimezzando così i pulsanti altezze se vanno da 1 a 2 righe).
mi piacerebbe sottolineare che mi piacerebbe sapere come fare questo il modo WPF . Mi rendo conto che posso consumare finestra ridimensionare i messaggi e ridimensionare i controlli in modo esplicito, ed è così che avrei fatto con MFC o WinForms, ma da quello che ho letto che non è come queste cose dovrebbero essere fatte con WPF.
Soluzione
Sostituire il pannello di controllo della voce con un UniformGrid, questa dimensione volontà tutti i bambini in modo tutto si adatta esattamente:
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="1"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Altri suggerimenti
Sono stato in grado di costruire su risposta di Nir per compiere il mio criterio facoltativo che consente la WPF gestiti estende con più righe.
Per prima cosa è necessario essere in grado di alterare le proprietà del modello UniformGrid . Per fare ciò è necessario memorizzare un riferimento ad esso. Ci sono diversi metodi per realizzare questo . Ho scelto di gestire il Loaded evento per il UniformGrid e memorizzare un riferimento a quel tempo.
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="1" Loaded="UniformGrid_Loaded" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
E nel codice dietro:
UniformGrid uniformgridButtons;
private void UniformGrid_Loaded(object sender, RoutedEventArgs e)
{
uniformgridButtons = sender as UniformGrid;
}
Quindi, gestire il SizeChanged eventi e regolare il parametro file in base a qualsiasi criterio che si desidera.
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));
}
}
Quindi questo permette WPF di gestire l'allungamento come nella risposta di Nir, ma consente di regolare in modo esplicito il numero di righe. Il codice di cui sopra dà questo risultato:
(Animated campione qui )
Aggiornamento 2009/08/28:
I stava sistemando la mia domanda di apprendimento in modo che il ItemsControl
era in un DataTemplate
in un ResourceDictionary
separata. Tuttavia, eventi come Loaded
non possono essere trattati in un file XAML ResourceDictionary
esterno perché non ha code-behind (di solito). Pertanto, avevo bisogno di memorizzare nella cache il riferimento al UniformGrid
utilizzando una tecnica diversa.
Ho invece usato metodo FindItemsPanel
di Alan Haigh (10 ° risposta) . In primo luogo, ho sostituito il ItemsControl
con:
<ContentPresenter x:Name="contentpresenterButtons" Content="{Binding obscolButtons}" />
Poi nel mio ResourceDictionary
, ho messo il ItemsControl
nella DataTemplate
:
<DataTemplate DataType="{x:Type local:Buttons}">
<ItemsControl ...
</DataTemplate>
Infine, ho memorizzato il riferimento alla UniformGrid
su modelli come:
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
}
Questo funziona proprio come la mia soluzione precedente, ma senza la necessità di un gestore di eventi nel ResourceDictionary
.
<!-- 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;
}
}