2011-03-24 6 views
11

중간 마우스 버튼 (일명 : 마우스 휠)을 클릭 한 다음 마우스를 약간 아래로 이동하면 IE 및 대부분의 Windows 응용 프로그램에서 스크롤 할 수 있습니다. 이 동작은 기본적으로 WPF 컨트롤에없는 것 같습니다? 해결 방법, 해결 방법 또는 누락 된 것이 있습니까?WPF ScrollViewer 중간 클릭 스크롤을 어떻게 만듭니 까?

+1

마우스 가운데 버튼 이벤트를 처리하는 것이 가능합니다 : http://stackoverflow.com/questions/517556/how-to-handle-the-mouse-wheel-click-event-in-wpf. 나는이 사건을 올바르게 처리하고 처리하기 위해 노력할 것이다. – vorrtex

답변

11

3 마우스 이벤트 (MouseDown, MouseUp, MouseMove)를 사용하여이를 수행하는 방법을 발견했습니다.

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

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

대신 코드 숨김에서 이벤트의 동작을 작성하는 더 나은 것,하지만 모두가 필요한 라이브러리를 가지고 있으며, 또한 그렇게하지 : 그들의 핸들러는 아래의 XAML에 ScrollViewer 요소에 부착 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); 
     } 
    } 

방법은 AddScrollSign이 예제가 작동 RemoveScrollSign를 호출 제거 ​​할 경우

. 아이콘의

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 솔루션에 대한 의견이 있으 십니다. 제 답변을 참조하십시오. –

2

vorrtex 멋진 솔루션 게시 위로 올리기하십시오주세요!

나는 그 해결책에 대한 몇 가지 제안을하고 있지만, 의견에 모두 들어 맞기에는 너무 길어서 별도의 답변을 게시하고 직접 알려줍니다!

보도 자료 -> 릴리스 -> 이동 문제를 언급합니다. 마우스가 더 이상 ScrollViewer를 넘지 않아도 MouseCapturing을 사용하여 MouseEvents를 가져와야합니다. 나는 그것을 테스트하지 않았지만, 당신의 솔루션도 Press->Move->Move outside of ScrollViewer->Release에서 실패했다고 생각합니다. Mousecapturing도 그것을 처리 할 것입니다.

또한 행동을 사용하여 언급합니다. 차라리 추가 종속성이 필요없는 attached behavior을 제안합니다.

분명히 여분의 캔버스를 사용하지 말고 Adorner에서해야합니다.

ScrollViewer 자체는 AdornerLayer를 정의하는 ScrollContentPresenter를 호스팅합니다. 거기에 Adorner를 삽입해야합니다. 이렇게하면 더 이상의 종속성이 필요 없으며 첨부 된 동작을 IsMiddleScrollable="true"과 같이 간단하게 유지할 수 있습니다.

+0

MouseCapture는 구현하기 어렵지 않습니다. 몇 시간 후에이 작업을 수행 할 것입니다.그러나 다른 의견은 그렇게 평범하지 않습니다. 첨부 된 속성이나 동작을 사용하면 스크롤 이미지를'Canvas'에 추가 할 수 없습니다. 반면에'ScrollContentPresenter '컨트롤은'ScrollViewer'의 템플릿 안에 위치합니다. 내부 부품에 액세스하려면 상속 된 컨트롤을 만들어야한다고 생각합니다. 이것으로 무언가를하려고 노력할 것이지만 새로운 컨트롤이 이벤트 핸들러 세트보다 나은지 확실하지 않습니다. – vorrtex

+0

마우스 캡처 후 MouseMove 이벤트는 한 번만 호출됩니다. 나는 움직이는 기능을 가진 메소드를 트리거 할 타이머를 생성해야한다. 모든 것이 점점 더 복잡해집니다. – vorrtex

+0

@vorrtex 내일 저녁에 시간이 있습니다. 나도 멋진 소형 솔루션에 관심이있어, 나는 그것에 잠수를 취할 것입니다! –

관련 문제