2011-03-24 45 views
11

單擊鼠標中鍵(又名:鼠標滾輪),然後稍微向下移動鼠標可讓用戶在IE和大多數Windows應用程序中滾動。這種行爲默認情況下在WPF控件中缺失?有沒有一種設置,解決方法,或者我失蹤的東西顯而易見?如何讓WPF ScrollViewer中鍵單擊滾動?

+1

它可以處理鼠標中鍵事件:http://stackoverflow.com/questions/517556/how-to-handle-the-mouse-wheel-click-event-in-wpf。我會嘗試使用tomorow來正確處理和處理這個事件。 – vorrtex 2011-03-25 22:44:49

回答

11

我發現如何使用3個鼠標事件(MouseDown,MouseUp,MouseMove)來實現此目的。他們的處理程序連接到ScrollViewer元素在下面的XAML:

<Grid> 
    <ScrollViewer MouseDown="ScrollViewer_MouseDown" MouseUp="ScrollViewer_MouseUp" MouseMove="ScrollViewer_MouseMove"> 
      <StackPanel x:Name="dynamicLongStackPanel"> 

      </StackPanel> 
    </ScrollViewer> 
    <Canvas x:Name="topLayer" IsHitTestVisible="False" /> 
</Grid> 

倒不如寫一個行爲,而不是在代碼隱藏事件,但並不是每個人都有必要的圖書館,也是我不知道如何將它與Canvas連接起來。

事件處理程序:

private bool isMoving = false;     //False - ignore mouse movements and don't scroll 
    private bool isDeferredMovingStarted = false; //True - Mouse down -> Mouse up without moving -> Move; False - Mouse down -> Move 
    private Point? startPosition = null; 
    private double slowdown = 200;     //The number 200 is found from experiments, it should be corrected 



    private void ScrollViewer_MouseDown(object sender, MouseButtonEventArgs e) 
    { 
     if (this.isMoving == true) //Moving with a released wheel and pressing a button 
       this.CancelScrolling(); 
     else if (e.ChangedButton == MouseButton.Middle && e.ButtonState == MouseButtonState.Pressed) 
     { 
      if (this.isMoving == false) //Pressing a wheel the first time 
      { 
       this.isMoving = true; 
       this.startPosition = e.GetPosition(sender as IInputElement); 
       this.isDeferredMovingStarted = true; //the default value is true until the opposite value is set 

       this.AddScrollSign(e.GetPosition(this.topLayer).X, e.GetPosition(this.topLayer).Y); 
      } 
     } 
    } 

    private void ScrollViewer_MouseUp(object sender, MouseButtonEventArgs e) 
    { 
     if (e.ChangedButton == MouseButton.Middle && e.ButtonState == MouseButtonState.Released && this.isDeferredMovingStarted != true) 
      this.CancelScrolling(); 
    } 

    private void CancelScrolling() 
    { 
     this.isMoving = false; 
     this.startPosition = null; 
     this.isDeferredMovingStarted = false; 
     this.RemoveScrollSign(); 
    } 

    private void ScrollViewer_MouseMove(object sender, MouseEventArgs e) 
    { 
     var sv = sender as ScrollViewer; 

     if (this.isMoving && sv != null) 
     { 
      this.isDeferredMovingStarted = false; //standard scrolling (Mouse down -> Move) 

      var currentPosition = e.GetPosition(sv); 
      var offset = currentPosition - startPosition.Value; 
      offset.Y /= slowdown; 
      offset.X /= slowdown; 

      //if(Math.Abs(offset.Y) > 25.0/slowdown) //Some kind of a dead space, uncomment if it is neccessary 
      sv.ScrollToVerticalOffset(sv.VerticalOffset + offset.Y); 
      sv.ScrollToHorizontalOffset(sv.HorizontalOffset + offset.X); 
     } 
    } 

如果去除方法調用AddScrollSignRemoveScrollSign這個例子會工作。圖標

private void AddScrollSign(double x, double y) 
    { 
     int size = 50; 
     var img = new BitmapImage(new Uri(@"d:\middle_button_scroll.png")); 
     var adorner = new Image() { Source = img, Width = size, Height = size }; 
     //var adorner = new Ellipse { Stroke = Brushes.Red, StrokeThickness = 2.0, Width = 20, Height = 20 }; 

     this.topLayer.Children.Add(adorner); 
     Canvas.SetLeft(adorner, x - size/2); 
     Canvas.SetTop(adorner, y - size/2); 
    } 

    private void RemoveScrollSign() 
    { 
     this.topLayer.Children.Clear(); 
    } 

例: enter image description hereenter image description here

而且最後一句話:但我有2種方法,其設置滾動圖標擴展它有一些問題的方式Press -> Immediately Release -> Move。如果用戶單擊鼠標左鍵或鍵盤的任何鍵,或者應用程序失去焦點,它應該取消滾動。有很多事件,我沒有時間處理它們。

但標準方式Press -> Move -> Release工作沒有問題。

+0

+1爲解決方案,我雖然有一些建議,請看我的單獨的答案 – 2011-03-28 12:05:49

2

vorrtex發佈了一個很好的解決方案,請upvote他

雖然我對他的解決方案有一些建議,但這些建議太冗長以至於無法將它們全部納入評論中,這就是爲什麼我發佈了一個單獨的答案並將其指引給他!

您提到了Press-> Release-> Move的問題。即使鼠標不在ScrollViewer上,您也應該使用MouseCapturing來獲取MouseEvents。我沒有測試過它,但我想你的解決方案在Press->Move->Move outside of ScrollViewer->Release也失敗了,鼠標捕捉也會照顧到這一點。

你也提到使用行爲。我寧願建議一個不需要額外依賴的attached behavior

你應該明確不使用額外的畫布,但在一個裝飾者中這樣做。

ScrollViewer自己擁有一個定義AdornerLayer的ScrollContentPresenter。你應該在那裏插入Adorner。這消除了對任何進一步依賴的需求,並且還保持了附加的行爲,如IsMiddleScrollable="true"一樣簡單。

+0

MouseCapture並不難實現,我會在幾個小時後這樣做。但其他評論並不那麼清楚。如果我使用附加屬性或行爲 - 我將無法將滾動圖像添加到「畫布」。另一方面'ScrollContentPresenter'控件位於'ScrollViewer'的模板中,我認爲我必須創建一個繼承的控件來訪問內部部件。我會盡力做到這一點,但我不確定新控件是否比一組事件處理程序更好。 – vorrtex 2011-03-28 14:57:51

+0

鼠標捕獲MouseMove事件後只被調用一次。我必須創建一個計時器來觸發移動功能的方法。一切都變得越來越複雜。 – vorrtex 2011-03-29 08:51:55

+0

@vorrtex明天晚上我會有時間的。正如我對一個不錯的緊湊型解決方案也很感興趣,我會深入探討它! – 2011-03-29 08:55:26