2010-11-21 3 views
1

Ich habe eine Leinwand in einem Scrollviewer. Um den Scrollviewer verwenden zu können, habe ich die MeasureOverride-Methode von Canvas überschrieben, um die Größe aller untergeordneten Elemente zurückzugeben.ScrollViewer mit Canvas, Canvas automatisch auf Inhalte mit negativem Leerzeichen skalieren

Dies funktioniert einwandfrei, außer dass die Zeichenfläche nur Elemente im positiven Bereich enthalten kann, damit der Scrollviewer ordnungsgemäß funktioniert.

Ich möchte, dass der Benutzer in der Lage ist, Elemente umherzuziehen, ohne dass die Einschränkung auf der positiven Seite des Ursprungs der Leinwand liegen muss.

Also grundsätzlich möchte ich die Elemente überall positionieren können, wie bei (-200, 10) oder (500, -20), und in der Lage sein, vom linken Element (-200) zu den meisten zu scrollen rechts (500) und von oben nach unten.

Um die Sache zu komplizieren, kann die Leinwand mit der LayoutTransform skaliert werden, und ich möchte den Ansichtsbereich in die Bildlaufleisten einbeziehen, so dass nicht nur die Min/Max-Grenzen der Kinder von den Bildlaufleisten berücksichtigt werden, aber auch das min/max des aktuellen View-Bereichs.

Weiß jemand, wie man das funktioniert?

Antwort

0

Ich denke, der ScrollViewer nimmt an, dass der Ursprung bei (0, 0) sein wird - schließlich gibt es keine Möglichkeit, es anders zu sagen; Canvas ist das einzige Panel, das über spezifizierte und nicht Null Ursprungs bekannt ist.

Eine einfache Option könnte sein, einen ScrollViewer zu vergessen, und implementieren Sie Ihre eigene Schwenkfunktionalität - vielleicht mit dem Ziehen, oder vielleicht mit einem großen "nach links scrollen" Knopf auf der linken Seite der Ansicht, ein "scrollen nach rechts" Schaltfläche rechts, etc. - und implementieren sie im Sinne einer Transformation auf den Inhalt.

Aber wenn Sie mit der klassischen Bildlaufleiste Metapher bleiben müssen, ich denke, Sie könnten Ihr eigenes Panel (oder Override ArrangeOverride auch, was auf das gleiche Ding) und Offset alle Positionen - machen sie Null-basiert. Wenn Sie also ein Element bei (20, -20) und ein anderes bei (0, 5) haben, müssten Sie alles um 20 nach unten versetzen, damit es in einen nullbasierten Raum passt. und wenn du deine Kinder auslegst, geht die erste um (20, 0) und die zweite um (0, 25).

Aber jetzt ist das Scrollen seltsam. Wenn der Benutzer ganz nach unten scrollt und etwas über die untere Kante zieht, bleibt die Ansicht unverändert. Gleiches mit dem Recht. Aber wenn sie bis zum oberen Rand gescrollt werden und etwas von der oberen Kante ziehen, springt plötzlich alles nach unten, weil der VerticalOffset von ScrollViewer vorher Null war und immer noch Null ist, aber Null bedeutet etwas anderes wegen des Layout-Versatzes.

Sie können die Scrolling-Seltsamkeit umgehen, indem Sie das HorizontalOffset und das VerticalOffset binden. Wenn Ihr ViewModel diese Eigenschaften automatisch korrigiert (und PropertyChanged ausgelöst hat), wenn sich ein Steuerelement "negativ" bewegt, können Sie die Ansicht möglicherweise auf denselben logischen Inhalt scrollen lassen, obwohl Sie jetzt der WPF-Layout-Engine sagen, dass alles in Ordnung ist irgendwo anders. Das heißt, Ihr ViewModel würde die Position der logischen Bildlaufleiste (Null, bevor Sie das Element negativ ziehen) verfolgen und die ScrollViewer-Position auf Null setzen (nach dem Ziehen würden Sie dem ScrollViewer mitteilen, dass sein VerticalOffset jetzt 20 ist).

1

Heute habe ich auf dieses Problem nur :) Glücklich, den Code zu teilen, die funktioniert:

public void RepositionAllObjects(Canvas canvas) 
{ 
    adjustNodesHorizontally(canvas); 
    adjustNodesVertically(canvas); 
} 

private void adjustNodesVertically(Canvas canvas) 
{ 
    double minLeft = Canvas.GetLeft(canvas.Children[0]); 
    foreach (UIElement child in canvas.Children) 
    { 
     double left = Canvas.GetLeft(child); 
     if (left < minLeft) 
      minLeft = left; 
    } 

    if (minLeft < 0) 
    { 
     minLeft = -minLeft; 
     foreach (UIElement child in canvas.Children) 
      Canvas.SetLeft(child, Canvas.GetLeft(child) + minLeft); 
    } 
} 

private void adjustNodesHorizontally(Canvas canvas) 
{ 
    double minTop = Canvas.GetTop(canvas.Children[0]); 
    foreach (UIElement child in canvas.Children) 
    { 
     double top = Canvas.GetTop(child); 
     if (top < minTop) 
      minTop = top; 
    } 

    if (minTop < 0) 
    { 
     minTop = -minTop; 
     foreach (UIElement child in canvas.Children) 
      Canvas.SetTop(child, Canvas.GetTop(child) + minTop); 
    } 
} 

die Objekte nun RepositionAllObjects Methode aufrufen, neu auszurichten und bei Bedarf.

Natürlich müssen Sie Ihre eigene Leinwand von Canvas abgeleitet haben. Und diese Methode erforderlich ist (man kann es zwicken, wenn Sie mögen):

public static Rect GetDimension(UIElement element) 
    { 
     Rect box = new Rect(); 
     box.X = Canvas.GetLeft(element); 
     box.Y = Canvas.GetTop(element); 
     box.Width = element.DesiredSize.Width; 
     box.Height = element.DesiredSize.Height; 
     return box; 
    } 

    protected override Size MeasureOverride(Size constraint) 
    { 
     Size availableSize = new Size(double.PositiveInfinity, double.PositiveInfinity); 
     double minX = 900000; //some dummy high number 
     double minY = 900000; //some dummy high number 
     double maxX = 0; 
     double maxY = 0; 

     foreach (UIElement element in this.Children) 
     { 
      element.Measure(availableSize); 

      Rect box = GetDimension(element); 
      if (minX > box.X) minX = box.X; 
      if (minY > box.Y) minY = box.Y; 
      if (maxX < box.X + box.Width) maxX = box.X + box.Width; 
      if (maxY < box.Y + box.Height) maxY = box.Y + box.Height; 
     } 

     if (minX == 900000) minX = 0; 
     if (minY == 900000) minY = 0; 

     return new Size { Width = maxX - minX, Height = maxY - minY }; 
    } 

nun alles, was Sie tun müssen, ist, wickeln diese Leinwand in einem Scrollviewer.

Hoffe es hilft!