diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs
index 4b81935452..08ac17d0c4 100644
--- a/samples/ControlCatalog.NetCore/Program.cs
+++ b/samples/ControlCatalog.NetCore/Program.cs
@@ -114,7 +114,7 @@ namespace ControlCatalog.NetCore
})
.With(new Win32PlatformOptions
{
- EnableMultitouch = true
+ EnableWmPointerEvents = true
})
.UseSkia()
.UseManagedSystemDialogs()
diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml
index 85f278b5fa..c61ba231e0 100644
--- a/samples/ControlCatalog/MainView.xaml
+++ b/samples/ControlCatalog/MainView.xaml
@@ -106,7 +106,7 @@
-
+
diff --git a/samples/ControlCatalog/Pages/PointerCanvas.cs b/samples/ControlCatalog/Pages/PointerCanvas.cs
new file mode 100644
index 0000000000..b815a573f2
--- /dev/null
+++ b/samples/ControlCatalog/Pages/PointerCanvas.cs
@@ -0,0 +1,221 @@
+#nullable enable
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Threading;
+
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Input;
+using Avalonia.Media;
+using Avalonia.Threading;
+
+namespace ControlCatalog.Pages;
+
+public class PointerCanvas : Control
+{
+ private readonly Stopwatch _stopwatch = Stopwatch.StartNew();
+ private int _events;
+ private IDisposable? _statusUpdated;
+ private Dictionary _pointers = new();
+ private PointerPointProperties? _lastProperties;
+ class PointerPoints
+ {
+ struct CanvasPoint
+ {
+ public IBrush Brush;
+ public Point Point;
+ public double Radius;
+ public double? Pressure;
+ }
+
+ readonly CanvasPoint[] _points = new CanvasPoint[1000];
+ int _index;
+
+ public void Render(DrawingContext context, bool drawPoints)
+ {
+ CanvasPoint? prev = null;
+ for (var c = 0; c < _points.Length; c++)
+ {
+ var i = (c + _index) % _points.Length;
+ var pt = _points[i];
+ var pressure = (pt.Pressure ?? prev?.Pressure ?? 0.5);
+ var thickness = pressure * 10;
+ var radius = pressure * pt.Radius;
+
+ if (drawPoints)
+ {
+ if (pt.Brush != null)
+ {
+ context.DrawEllipse(pt.Brush, null, pt.Point, radius, radius);
+ }
+ }
+ else
+ {
+ if (prev.HasValue && prev.Value.Brush != null && pt.Brush != null
+ && prev.Value.Pressure != null && pt.Pressure != null)
+ {
+ var linePen = new Pen(Brushes.Black, thickness, null, PenLineCap.Round, PenLineJoin.Round);
+ context.DrawLine(linePen, prev.Value.Point, pt.Point);
+ }
+ }
+ prev = pt;
+ }
+
+ }
+
+ void AddPoint(Point pt, IBrush brush, double radius, float? pressure = null)
+ {
+ _points[_index] = new CanvasPoint { Point = pt, Brush = brush, Radius = radius, Pressure = pressure };
+ _index = (_index + 1) % _points.Length;
+ }
+
+ public void HandleEvent(PointerEventArgs e, Visual v)
+ {
+ e.Handled = true;
+ var currentPoint = e.GetCurrentPoint(v);
+ if (e.RoutedEvent == PointerPressedEvent)
+ AddPoint(currentPoint.Position, Brushes.Green, 10);
+ else if (e.RoutedEvent == PointerReleasedEvent)
+ AddPoint(currentPoint.Position, Brushes.Red, 10);
+ else
+ {
+ var pts = e.GetIntermediatePoints(v);
+ for (var c = 0; c < pts.Count; c++)
+ {
+ var pt = pts[c];
+ AddPoint(pt.Position, c == pts.Count - 1 ? Brushes.Blue : Brushes.Black,
+ c == pts.Count - 1 ? 5 : 2, pt.Properties.Pressure);
+ }
+ }
+ }
+ }
+
+ private int _threadSleep;
+ public static DirectProperty ThreadSleepProperty =
+ AvaloniaProperty.RegisterDirect(nameof(ThreadSleep), c => c.ThreadSleep, (c, v) => c.ThreadSleep = v);
+
+ public int ThreadSleep
+ {
+ get => _threadSleep;
+ set => SetAndRaise(ThreadSleepProperty, ref _threadSleep, value);
+ }
+
+ private bool _drawOnlyPoints;
+ public static DirectProperty DrawOnlyPointsProperty =
+ AvaloniaProperty.RegisterDirect(nameof(DrawOnlyPoints), c => c.DrawOnlyPoints, (c, v) => c.DrawOnlyPoints = v);
+
+ public bool DrawOnlyPoints
+ {
+ get => _drawOnlyPoints;
+ set => SetAndRaise(DrawOnlyPointsProperty, ref _drawOnlyPoints, value);
+ }
+
+ private string? _status;
+ public static DirectProperty StatusProperty =
+ AvaloniaProperty.RegisterDirect(nameof(DrawOnlyPoints), c => c.Status, (c, v) => c.Status = v,
+ defaultBindingMode: Avalonia.Data.BindingMode.TwoWay);
+
+ public string? Status
+ {
+ get => _status;
+ set => SetAndRaise(StatusProperty, ref _status, value);
+ }
+
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnAttachedToVisualTree(e);
+
+ _statusUpdated = DispatcherTimer.Run(() =>
+ {
+ if (_stopwatch.Elapsed.TotalSeconds > 1)
+ {
+ Status = $@"Events per second: {(_events / _stopwatch.Elapsed.TotalSeconds)}
+PointerUpdateKind: {_lastProperties?.PointerUpdateKind}
+IsLeftButtonPressed: {_lastProperties?.IsLeftButtonPressed}
+IsRightButtonPressed: {_lastProperties?.IsRightButtonPressed}
+IsMiddleButtonPressed: {_lastProperties?.IsMiddleButtonPressed}
+IsXButton1Pressed: {_lastProperties?.IsXButton1Pressed}
+IsXButton2Pressed: {_lastProperties?.IsXButton2Pressed}
+IsBarrelButtonPressed: {_lastProperties?.IsBarrelButtonPressed}
+IsEraser: {_lastProperties?.IsEraser}
+IsInverted: {_lastProperties?.IsInverted}
+Pressure: {_lastProperties?.Pressure}
+XTilt: {_lastProperties?.XTilt}
+YTilt: {_lastProperties?.YTilt}
+Twist: {_lastProperties?.Twist}";
+ _stopwatch.Restart();
+ _events = 0;
+ }
+
+ return true;
+ }, TimeSpan.FromMilliseconds(10));
+ }
+
+ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnDetachedFromVisualTree(e);
+
+ _statusUpdated?.Dispose();
+ }
+
+ void HandleEvent(PointerEventArgs e)
+ {
+ _events++;
+ if (_threadSleep != 0)
+ {
+ Thread.Sleep(_threadSleep);
+ }
+ InvalidateVisual();
+
+ if (e.RoutedEvent == PointerReleasedEvent && e.Pointer.Type == PointerType.Touch)
+ {
+ _pointers.Remove(e.Pointer.Id);
+ return;
+ }
+
+ var lastPointer = e.GetCurrentPoint(this);
+ _lastProperties = lastPointer.Properties;
+
+ if (e.Pointer.Type != PointerType.Pen
+ || lastPointer.Properties.Pressure > 0)
+ {
+ if (!_pointers.TryGetValue(e.Pointer.Id, out var pt))
+ _pointers[e.Pointer.Id] = pt = new PointerPoints();
+ pt.HandleEvent(e, this);
+ }
+ }
+
+ public override void Render(DrawingContext context)
+ {
+ context.FillRectangle(Brushes.White, Bounds);
+ foreach (var pt in _pointers.Values)
+ pt.Render(context, _drawOnlyPoints);
+ base.Render(context);
+ }
+
+ protected override void OnPointerPressed(PointerPressedEventArgs e)
+ {
+ if (e.ClickCount == 2)
+ {
+ _pointers.Clear();
+ InvalidateVisual();
+ return;
+ }
+
+ HandleEvent(e);
+ base.OnPointerPressed(e);
+ }
+
+ protected override void OnPointerMoved(PointerEventArgs e)
+ {
+ HandleEvent(e);
+ base.OnPointerMoved(e);
+ }
+
+ protected override void OnPointerReleased(PointerReleasedEventArgs e)
+ {
+ HandleEvent(e);
+ base.OnPointerReleased(e);
+ }
+}
diff --git a/samples/ControlCatalog/Pages/PointerContactsTab.cs b/samples/ControlCatalog/Pages/PointerContactsTab.cs
new file mode 100644
index 0000000000..b6aabebf99
--- /dev/null
+++ b/samples/ControlCatalog/Pages/PointerContactsTab.cs
@@ -0,0 +1,109 @@
+#nullable enable
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reactive.Linq;
+
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Input;
+using Avalonia.Media;
+using Avalonia.Media.Immutable;
+
+namespace ControlCatalog.Pages;
+
+public class PointerContactsTab : Control
+{
+ class PointerInfo
+ {
+ public Point Point { get; set; }
+ public Color Color { get; set; }
+ }
+
+ private static Color[] AllColors = new[]
+ {
+ Colors.Aqua,
+ Colors.Beige,
+ Colors.Chartreuse,
+ Colors.Coral,
+ Colors.Fuchsia,
+ Colors.Crimson,
+ Colors.Lavender,
+ Colors.Orange,
+ Colors.Orchid,
+ Colors.ForestGreen,
+ Colors.SteelBlue,
+ Colors.PapayaWhip,
+ Colors.PaleVioletRed,
+ Colors.Goldenrod,
+ Colors.Maroon,
+ Colors.Moccasin,
+ Colors.Navy,
+ Colors.Wheat,
+ Colors.Violet,
+ Colors.Sienna,
+ Colors.Indigo,
+ Colors.Honeydew
+ };
+
+ private Dictionary _pointers = new Dictionary();
+
+ public PointerContactsTab()
+ {
+ ClipToBounds = true;
+ }
+
+ void UpdatePointer(PointerEventArgs e)
+ {
+ if (!_pointers.TryGetValue(e.Pointer, out var info))
+ {
+ if (e.RoutedEvent == PointerMovedEvent)
+ return;
+ var colors = AllColors.Except(_pointers.Values.Select(c => c.Color)).ToArray();
+ var color = colors[new Random().Next(0, colors.Length - 1)];
+ _pointers[e.Pointer] = info = new PointerInfo { Color = color };
+ }
+
+ info.Point = e.GetPosition(this);
+ InvalidateVisual();
+ }
+
+ protected override void OnPointerPressed(PointerPressedEventArgs e)
+ {
+ UpdatePointer(e);
+ e.Pointer.Capture(this);
+ e.Handled = true;
+ base.OnPointerPressed(e);
+ }
+
+ protected override void OnPointerMoved(PointerEventArgs e)
+ {
+ UpdatePointer(e);
+ e.Handled = true;
+ base.OnPointerMoved(e);
+ }
+
+ protected override void OnPointerReleased(PointerReleasedEventArgs e)
+ {
+ _pointers.Remove(e.Pointer);
+ e.Handled = true;
+ InvalidateVisual();
+ }
+
+ protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e)
+ {
+ _pointers.Remove(e.Pointer);
+ InvalidateVisual();
+ }
+
+ public override void Render(DrawingContext context)
+ {
+ context.FillRectangle(Brushes.Transparent, new Rect(default, Bounds.Size));
+ foreach (var pt in _pointers.Values)
+ {
+ var brush = new ImmutableSolidColorBrush(pt.Color);
+
+ context.DrawEllipse(brush, null, pt.Point, 75, 75);
+ }
+ }
+}
diff --git a/samples/ControlCatalog/Pages/PointersPage.cs b/samples/ControlCatalog/Pages/PointersPage.cs
deleted file mode 100644
index 0668c248c7..0000000000
--- a/samples/ControlCatalog/Pages/PointersPage.cs
+++ /dev/null
@@ -1,394 +0,0 @@
-#nullable enable
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
-using System.Reactive.Linq;
-using System.Runtime.InteropServices;
-using System.Threading;
-using Avalonia;
-using Avalonia.Controls;
-using Avalonia.Controls.Documents;
-using Avalonia.Input;
-using Avalonia.Layout;
-using Avalonia.Media;
-using Avalonia.Media.Immutable;
-using Avalonia.Threading;
-using Avalonia.VisualTree;
-
-namespace ControlCatalog.Pages;
-
-public class PointersPage : Decorator
-{
- public PointersPage()
- {
- Child = new TabControl
- {
- Items = new[]
- {
- new TabItem() { Header = "Contacts", Content = new PointerContactsTab() },
- new TabItem() { Header = "IntermediatePoints", Content = new PointerIntermediatePointsTab() },
- new TabItem() { Header = "Pressure", Content = new PointerPressureTab() }
- }
- };
- }
-
-
- class PointerContactsTab : Control
- {
- class PointerInfo
- {
- public Point Point { get; set; }
- public Color Color { get; set; }
- }
-
- private static Color[] AllColors = new[]
- {
- Colors.Aqua,
- Colors.Beige,
- Colors.Chartreuse,
- Colors.Coral,
- Colors.Fuchsia,
- Colors.Crimson,
- Colors.Lavender,
- Colors.Orange,
- Colors.Orchid,
- Colors.ForestGreen,
- Colors.SteelBlue,
- Colors.PapayaWhip,
- Colors.PaleVioletRed,
- Colors.Goldenrod,
- Colors.Maroon,
- Colors.Moccasin,
- Colors.Navy,
- Colors.Wheat,
- Colors.Violet,
- Colors.Sienna,
- Colors.Indigo,
- Colors.Honeydew
- };
-
- private Dictionary _pointers = new Dictionary();
-
- public PointerContactsTab()
- {
- ClipToBounds = true;
- }
-
- void UpdatePointer(PointerEventArgs e)
- {
- if (!_pointers.TryGetValue(e.Pointer, out var info))
- {
- if (e.RoutedEvent == PointerMovedEvent)
- return;
- var colors = AllColors.Except(_pointers.Values.Select(c => c.Color)).ToArray();
- var color = colors[new Random().Next(0, colors.Length - 1)];
- _pointers[e.Pointer] = info = new PointerInfo {Color = color};
- }
-
- info.Point = e.GetPosition(this);
- InvalidateVisual();
- }
-
- protected override void OnPointerPressed(PointerPressedEventArgs e)
- {
- UpdatePointer(e);
- e.Pointer.Capture(this);
- e.Handled = true;
- base.OnPointerPressed(e);
- }
-
- protected override void OnPointerMoved(PointerEventArgs e)
- {
- UpdatePointer(e);
- e.Handled = true;
- base.OnPointerMoved(e);
- }
-
- protected override void OnPointerReleased(PointerReleasedEventArgs e)
- {
- _pointers.Remove(e.Pointer);
- e.Handled = true;
- InvalidateVisual();
- }
-
- protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e)
- {
- _pointers.Remove(e.Pointer);
- InvalidateVisual();
- }
-
- public override void Render(DrawingContext context)
- {
- context.FillRectangle(Brushes.Transparent, new Rect(default, Bounds.Size));
- foreach (var pt in _pointers.Values)
- {
- var brush = new ImmutableSolidColorBrush(pt.Color);
-
- context.DrawEllipse(brush, null, pt.Point, 75, 75);
- }
- }
- }
-
- public class PointerIntermediatePointsTab : Decorator
- {
- public PointerIntermediatePointsTab()
- {
- this[TextElement.ForegroundProperty] = Brushes.Black;
- var slider = new Slider
- {
- Margin = new Thickness(5),
- Minimum = 0,
- Maximum = 500
- };
-
- var status = new TextBlock()
- {
- HorizontalAlignment = HorizontalAlignment.Left,
- VerticalAlignment = VerticalAlignment.Top,
- };
- Child = new Grid
- {
- Children =
- {
- new PointerCanvas(slider, status, true),
- new Border
- {
- Background = Brushes.LightYellow,
- Child = new StackPanel
- {
- Children =
- {
- new StackPanel
- {
- Orientation = Orientation.Horizontal,
- Children =
- {
- new TextBlock { Text = "Thread sleep:" },
- new TextBlock()
- {
- [!TextBlock.TextProperty] =slider.GetObservable(Slider.ValueProperty)
- .Select(x=>x.ToString()).ToBinding()
- }
- }
- },
- slider
- }
- },
-
- HorizontalAlignment = HorizontalAlignment.Right,
- VerticalAlignment = VerticalAlignment.Top,
- Width = 300,
- Height = 60
- },
- status
- }
- };
- }
- }
-
- public class PointerPressureTab : Decorator
- {
- public PointerPressureTab()
- {
- this[TextBlock.ForegroundProperty] = Brushes.Black;
-
- var status = new TextBlock()
- {
- HorizontalAlignment = HorizontalAlignment.Left,
- VerticalAlignment = VerticalAlignment.Top,
- FontSize = 12
- };
- Child = new Grid
- {
- Children =
- {
- new PointerCanvas(null, status, false),
- status
- }
- };
- }
- }
-
- class PointerCanvas : Control
- {
- private readonly Slider? _slider;
- private readonly TextBlock _status;
- private readonly bool _drawPoints;
- private int _events;
- private Stopwatch _stopwatch = Stopwatch.StartNew();
- private IDisposable? _statusUpdated;
- private Dictionary _pointers = new();
- private PointerPointProperties? _lastProperties;
- class PointerPoints
- {
- struct CanvasPoint
- {
- public IBrush Brush;
- public Point Point;
- public double Radius;
- public double Pressure;
- }
-
- readonly CanvasPoint[] _points = new CanvasPoint[1000];
- int _index;
-
- public void Render(DrawingContext context, bool drawPoints)
- {
-
- CanvasPoint? prev = null;
- for (var c = 0; c < _points.Length; c++)
- {
- var i = (c + _index) % _points.Length;
- var pt = _points[i];
- var thickness = pt.Pressure == 0 ? 1 : (pt.Pressure / 1024) * 5;
-
- if (drawPoints)
- {
- if (prev.HasValue && prev.Value.Brush != null && pt.Brush != null)
- context.DrawLine(new Pen(Brushes.Black, thickness), prev.Value.Point, pt.Point);
- if (pt.Brush != null)
- context.DrawEllipse(pt.Brush, null, pt.Point, pt.Radius, pt.Radius);
- }
- else
- {
- if (prev.HasValue && prev.Value.Brush != null && pt.Brush != null)
- context.DrawLine(new Pen(Brushes.Black, thickness, lineCap: PenLineCap.Round, lineJoin: PenLineJoin.Round), prev.Value.Point, pt.Point);
- }
- prev = pt;
- }
-
- }
-
- void AddPoint(Point pt, IBrush brush, double radius, float pressure)
- {
- _points[_index] = new CanvasPoint { Point = pt, Brush = brush, Radius = radius, Pressure = pressure };
- _index = (_index + 1) % _points.Length;
- }
-
- public void HandleEvent(PointerEventArgs e, Visual v)
- {
- e.Handled = true;
- var currentPoint = e.GetCurrentPoint(v);
- if (e.RoutedEvent == PointerPressedEvent)
- AddPoint(currentPoint.Position, Brushes.Green, 10, currentPoint.Properties.Pressure);
- else if (e.RoutedEvent == PointerReleasedEvent)
- AddPoint(currentPoint.Position, Brushes.Red, 10, currentPoint.Properties.Pressure);
- else
- {
- var pts = e.GetIntermediatePoints(v);
- for (var c = 0; c < pts.Count; c++)
- {
- var pt = pts[c];
- AddPoint(pt.Position, c == pts.Count - 1 ? Brushes.Blue : Brushes.Black,
- c == pts.Count - 1 ? 5 : 2, pt.Properties.Pressure);
- }
- }
- }
- }
-
- public PointerCanvas(Slider? slider, TextBlock status, bool drawPoints)
- {
- _slider = slider;
- _status = status;
- _drawPoints = drawPoints;
- }
-
- protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
- {
- base.OnAttachedToVisualTree(e);
-
- _statusUpdated = DispatcherTimer.Run(() =>
- {
- if (_stopwatch.Elapsed.TotalSeconds > 1)
- {
- _status.Text = $@"Events per second: {(_events / _stopwatch.Elapsed.TotalSeconds)}
-PointerUpdateKind: {_lastProperties?.PointerUpdateKind}
-IsLeftButtonPressed: {_lastProperties?.IsLeftButtonPressed}
-IsRightButtonPressed: {_lastProperties?.IsRightButtonPressed}
-IsMiddleButtonPressed: {_lastProperties?.IsMiddleButtonPressed}
-IsXButton1Pressed: {_lastProperties?.IsXButton1Pressed}
-IsXButton2Pressed: {_lastProperties?.IsXButton2Pressed}
-IsBarrelButtonPressed: {_lastProperties?.IsBarrelButtonPressed}
-IsEraser: {_lastProperties?.IsEraser}
-IsInverted: {_lastProperties?.IsInverted}
-Pressure: {_lastProperties?.Pressure}
-XTilt: {_lastProperties?.XTilt}
-YTilt: {_lastProperties?.YTilt}
-Twist: {_lastProperties?.Twist}";
- _stopwatch.Restart();
- _events = 0;
- }
-
- return true;
- }, TimeSpan.FromMilliseconds(10));
- }
-
- protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
- {
- base.OnDetachedFromVisualTree(e);
-
- _statusUpdated?.Dispose();
- }
-
- void HandleEvent(PointerEventArgs e)
- {
- _events++;
- if (_slider != null)
- {
- Thread.Sleep((int)_slider.Value);
- }
- InvalidateVisual();
-
- if (e.RoutedEvent == PointerReleasedEvent && e.Pointer.Type == PointerType.Touch)
- {
- _pointers.Remove(e.Pointer.Id);
- return;
- }
-
- var lastPointer = e.GetCurrentPoint(this);
- _lastProperties = lastPointer.Properties;
-
- if (e.Pointer.Type != PointerType.Pen
- || lastPointer.Properties.Pressure > 0)
- {
- if (!_pointers.TryGetValue(e.Pointer.Id, out var pt))
- _pointers[e.Pointer.Id] = pt = new PointerPoints();
- pt.HandleEvent(e, this);
- }
- }
-
- public override void Render(DrawingContext context)
- {
- context.FillRectangle(Brushes.White, Bounds);
- foreach (var pt in _pointers.Values)
- pt.Render(context, _drawPoints);
- base.Render(context);
- }
-
- protected override void OnPointerPressed(PointerPressedEventArgs e)
- {
- if (e.ClickCount == 2)
- {
- _pointers.Clear();
- InvalidateVisual();
- return;
- }
-
- HandleEvent(e);
- base.OnPointerPressed(e);
- }
-
- protected override void OnPointerMoved(PointerEventArgs e)
- {
- HandleEvent(e);
- base.OnPointerMoved(e);
- }
-
- protected override void OnPointerReleased(PointerReleasedEventArgs e)
- {
- HandleEvent(e);
- base.OnPointerReleased(e);
- }
- }
-}
diff --git a/samples/ControlCatalog/Pages/PointersPage.xaml b/samples/ControlCatalog/Pages/PointersPage.xaml
new file mode 100644
index 0000000000..1281ec77b6
--- /dev/null
+++ b/samples/ControlCatalog/Pages/PointersPage.xaml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Capture 1
+
+
+ Capture 2
+
+
+
+
+
diff --git a/samples/ControlCatalog/Pages/PointersPage.xaml.cs b/samples/ControlCatalog/Pages/PointersPage.xaml.cs
new file mode 100644
index 0000000000..977cee3d58
--- /dev/null
+++ b/samples/ControlCatalog/Pages/PointersPage.xaml.cs
@@ -0,0 +1,76 @@
+using System;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Input;
+using Avalonia.Interactivity;
+using Avalonia.Markup.Xaml;
+
+namespace ControlCatalog.Pages;
+
+public class PointersPage : UserControl
+{
+ public PointersPage()
+ {
+ this.InitializeComponent();
+
+ var border1 = this.Get("BorderCapture1");
+ var border2 = this.Get("BorderCapture2");
+
+ border1.PointerPressed += Border_PointerPressed;
+ border1.PointerReleased += Border_PointerReleased;
+ border1.PointerCaptureLost += Border_PointerCaptureLost;
+ border1.PointerMoved += Border_PointerUpdated;
+ border1.PointerEnter += Border_PointerUpdated;
+ border1.PointerLeave += Border_PointerUpdated;
+
+ border2.PointerPressed += Border_PointerPressed;
+ border2.PointerReleased += Border_PointerReleased;
+ border2.PointerCaptureLost += Border_PointerCaptureLost;
+ border2.PointerMoved += Border_PointerUpdated;
+ border2.PointerEnter += Border_PointerUpdated;
+ border2.PointerLeave += Border_PointerUpdated;
+ }
+
+ private void Border_PointerUpdated(object sender, PointerEventArgs e)
+ {
+ var textBlock = (TextBlock)((Border)sender).Child;
+ var position = e.GetPosition((Border)sender);
+ textBlock.Text = @$"Captured: {e.Pointer.Captured == sender}
+PointerId: {e.Pointer.Id}
+Position: {(int)position.X} {(int)position.Y}";
+ e.Handled = true;
+ }
+
+ private void Border_PointerCaptureLost(object sender, PointerCaptureLostEventArgs e)
+ {
+ var textBlock = (TextBlock)((Border)sender).Child;
+ textBlock.Text = @$"Captured: {e.Pointer.Captured == sender}
+PointerId: {e.Pointer.Id}
+Position: ??? ???";
+ e.Handled = true;
+ }
+
+ private void Border_PointerReleased(object sender, PointerReleasedEventArgs e)
+ {
+ if (e.Pointer.Captured == sender)
+ {
+ e.Pointer.Capture(null);
+ e.Handled = true;
+ }
+ else
+ {
+ throw new InvalidOperationException("How?");
+ }
+ }
+
+ private void Border_PointerPressed(object sender, PointerPressedEventArgs e)
+ {
+ e.Pointer.Capture((Border)sender);
+ e.Handled = true;
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+}