我的Slider上有IsMoveToPointEnabled,所以当我点击组件的任何地方时,选择器移动到我的鼠标。问题是如果我打开此选项并单击并按住鼠标以拖动选择器,选择器不会移动。任何人都知道如何解决这个问题?
答案 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)
您需要自定义滑块和自定义滑块。单击滑块(不是用拇指击中) -
var hitTestResult = VisualTreeHelper.HitTest(this, point);
if (hitTestResult == null) return;
var parent = hitTestResult.VisualHit.ParentOfType<Thumb>();
if (parent != null) return;
_customThumb.OnMouseLeftButtonDown(e);
您可以调用自定义滑块方法,该方法在OnMouseLeftButtonDown(MouseButtonEventArgs e)
OnMouseLeftButtonDown(MouseButtonEventArgs e)
答案 5 :(得分:0)
绑定到鼠标移动事件对我来说真的很不对劲-我希望单击滑块就像单击拇指一样,以便该事件仅触发一次并触发正常的拖动行为。经过一些挖掘之后,我想出了一个可行的替代解决方案。
当OnPreviewMouseLeftButtonDown
为IsMoveToPointEnabled
时,滑块的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
true
为IsMoveToPointEnabled
时true
的属性。另外,我们可以继承Slider的子类并覆盖它的OnPreviewMouseLeftButton
方法。