2008-10-13 8 views
11

Ich erstelle ein Steuerelement für WPF, und ich habe eine Frage für Sie WPF Gurus da draußen.Verschachtelte Bildlaufbereiche

Ich möchte, dass meine Steuerung erweitert werden kann, um in ein Fenster mit veränderbarer Größe zu passen.

In meiner Kontrolle habe ich ein Listenfeld, das ich mit dem Fenster erweitern möchte. Ich habe auch andere Steuerelemente um das Listenfeld (Schaltflächen, Text usw.).

Ich möchte eine Mindestgröße für mein Steuerelement festlegen, aber ich möchte, dass das Fenster kleiner sein kann, indem Sie Bildlaufleisten zum Anzeigen des Steuerelements erstellen.

Dadurch werden verschachtelte Bildlaufbereiche erstellt: Einer für das Listenfeld und ein ScrollViewer, der das gesamte Steuerelement einhüllt.

Jetzt, wenn das Listenfeld auf automatische Größe eingestellt ist, wird es nie eine Bildlaufleiste haben, da es immer innerhalb ScrollViewer in voller Größe gezeichnet wird.

Ich möchte nur das Steuerelement scrollen, wenn der Inhalt nicht kleiner werden kann, sonst möchte ich nicht das Steuerelement scrollen; Stattdessen möchte ich das Listenfeld innerhalb des Steuerelements scrollen.

Wie kann ich das Standardverhalten der ScrollViewer-Klasse ändern? Ich habe versucht, von der ScrollViewer-Klasse zu erben und die MeasureOverride- und ArrangeOverride-Klassen zu überschreiben, aber ich konnte nicht herausfinden, wie man das Kind richtig misst und anordnet. Es scheint, dass das Arrangement irgendwie den ScrollContentPresenter beeinflussen muss, nicht das eigentliche Inhaltskind.

Jede Hilfe/Vorschläge würde sehr geschätzt werden.

+0

Große Frage. Wir sind selbst auf ein sehr ähnliches Problem gestoßen. – cplotts

Antwort

0

Während ich würde nicht empfehlen, eine Benutzeroberfläche erstellen, die äußeren Bildlaufleisten erfordert können Sie dies ziemlich leicht erreichen:

<Window 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    >  
    <ScrollViewer HorizontalScrollBarVisibility="Auto" 
        VerticalScrollBarVisibility="Auto"> 
     <Grid> 
      <Grid.RowDefinitions> 
       <RowDefinition Height="Auto"/> 
       <RowDefinition Height="Auto"/> 
       <RowDefinition Height="Auto"/> 
      </Grid.RowDefinitions> 
      <Grid.ColumnDefinitions> 
       <ColumnDefinition Width="*"/> 
       <ColumnDefinition Width="Auto"/> 
      </Grid.ColumnDefinitions> 
      <ListBox Grid.Row="0" Grid.RowSpan="3" Grid.Column="0" MinWidth="200"/> 
      <Button Grid.Row="0" Grid.Column="1" Content="Button1"/> 
      <Button Grid.Row="1" Grid.Column="1" Content="Button2"/> 
      <Button Grid.Row="2" Grid.Column="1" Content="Button3"/> 
     </Grid> 
    </ScrollViewer> 
</Window> 

ich das wirklich nicht empfehlen. WPF bietet außergewöhnliche Layout-Systeme, wie Grid, und Sie sollten versuchen, die Größe der App nach Bedarf zu ändern. Vielleicht können Sie eine MinWidth/MinHeight auf das Fenster selbst setzen, um diese Größenänderung zu verhindern?

+0

Die Schwierigkeit besteht darin, dass ich das Fenster unter der Mindestgröße verkleinern kann, ohne die Steuerelemente zu beschneiden. –

+0

Muss * das Fenster unterhalb dieser Mindestgröße verkleinert werden? Was willst du erreichen, indem du das erlaubst? –

+0

Nein, muss nicht, aber das ganze Problem verschwindet, wenn ich es nicht tue. Ich möchte unter die Mindestgröße verkleinern, um flexible Layouts zu ermöglichen. Ich baue gerade eine App, die "fensterartige" Steuerelemente enthält, die angepasst und positioniert werden können. –

2

Sie Problem entsteht, weil Control s innerhalb einer ScrollViewer haben praktisch unbegrenzten Speicherplatz zur Verfügung. Daher glaubt Ihr Inneres ListBox, dass es Scrollen vermeiden kann, indem es die komplette Höhe einnimmt, die notwendig ist, um alle seine Elemente anzuzeigen. Natürlich hat dieses Verhalten in Ihrem Fall den unerwünschten Nebeneffekt, das äußere ScrollViewer zu sehr zu trainieren.

