滑块不会与IsMoveToPointEnabled行为一起拖动

时间:2010-05-26 02:58:27

标签: c# wpf slider

我的Slider上有IsMoveToPointEnabled,所以当我点击组件的任何地方时,选择器移动到我的鼠标。问题是如果我打开此选项并单击并按住鼠标以拖动选择器,选择器不会移动。任何人都知道如何解决这个问题?

6 个答案:

答案 0 :(得分:18)

最简单的方法是子类Slider:

public class CustomSlider : Slider
{
  public override void OnPreviewMouseMove(MouseEventArgs e)
  {
    if(e.LeftButton == MouseButtonState.Pressed)
      OnPreviewMouseLeftButtonDown(e);
  }
}

在这种情况下,您的XAML将是:

<my:CustomSlider IsMoveToPointEnabled="True" />

对于不是Slider子类的更通用的解决方案,您可以使用附加属性来执行此操作:

public class SliderTools : DependencyObject
{
  public static bool GetMoveToPointOnDrag(DependencyObject obj) { return (bool)obj.GetValue(MoveToPointOnDragProperty); }
  public static void SetMoveToPointOnDrag(DependencyObject obj, bool value) { obj.SetValue(MoveToPointOnDragProperty, value); }
  public static readonly DependencyProperty MoveToPointOnDragProperty = DependencyProperty.RegisterAttached("MoveToPointOnDrag", typeof(bool), typeof(SliderTools), new PropertyMetadata
  {
    PropertyChangedCallback = (obj, changeEvent) =>
    {
      var slider = (Slider)obj;
      if((bool)changeEvent.NewValue)
        slider.MouseMove += (obj2, mouseEvent) =>
        {
          if(mouseEvent.LeftButton == MouseButtonState.Pressed)
            slider.RaiseEvent(new MouseButtonEventArgs(mouseEvent.MouseDevice, mouseEvent.Timestamp, MouseButton.Left)
            {
              RoutedEvent = UIElement.PreviewMouseLeftButtonDownEvent,
              Source = mouseEvent.Source,
            });
        };
    }
  });
}

您可以在Slider上使用此附加属性以及IsMoveToPointEnabled属性:

<Slider IsMoveToPointEnabled="True" my:SliderTools.MoveToPointOnDrag="True" ... />

这两个解决方案都可以通过在左键按下时将PreviewMouseMove事件转换为等效的PreviewMouseLeftButtonDown事件来工作。

请注意,当属性设置为false时,附加属性不会删除事件处理程序。我这样写是为了简单,因为你几乎从不需要删除这样的处理程序。我建议您坚持使用这个简单的解决方案,但是如果您愿意,可以修改PropertyChangedCallback以在NewValue为false时删除处理程序。

答案 1 :(得分:7)

受Ray Burns答案的启发,我找到的最简单的方法是:

mySlider.PreviewMouseMove += (sender, args) =>
{
    if (args.LeftButton == MouseButtonState.Pressed)
    {
        mySlider.RaiseEvent(new MouseButtonEventArgs(args.MouseDevice, args.Timestamp, MouseButton.Left)
        {
            RoutedEvent = UIElement.PreviewMouseLeftButtonDownEvent,
                 Source = args.Source
        });
    }
};

mySlider是我的Slider的名字。

此解决方案存在两个问题(本主题中的大多数问题):
1.如果在滑块外单击并按住鼠标,然后在滑块上移动鼠标,则会开始拖动 2.如果您使用的是滑块的自动工具提示,则使用此方法拖动时无效。

所以这是一个改进的版本,可以解决这两个问题:

mySlider.MouseMove += (sender, args) =>
{
    if (args.LeftButton == MouseButtonState.Pressed && this.clickedInSlider)
    {
        var thumb = (mySlider.Template.FindName("PART_Track", mySlider) as System.Windows.Controls.Primitives.Track).Thumb;
        thumb.RaiseEvent(new MouseButtonEventArgs(args.MouseDevice, args.Timestamp, MouseButton.Left)
        {
            RoutedEvent = UIElement.MouseLeftButtonDownEvent,
                 Source = args.Source
        });
    }
};

mySlider.AddHandler(UIElement.PreviewMouseLeftButtonDownEvent, new RoutedEventHandler((sender, args) =>
{
    clickedInSlider = true;
}), true);

mySlider.AddHandler(UIElement.PreviewMouseLeftButtonUpEvent, new RoutedEventHandler((sender, args) =>
{
    clickedInSlider = false;
}), true);

clickedInSlider是在类中定义的私有辅助变量。

通过使用clickedInSlider辅助变量,我们避免1.处理PreviewMouseButtonDown事件(因为MoveToPoint = true),所以我们必须使用mySlider.AddHandler。
通过在Thumb上而不是Slider上引发事件,我们确保显示自动工具提示。

答案 2 :(得分:1)

