Here's a little 5-minute glob of code I hacked together to handle dragging ScrollViewer content around with the mouse (or your finger).
It can also reverse the axes for "natural scrolling." There is also a Tolerance setting that is supposed to prevent it from scrolling if you only drag it a little bit. I did this because my crappy touchscreen driver has a lot of jitter.
I claim no responsibility if it explodes or otherwise has undesired effects. :)
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Controls;
namespace MouseExtensions
{
public class MouseDragBehavior
{
public static readonly DependencyProperty ToleranceProperty = DependencyProperty.RegisterAttached("Tolerance", typeof(double), typeof(MouseDragBehavior), new FrameworkPropertyMetadata((double)10));
public static double GetTolerance(DependencyObject d)
{
return (double)d.GetValue(ToleranceProperty);
}
public static void SetTolerance(DependencyObject d, double value)
{
d.SetValue(ToleranceProperty, value);
}
public static readonly DependencyProperty ReverseAxisProperty = DependencyProperty.RegisterAttached("ReverseAxis", typeof(bool), typeof(MouseDragBehavior), new FrameworkPropertyMetadata((bool)false));
public static bool GetReverseAxis(DependencyObject d)
{
return (bool)d.GetValue(ReverseAxisProperty);
}
public static void SetReverseAxis(DependencyObject d, bool value)
{
d.SetValue(ReverseAxisProperty, value);
}
private static readonly DependencyProperty DragStartPointProperty = DependencyProperty.RegisterAttached("DragStartPoint", typeof(Point), typeof(MouseDragBehavior), new FrameworkPropertyMetadata((Point)new Point()));
private static Point GetDragStartPoint(DependencyObject d)
{
return (Point)d.GetValue(DragStartPointProperty);
}
private static void SetDragStartPoint(DependencyObject d, Point value)
{
d.SetValue(DragStartPointProperty, value);
}
private static readonly DependencyProperty DragLastPointProperty = DependencyProperty.RegisterAttached("DragLastPoint", typeof(Point), typeof(MouseDragBehavior), new FrameworkPropertyMetadata((Point)new Point()));
private static Point GetDragLastPoint(DependencyObject d)
{
return (Point)d.GetValue(DragLastPointProperty);
}
private static void SetDragLastPoint(DependencyObject d, Point value)
{
d.SetValue(DragLastPointProperty, value);
}
public static readonly DependencyProperty HandleDragProperty = DependencyProperty.RegisterAttached("HandleDrag", typeof(bool), typeof(MouseDragBehavior), new FrameworkPropertyMetadata((bool)false, new PropertyChangedCallback(OnHandleDragChanged)));
public static bool GetHandleDrag(DependencyObject d)
{
return (bool)d.GetValue(HandleDragProperty);
}
public static void SetHandleDrag(DependencyObject d, bool value)
{
d.SetValue(HandleDragProperty, value);
}
private static void OnHandleDragChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ScrollViewer scrollViewer = d as ScrollViewer;
if ((bool)e.NewValue)
{
scrollViewer.PreviewMouseDown += new MouseButtonEventHandler(target_PreviewMouseDown);
scrollViewer.PreviewMouseMove += new MouseEventHandler(target_PreviewMouseMove);
scrollViewer.PreviewMouseUp += new MouseButtonEventHandler(target_PreviewMouseUp);
scrollViewer.MouseLeave += new MouseEventHandler(target_MouseLeave);
}
else
{
scrollViewer.PreviewMouseDown -= new MouseButtonEventHandler(target_PreviewMouseDown);
scrollViewer.PreviewMouseMove -= new MouseEventHandler(target_PreviewMouseMove);
scrollViewer.PreviewMouseUp -= new MouseButtonEventHandler(target_PreviewMouseUp);
scrollViewer.MouseLeave -= new MouseEventHandler(target_MouseLeave);
}
}
private static bool IsDragging(ScrollViewer scrollViewer)
{
Point posNow = Mouse.GetPosition(scrollViewer);
return ((Math.Abs(GetDragStartPoint(scrollViewer).X - posNow.X) > GetTolerance(scrollViewer)) || (Math.Abs(GetDragStartPoint(scrollViewer).Y - posNow.Y) > GetTolerance(scrollViewer)));
}
private static void target_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
var scrollViewer = (ScrollViewer)sender;
var mousePos = e.GetPosition(scrollViewer);
scrollViewer.CaptureMouse();
if (GetDragLastPoint(scrollViewer) == new Point())
{
SetDragStartPoint(scrollViewer, mousePos);
SetDragLastPoint(scrollViewer, mousePos);
}
if (IsDragging(scrollViewer))
{
e.Handled = true;
return;
}
else
scrollViewer.ReleaseMouseCapture();
}
private static void target_PreviewMouseMove(object sender, MouseEventArgs e)
{
var scrollViewer = (ScrollViewer)sender;
if (GetDragLastPoint(scrollViewer) != new Point())
{
Point posNow = e.GetPosition(scrollViewer);
double dX = posNow.X - GetDragLastPoint(scrollViewer).X;
double dY = posNow.Y - GetDragLastPoint(scrollViewer).Y;
SetDragLastPoint(scrollViewer, posNow);
if (!IsDragging(scrollViewer))
return;
if (GetReverseAxis(scrollViewer))
{
scrollViewer.ScrollToHorizontalOffset(scrollViewer.HorizontalOffset + dX);
scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset + dY);
}
else
{
scrollViewer.ScrollToHorizontalOffset(scrollViewer.HorizontalOffset - dX);
scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - dY);
}
}
e.Handled = true;
}
private static void target_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
var scrollViewer = (ScrollViewer)sender;
if (GetDragLastPoint(scrollViewer) != new Point() && IsDragging(scrollViewer))
{
e.Handled = true;
scrollViewer.ReleaseMouseCapture();
}
Reset(scrollViewer);
}
private static void target_MouseLeave(object sender, MouseEventArgs e)
{
var scrollViewer = (ScrollViewer)sender;
Reset(scrollViewer);
}
private static void Reset(ScrollViewer scrollViewer)
{
SetDragLastPoint(scrollViewer, new Point());
SetDragStartPoint(scrollViewer, new Point());
}
}
}
To use it simply reference the namespace and set MouseDragBehavior.HandleDrag to true in your
It is silly there is no simple way to check whether this profile has been activated. CFRs are normal, but trying to even hide the fact if it's on / off seems silly, especially for something so user-facing.
Surely Microsoft is "proud" of their engineering efforts on this one and ought to display it somwhere in the GUI.
Many Linux distros are not known for excellent battery life, so I'm not sure that is the best example.
A more apt example may be Apple, but Apple's CPUs are simply far more efficient than Intel & AMD at single-threaded tasks like these, so "boosting" is not as power-hungry and less heat-inducing. Not to mention Apple will hardly engage P-cores for basic UI tasks; they use a pretty complicated QoS scheme to only activate P-cores for more serious workloads like HTML / JS execution or decompression or application launch.
Microsoft is (smartly) doing it for launch, but also for UI tasks, which is the more nonsensical part: why ... do Windows 11's UIs need modern CPUs to boost? It should load so quickly that there's not even time for the CPU to boost.
I've not seen any controlled testing and, judging by Microsoft's mentality, within a year, they'll have added so much more bloat, it'll undo any perceptible latency benefit and we'll have boosted the CPU clocks for nothing.
It depends: heat soak is a thing.
Initially on cold boot-up, the heatsinks & heatpipes are at ambient temp. After heatsinks & heatpipes warm up (through normal usage), they don't immediately cool to ambient temp when the load goes away. So their baseline is higher and the trigger point for fans is much less stress.
Add a few more CPU spikes → it's too hot to stay at the same fan RPM → fans get triggered to start up up much sooner / get triggered to ramp much more quickly.
Question
Eric Veteran
Here's a little 5-minute glob of code I hacked together to handle dragging ScrollViewer content around with the mouse (or your finger).
It can also reverse the axes for "natural scrolling." There is also a Tolerance setting that is supposed to prevent it from scrolling if you only drag it a little bit. I did this because my crappy touchscreen driver has a lot of jitter.
I claim no responsibility if it explodes or otherwise has undesired effects. :)
using System; using System.Windows; using System.Windows.Input; using System.Windows.Controls; namespace MouseExtensions { public class MouseDragBehavior { public static readonly DependencyProperty ToleranceProperty = DependencyProperty.RegisterAttached("Tolerance", typeof(double), typeof(MouseDragBehavior), new FrameworkPropertyMetadata((double)10)); public static double GetTolerance(DependencyObject d) { return (double)d.GetValue(ToleranceProperty); } public static void SetTolerance(DependencyObject d, double value) { d.SetValue(ToleranceProperty, value); } public static readonly DependencyProperty ReverseAxisProperty = DependencyProperty.RegisterAttached("ReverseAxis", typeof(bool), typeof(MouseDragBehavior), new FrameworkPropertyMetadata((bool)false)); public static bool GetReverseAxis(DependencyObject d) { return (bool)d.GetValue(ReverseAxisProperty); } public static void SetReverseAxis(DependencyObject d, bool value) { d.SetValue(ReverseAxisProperty, value); } private static readonly DependencyProperty DragStartPointProperty = DependencyProperty.RegisterAttached("DragStartPoint", typeof(Point), typeof(MouseDragBehavior), new FrameworkPropertyMetadata((Point)new Point())); private static Point GetDragStartPoint(DependencyObject d) { return (Point)d.GetValue(DragStartPointProperty); } private static void SetDragStartPoint(DependencyObject d, Point value) { d.SetValue(DragStartPointProperty, value); } private static readonly DependencyProperty DragLastPointProperty = DependencyProperty.RegisterAttached("DragLastPoint", typeof(Point), typeof(MouseDragBehavior), new FrameworkPropertyMetadata((Point)new Point())); private static Point GetDragLastPoint(DependencyObject d) { return (Point)d.GetValue(DragLastPointProperty); } private static void SetDragLastPoint(DependencyObject d, Point value) { d.SetValue(DragLastPointProperty, value); } public static readonly DependencyProperty HandleDragProperty = DependencyProperty.RegisterAttached("HandleDrag", typeof(bool), typeof(MouseDragBehavior), new FrameworkPropertyMetadata((bool)false, new PropertyChangedCallback(OnHandleDragChanged))); public static bool GetHandleDrag(DependencyObject d) { return (bool)d.GetValue(HandleDragProperty); } public static void SetHandleDrag(DependencyObject d, bool value) { d.SetValue(HandleDragProperty, value); } private static void OnHandleDragChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ScrollViewer scrollViewer = d as ScrollViewer; if ((bool)e.NewValue) { scrollViewer.PreviewMouseDown += new MouseButtonEventHandler(target_PreviewMouseDown); scrollViewer.PreviewMouseMove += new MouseEventHandler(target_PreviewMouseMove); scrollViewer.PreviewMouseUp += new MouseButtonEventHandler(target_PreviewMouseUp); scrollViewer.MouseLeave += new MouseEventHandler(target_MouseLeave); } else { scrollViewer.PreviewMouseDown -= new MouseButtonEventHandler(target_PreviewMouseDown); scrollViewer.PreviewMouseMove -= new MouseEventHandler(target_PreviewMouseMove); scrollViewer.PreviewMouseUp -= new MouseButtonEventHandler(target_PreviewMouseUp); scrollViewer.MouseLeave -= new MouseEventHandler(target_MouseLeave); } } private static bool IsDragging(ScrollViewer scrollViewer) { Point posNow = Mouse.GetPosition(scrollViewer); return ((Math.Abs(GetDragStartPoint(scrollViewer).X - posNow.X) > GetTolerance(scrollViewer)) || (Math.Abs(GetDragStartPoint(scrollViewer).Y - posNow.Y) > GetTolerance(scrollViewer))); } private static void target_PreviewMouseDown(object sender, MouseButtonEventArgs e) { var scrollViewer = (ScrollViewer)sender; var mousePos = e.GetPosition(scrollViewer); scrollViewer.CaptureMouse(); if (GetDragLastPoint(scrollViewer) == new Point()) { SetDragStartPoint(scrollViewer, mousePos); SetDragLastPoint(scrollViewer, mousePos); } if (IsDragging(scrollViewer)) { e.Handled = true; return; } else scrollViewer.ReleaseMouseCapture(); } private static void target_PreviewMouseMove(object sender, MouseEventArgs e) { var scrollViewer = (ScrollViewer)sender; if (GetDragLastPoint(scrollViewer) != new Point()) { Point posNow = e.GetPosition(scrollViewer); double dX = posNow.X - GetDragLastPoint(scrollViewer).X; double dY = posNow.Y - GetDragLastPoint(scrollViewer).Y; SetDragLastPoint(scrollViewer, posNow); if (!IsDragging(scrollViewer)) return; if (GetReverseAxis(scrollViewer)) { scrollViewer.ScrollToHorizontalOffset(scrollViewer.HorizontalOffset + dX); scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset + dY); } else { scrollViewer.ScrollToHorizontalOffset(scrollViewer.HorizontalOffset - dX); scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - dY); } } e.Handled = true; } private static void target_PreviewMouseUp(object sender, MouseButtonEventArgs e) { var scrollViewer = (ScrollViewer)sender; if (GetDragLastPoint(scrollViewer) != new Point() && IsDragging(scrollViewer)) { e.Handled = true; scrollViewer.ReleaseMouseCapture(); } Reset(scrollViewer); } private static void target_MouseLeave(object sender, MouseEventArgs e) { var scrollViewer = (ScrollViewer)sender; Reset(scrollViewer); } private static void Reset(ScrollViewer scrollViewer) { SetDragLastPoint(scrollViewer, new Point()); SetDragStartPoint(scrollViewer, new Point()); } } }To use it simply reference the namespace and set MouseDragBehavior.HandleDrag to true in your
ScrollViewer declaration:
<ScrollViewer xmlns:my="clr-namespace:MouseExtensions" my:MouseDragBehavior.HandleDrag="True" my:MouseDragBehavior.ReverseAxis="True" />Link to comment
https://www.neowin.net/forum/topic/1022710-cwpf-mouse-drag-behavior/Share on other sites
3 answers to this question
Recommended Posts