源項目地址:https://github.com/Microsoft/...
如下是把樣例轉換爲簡要說明,同時給出實際運行效果及關鍵代碼:git
ps:刷新率=總計數/總時間秒數github
public MainWindow() { InitializeComponent(); // Add an event handler to update canvas background color just before it is rendered. System.Windows.Media.CompositionTarget.Rendering += UpdateColor; } // Called just before frame is rendered to allow custom drawing. protected void UpdateColor(object sender, EventArgs e) { if (_frameCounter++ == 0) { // Starting timing. _stopwatch.Start(); } // Determine frame rate in fps (frames per second). var frameRate = (long) (_frameCounter/_stopwatch.Elapsed.TotalSeconds); if (frameRate > 0) { // Update elapsed time, number of frames, and frame rate. myStopwatchLabel.Content = _stopwatch.Elapsed.ToString(); myFrameCounterLabel.Content = _frameCounter.ToString(CultureInfo.InvariantCulture); myFrameRateLabel.Content = frameRate.ToString(); } // Update the background of the canvas by converting MouseMove info to RGB info. var redColor = (byte) (_pt.X/3.0); var blueColor = (byte) (_pt.Y/2.0); myCanvas.Background = new SolidColorBrush(Color.FromRgb(redColor, 0x0, blueColor)); } public void MouseMoveHandler(object sender, MouseEventArgs e) { // Retreive the coordinates of the mouse button event. _pt = e.GetPosition((UIElement) sender); }
可視化宿主類繼承:public class MyVisualHost : FrameworkElement 構建可視化集合: // Create a collection of child visual objects. private readonly VisualCollection _children; public MyVisualHost() { _children = new VisualCollection(this) { CreateDrawingVisualRectangle(), CreateDrawingVisualText(), CreateDrawingVisualEllipses() }; // Add the event handler for MouseLeftButtonUp. MouseLeftButtonUp += MyVisualHost_MouseLeftButtonUp; }
構建可視化對象方框、文本、圓方法canvas
// Create a DrawingVisual that contains a rectangle. private System.Windows.Media.DrawingVisual CreateDrawingVisualRectangle() { System.Windows.Media.DrawingVisual drawingVisual = new System.Windows.Media.DrawingVisual(); // Retrieve the DrawingContext in order to create new drawing content. DrawingContext drawingContext = drawingVisual.RenderOpen(); // Create a rectangle and draw it in the DrawingContext. Rect rect = new Rect(new Point(160, 100), new Size(320, 80)); drawingContext.DrawRectangle(Brushes.LightBlue, null, rect); // Persist the drawing content. drawingContext.Close(); return drawingVisual; } // Create a DrawingVisual that contains text. private System.Windows.Media.DrawingVisual CreateDrawingVisualText() { // Create an instance of a DrawingVisual. System.Windows.Media.DrawingVisual drawingVisual = new System.Windows.Media.DrawingVisual(); // Retrieve the DrawingContext from the DrawingVisual. DrawingContext drawingContext = drawingVisual.RenderOpen(); // Draw a formatted text string into the DrawingContext. drawingContext.DrawText( new FormattedText("Click Me!", CultureInfo.GetCultureInfo("en-us"), FlowDirection.LeftToRight, new Typeface("Verdana"), 36, Brushes.Black), new Point(200, 116)); // Close the DrawingContext to persist changes to the DrawingVisual. drawingContext.Close(); return drawingVisual; } // Create a DrawingVisual that contains an ellipse. private System.Windows.Media.DrawingVisual CreateDrawingVisualEllipses() { System.Windows.Media.DrawingVisual drawingVisual = new System.Windows.Media.DrawingVisual(); DrawingContext drawingContext = drawingVisual.RenderOpen(); drawingContext.DrawEllipse(Brushes.Maroon, null, new Point(430, 136), 20, 20); drawingContext.Close(); return drawingVisual; }
處理點擊事件,根據可視化樹命中測試來獲取命中結果,在回調函數更改命中對象透明度dom
// Capture the mouse event and hit test the coordinate point value against // the child visual objects. private void MyVisualHost_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { // Retreive the coordinates of the mouse button event. Point pt = e.GetPosition((UIElement) sender); // Initiate the hit test by setting up a hit test result callback method. VisualTreeHelper.HitTest(this, null, MyCallback, new PointHitTestParameters(pt)); } // If a child visual object is hit, toggle its opacity to visually indicate a hit. public HitTestResultBehavior MyCallback(HitTestResult result) { if (result.VisualHit.GetType() == typeof (System.Windows.Media.DrawingVisual)) { ((System.Windows.Media.DrawingVisual) result.VisualHit).Opacity = ((System.Windows.Media.DrawingVisual) result.VisualHit).Opacity == 1.0 ? 0.4 : 1.0; } // Stop the hit test enumeration of objects in the visual tree. return HitTestResultBehavior.Stop; }
如下必須部件:由系統自動調到,獲取可視化子對象的索引屬性ide
// Provide a required override for the VisualChildrenCount property. protected override int VisualChildrenCount => _children.Count; // Provide a required override for the GetVisualChild method. protected override Visual GetVisualChild(int index) { if (index < 0 || index >= _children.Count) { throw new ArgumentOutOfRangeException(); } return _children[index]; }
public class MyShape : ContainerVisual { // Constant values from the "winuser.h" header file. public const int WmLbuttonup = 0x0202, WmRbuttonup = 0x0205; public static int NumberCircles = 5; public static double Radius = 50.0d; public static Random MyRandom = new Random(); public static ArrayList HitResultsList = new ArrayList(); internal MyShape() { // Create a random x:y coordinate for the shape. var left = MyRandom.Next(0, MyWindow.Width); var top = MyRandom.Next(0, MyWindow.Height); var currRadius = Radius; if (Radius == 0.0d) { currRadius = MyRandom.Next(30, 100); } // Draw five concentric circles for the shape. var r = currRadius; for (var i = 0; i < NumberCircles; i++) { new MyCircle(this, new Point(left, top), r); r = currRadius*(1.0d - ((i + 1.0d)/NumberCircles)); } } // Respond to WM_LBUTTONUP or WM_RBUTTONUP messages by determining which visual object was clicked. public static void OnHitTest(Point pt, int msg) { // Clear the contents of the list used for hit test results. HitResultsList.Clear(); // Determine whether to change the color of the circle or to delete the shape. if (msg == WmLbuttonup) { MyWindow.ChangeColor = true; } if (msg == WmRbuttonup) { MyWindow.ChangeColor = false; } // Set up a callback to receive the hit test results enumeration. VisualTreeHelper.HitTest(MyWindow.MyHwndSource.RootVisual, null, CircleHitTestResult, new PointHitTestParameters(pt)); // Perform actions on the hit test results list. if (HitResultsList.Count > 0) { ProcessHitTestResultsList(); } } // Handle the hit test results enumeration in the callback. internal static HitTestResultBehavior CircleHitTestResult(HitTestResult result) { // Add the hit test result to the list that will be processed after the enumeration. HitResultsList.Add(result.VisualHit); // Determine whether hit test should return only the top-most layer visual. if (MyWindow.TopmostLayer) { // Set the behavior to stop the enumeration of visuals. return HitTestResultBehavior.Stop; } // Set the behavior to continue the enumeration of visuals. // All visuals that intersect at the hit test coordinates are returned, // whether visible or not. return HitTestResultBehavior.Continue; } // Process the results of the hit test after the enumeration in the callback. // Do not add or remove objects from the visual tree until after the enumeration. internal static void ProcessHitTestResultsList() { foreach (MyCircle circle in HitResultsList) { // Determine whether to change the color of the ring or to delete the circle. if (MyWindow.ChangeColor) { // Draw a different color ring for the circle. circle.Render(); } else { if (circle.Parent == MyWindow.MyHwndSource.RootVisual) { // Remove the root visual by disposing of the host hwnd. MyWindow.MyHwndSource.Dispose(); MyWindow.MyHwndSource = null; return; } // Remove the shape that is the parent of the child circle. ((ContainerVisual) MyWindow.MyHwndSource.RootVisual).Children.Remove((Visual) circle.Parent); } } } internal class MyCircle : DrawingVisual { public Point Pt; private readonly double _radius; internal MyCircle(MyShape parent, Point pt, double radius) { Pt = pt; _radius = radius; Render(); // Add the circle as a child to the shape parent. parent.Children.Add(this); } internal void Render() { // Draw a circle. var dc = RenderOpen(); dc.DrawEllipse(new SolidColorBrush(MyColor.GenRandomColor()), null, Pt, _radius, _radius); dc.Close(); } } private class MyColor { private static readonly Random myRandom = new Random(); public static Color GenRandomColor() => Color.FromRgb((byte)myRandom.Next(0, 255), (byte)myRandom.Next(0, 255), (byte)myRandom.Next(0, 255)); } }
internal class MyWindow { // Constant values from the "winuser.h" header file. internal const int WsChild = 0x40000000, WsVisible = 0x10000000; // Constant values from the "winuser.h" header file. internal const int WmLbuttonup = 0x0202, WmRbuttonup = 0x0205; public static int Width = Form.ActiveForm.ClientSize.Width; public static int Height = Form.ActiveForm.ClientSize.Height; public static HwndSource MyHwndSource; public static bool TopmostLayer = true; public static bool ChangeColor; public static void FillWithCircles(IntPtr parentHwnd) { // Fill the client area of the form with randomly placed circles. for (var i = 0; i < 200; i++) { CreateShape(parentHwnd); } } public static void CreateShape(IntPtr parentHwnd) { // Create an instance of the shape. var myShape = new MyShape(); // Determine whether the host container window has been created. if (MyHwndSource == null) { // Create the host container window for the visual objects. CreateHostHwnd(parentHwnd); // Associate the shape with the host container window. MyHwndSource.RootVisual = myShape; } else { // Assign the shape as a child of the root visual. ((ContainerVisual) MyHwndSource.RootVisual).Children.Add(myShape); } } internal static void CreateHostHwnd(IntPtr parentHwnd) { // Set up the parameters for the host hwnd. var parameters = new HwndSourceParameters("Visual Hit Test", Width, Height) { WindowStyle = WsVisible | WsChild }; parameters.SetPosition(0, 24); parameters.ParentWindow = parentHwnd; parameters.HwndSourceHook = ApplicationMessageFilter; // Create the host hwnd for the visuals. MyHwndSource = new HwndSource(parameters); // Set the hwnd background color to the form's background color. MyHwndSource.CompositionTarget.BackgroundColor = Brushes.OldLace.Color; } internal static IntPtr ApplicationMessageFilter( IntPtr hwnd, int message, IntPtr wParam, IntPtr lParam, ref bool handled) { // Handle messages passed to the visual. switch (message) { // Handle the left and right mouse button up messages. case WmLbuttonup: case WmRbuttonup: var pt = new Point { X = (uint) lParam & 0x0000ffff, Y = (uint) lParam >> 16 }; // LOWORD = x // HIWORD = y MyShape.OnHitTest(pt, message); break; } return IntPtr.Zero; } }
PS:有個小bug,當命中一個建立的圓環控件時,右鍵時會移除全部的圓圈,緣由爲:控件的根未處理好。函數