这是@TimPohlmann答案的改进版本。

这个只提升MouseButtonEventHandler一次。虽然拇指在每次鼠标移动时都会在内部抬起DragDeltaEventHandler。但我发现自己每次移动鼠标时都没有必要用拇指发射MouseLeftButtonDownEvent

此外,不需要辅助变量。

这是作为行为实现的。 AssociatedObject是附加此行为的滑块。

XAML:

<Slider ...>
    <i:Interaction.Behaviors>
        <slid:FreeSlideBehavior />
    </i:Interaction.Behaviors>
</Slider>

行为:

public sealed class FreeSlideBehavior : Behavior<Slider>
{
    private Thumb _thumb;

    private Thumb Thumb
    {
        get
        {
            if (_thumb == null)
            {
                _thumb = ((Track)AssociatedObject.Template.FindName("PART_Track", AssociatedObject)).Thumb;
            }
            return _thumb;
        }
    }

    protected override void OnAttached()
    {
        AssociatedObject.MouseMove += OnMouseMove;
    }

    protected override void OnDetaching()
    {
        AssociatedObject.MouseMove -= OnMouseMove;
    }

    private void OnMouseMove(object sender, MouseEventArgs args)
    {
        if (args.LeftButton == MouseButtonState.Released) return;
        if(Thumb.IsDragging) return;
        if (!Thumb.IsMouseOver) return;

        Thumb.RaiseEvent(new MouseButtonEventArgs(args.MouseDevice, args.Timestamp, MouseButton.Left)
        {
            RoutedEvent = UIElement.MouseLeftButtonDownEvent
        });
    }
}

答案 3 :(得分:0)

怎么样

    private void slider_PreviewMouseDown(object sender, MouseButtonEventArgs e)
    {
        Point point = e.GetPosition(slider);
        slider.Value = point.X;
    }

答案 4 :(得分:0)

  1. 您需要自定义滑块和自定义滑块。单击滑块(不是用拇指击中) -

    var hitTestResult = VisualTreeHelper.HitTest(this, point);
    if (hitTestResult == null) return;
    var parent = hitTestResult.VisualHit.ParentOfType<Thumb>();
    if (parent != null) return;
    _customThumb.OnMouseLeftButtonDown(e);
    
  2. 您可以调用自定义滑块方法,该方法在OnMouseLeftButtonDown(MouseButtonEventArgs e)

    内调用
    1. 你可以支持Thumb并使用Reflaction来调用OnMouseLeftButtonDown(MouseButtonEventArgs e)

答案 5 :(得分:0)

绑定到鼠标移动事件对我来说真的很不对劲-我希望单击滑块就像单击拇指一样,以便该事件仅触发一次并触发正常的拖动行为。经过一些挖掘之后,我想出了一个可行的替代解决方案。

OnPreviewMouseLeftButtonDownIsMoveToPointEnabled时,滑块的true方法可以处理更改滑块的值。但是,Thumb的OnMouseLeftButtonDown方法处理焦点,捕获鼠标,然后开始DragStarted事件。因此,我采取的策略是使Slider上的PreviewMouseLeftButtonDown事件手动触发Thumb上的MouseLeftButtonDown事件:

mySlider.AddHandler(
    PreviewMouseLeftButtonDownEvent,
    new MouseButtonEventHandler((sender, e) =>
    {
        Track track = mySlider.Template.FindName("PART_Track", mySlider) as Track;

        // It's important to check `track.Thumb.IsMouseOver`, because if it's `true` then
        // the Thumb will already have its `OnMouseLeftButtonDown` method called - there's
        // no need for us to manually trigger it (and doing so would result in firing the
        // event twice, which is bad).
        if (!mySlider.IsMoveToPointEnabled || track == null || track.Thumb == null || track.Thumb.IsMouseOver)
        {
            return;
        }

        // When `IsMoveToPointEnabled` is true, the Slider's `OnPreviewMouseLeftButtonDown`
        // method updates the slider's value to where the user clicked. However, the Thumb
        // hasn't had its position updated yet to reflect the change. As a result, we must
        // call `UpdateLayout` on the Thumb to make sure its position is correct before we
        // trigger a `MouseLeftButtonDownEvent` on it.
        track.Thumb.UpdateLayout();

        track.Thumb.RaiseEvent(new MouseButtonEventArgs(e.MouseDevice, e.Timestamp, MouseButton.Left)
        {
            RoutedEvent = MouseLeftButtonDownEvent,
            Source = track.Thumb
        });
    }),
    true);

我们必须使用AddHandler重载,该重载需要Slider上的第三个handledEventsToo参数来连接我们的处理程序,因为Slider的OnPreviewMouseLeftButtonDown方法设置了事件的Handled trueIsMoveToPointEnabledtrue的属性。另外,我们可以继承Slider的子类并覆盖它的OnPreviewMouseLeftButton方法。