Das Ziel ist daher die ListBox bekommen die sichtbar Höhe innerhalb des ScrollViewer zu verwenden iff gibt es genug und eine gewisse Mindesthöhe anders ist. Um dies zu erreichen, der direkteste Weg von ScrollViewer zu erben und überschreiben MeasureOverride() passieren ein entsprechend dimensioniertes availableSize (dh die gegebene availableSize auf die minimale Größe geblasen anstelle der „üblichen“ Unendlichkeit) zu den Visual s gefunden VisualChildrenCount mit und GetVisualChild(int).

+0

+1 für die Erklärung bezüglich der unbegrenzten verfügbaren Speicherplatz. – Ridcully

12

ich eine Klasse erstellt haben, um dieses Problem zu umgehen:

public class RestrictDesiredSize : Decorator 
{ 
    Size lastArrangeSize = new Size(double.PositiveInfinity, double.PositiveInfinity); 

    protected override Size MeasureOverride(Size constraint) 
    { 
     Debug.WriteLine("Measure: " + constraint); 
     base.MeasureOverride(new Size(Math.Min(lastArrangeSize.Width, constraint.Width), 
             Math.Min(lastArrangeSize.Height, constraint.Height))); 
     return new Size(0, 0); 
    } 

    protected override Size ArrangeOverride(Size arrangeSize) 
    { 
     Debug.WriteLine("Arrange: " + arrangeSize); 
     if (lastArrangeSize != arrangeSize) { 
      lastArrangeSize = arrangeSize; 
      base.MeasureOverride(arrangeSize); 
     } 
     return base.ArrangeOverride(arrangeSize); 
    } 
} 

Es wird immer eine gewünschte Größe (0,0) zurückkehren, auch wenn das Aufnahmeelement will größer sein. Verbrauch:

<local:RestrictDesiredSize MinWidth="200" MinHeight="200"> 
    <ListBox /> 
</local> 
+0

Das ist die Lösung, die für uns funktioniert hat. – cplotts

+0

Saubere, einfache, elegante Lösung –

1

verwendete ich Daniel s Lösung. Das funktioniert großartig. Vielen Dank.

Dann fügte ich der Decorator-Klasse zwei boolesche Abhängigkeitseigenschaften hinzu: KeepWidth und . So kann das neue Feature für eine Dimension unterdrückt werden.

Dies erfordert eine Änderung in MeasureOverride:

protected override Size MeasureOverride(Size constraint) 
{ 
    var innerWidth = Math.Min(this._lastArrangeSize.Width, constraint.Width); 
    var innerHeight = Math.Min(this._lastArrangeSize.Height, constraint.Height); 
    base.MeasureOverride(new Size(innerWidth, innerHeight)); 

    var outerWidth = KeepWidth ? Child.DesiredSize.Width : 0; 
    var outerHeight = KeepHeight ? Child.DesiredSize.Height : 0; 
    return new Size(outerWidth, outerHeight); 
} 
0

Erstellen Sie eine Methode in dem Code-behind, dass die ListBox des MaxHeight der Höhe setzt gleich welcher Steuerung wird und andere Steuerelemente enthält. Wenn die Listbox Steuerelemente/Ränder/Abstand darüber oder darunter hat, subtrahieren Sie ihre Höhen von der Containerhöhe, die MaxHeight zugewiesen ist. Rufen Sie diese Methode in den Hauptfenstern "loaded" und "window resize" Event Handlers auf.

Dies sollte Ihnen das Beste aus beiden Welten geben. Sie geben der ListBox eine "feste" Größe, die zum Scrollen führt, obwohl das Hauptfenster über eine eigene Bildlaufleiste verfügt.

0

für 2 Scroll

public class ScrollExt: ScrollViewer 
{ 
    Size lastArrangeSize = new Size(double.PositiveInfinity, double.PositiveInfinity); 

    public ScrollExt() 
    { 

    } 
    protected override Size MeasureOverride(Size constraint) 
    { 
     base.MeasureOverride(new Size(Math.Min(lastArrangeSize.Width, constraint.Width), 
             Math.Min(lastArrangeSize.Height, constraint.Height))); 
     return new Size(0, 0); 
    } 

    protected override Size ArrangeOverride(Size arrangeSize) 
    { 
     if (lastArrangeSize != arrangeSize) 
     { 
      lastArrangeSize = arrangeSize; 
      base.MeasureOverride(arrangeSize); 
     } 
     return base.ArrangeOverride(arrangeSize); 
    } 
} 

Code:

<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"> 
     <Grid > 
      <Grid.ColumnDefinitions> 
       <ColumnDefinition Width="Auto" /> 
       <ColumnDefinition Width="*" /> 
      </Grid.ColumnDefinitions> 
      <TextBlock Background="Beige" Width="600" Text="Example"/> 
      <Grid Grid.Column="1" x:Name="grid"> 
        <Grid Grid.Column="1" Margin="25" Background="Green"> 
        <local:ScrollExt HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"> 
         <Grid Width="10000" Margin="25" Background="Red" /> 
        </local:ScrollExt> 
        </Grid> 
       </Grid> 
     </Grid> 
    </ScrollViewer>