源项目地址:https://github.com/Microsoft/...
以下是把样例转换为简要说明,同时给出实际运行效果及关键代码:
CompositionTarget
ps:刷新率=总计数/总时间秒数
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);
}
DrawingVisual
可视化宿主类继承: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;
}
构建可视化对象方框、文本、圆方法
// 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;
}
处理点击事件,根据可视化树命中测试来获取命中结果,在回调函数更改命中对象透明度
// 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;
}
以下必须部件:由系统自动调到,获取可视化子对象的索引属性
// 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;
}
}
- Form类
PS:有个小bug,当命中一个创建的圆环控件时,右键时会移除所有的圆圈,原因为:控件的根未处理好。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。