2010-11-21 22 views
1

我有一個畫布在scrollviewer。 爲了能夠使用scrollviewer,我重寫了Canvas的MeasureOverride方法以返回所有子級的大小。ScrollViewer與畫布,自動調整大小的畫布到內容包括負空間

這可以正常工作,但畫布只能包含正面空間中的項目以便scrollviewer正常工作。

我希望用戶能夠拖動元素,而不必限制它們在畫布原點的正面。因此基本上我希望能夠在任何位置定位元素,例如(-200,10)或(500,-20),並且能夠從最左邊的元素(-200)滾動到最多右(500),從上到下。

複雜的事情可以使用LayoutTransform來縮放畫布,並且我希望在滾動條中包含視圖區域,所以不僅滾動條考慮了子項的最小/最大邊界,還包括當前視圖區域的最小/最大值。

有誰知道如何使這項工作?

回答

0

我認爲ScrollViewer假定原點將在(0,0) - 畢竟,沒有辦法告訴它,否則;畫布是唯一知道指定和非零起源的面板。

一個簡單的選項可能是忘記一個ScrollViewer,並實現自己的平移功能 - 可能是拖動,也可能是在視圖左側有一個大的「向左滾動」按鈕,「向右滾動」右邊的按鈕等等 - 並且通過對內容進行轉換來實現它。但是如果你需要堅持使用經典的滾動條隱喻,我認爲你可以製作自己的面板(或者也可以重寫ArrangeOverride,這相當於同樣的事情)並抵消所有的位置 - 使它們爲零。所以如果你在(20,-20)和另一個在(0,5)有一個元素,你需要將所有東西向下偏移20以使它適合於一個基於零的空間;當你放下你的孩子時,第一個會在(20,0)和第二個在(0,25)。

但現在滾動很奇怪。如果用戶一直滾動到底部,並從底部邊緣拖動某些東西,則該視圖保持放置狀態;與右邊相同。但是,如果它們一直滾動到頂部,並從頂部邊緣拖動某些東西,突然間所有東西都會向下跳躍,因爲ScrollViewer的VerticalOffset之前爲零,並且仍然爲零,但由於佈局偏移量的原因,零意味着不同。

您可以通過綁定Horizo​​ntalOffset和VerticalOffset來解決滾動的奇怪問題。如果您的ViewModel在控件移動爲「負面」時自動修復了這些屬性(並且觸發了PropertyChanged),那麼即使您現在告訴WPF的佈局引擎一切都是如此,您仍可以將視圖滾動到相同的邏輯內容別的地方。也就是說,您的ViewModel將跟蹤邏輯滾動位置(零,在您拖動元素爲負數之前),並向ScrollViewer報告從零開始的滾動位置(所以在拖動之後,您會告訴ScrollViewer它的VerticalOffset現在是20 )。

1

今天,我曾在這個問題上只:) 樂於分享其工作代碼:

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); 
    } 
} 

現在叫RepositionAllObjects方法重新調整的對象爲有需要時。

當然,你需要有自己的畫布,從Canvas派生。這種方法需要(如果你喜歡,你可以調整它):

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 }; 
    } 

現在,所有你需要做的是一個包裹的ScrollViewer這裏面的畫布。

希望它有幫助!