我們有時候又需求從當前視覺樹中找一些東西,比如滑鼠按下的時候,看看滑鼠下的元素都有什么,又比如某塊區域下有哪些元素?某個坐標點下有哪些元素?
這些需求在使用 命中測驗的時候,可以非常方便和快速的去找到我們需要的內容,
簡單命中測驗
我們寫一個最簡單的命中測驗的示例,來了解命中測驗,我在一個畫板上在不同的位置放了3個圓形,給他們放置了不同的位置和填充不同的顏色,我們通過命中測驗判斷如果滑鼠在圓上抬起了,我們讀取當前圓的填充顏色,
<Window x:Class="WPFVisualTreeHelper.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WPFVisualTreeHelper" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid MouseLeftButtonUp="Grid_MouseLeftButtonUp"> <Canvas> <Ellipse Canvas.Left="30" Canvas.Top="200" Width="130" Height="130" Fill="Blue"/> <Ellipse Canvas.Left="110" Canvas.Top="0" Width="130" Height="130" Fill="Red"/> <Ellipse Canvas.Left="220" Canvas.Top="100" Width="130" Height="130" Fill="Yellow"/> <TextBlock Canvas.Left="0" Canvas.Top="0" Text="抬起滑鼠左鍵,開始對滑鼠所在點進行命中測驗" /> </Canvas> </Grid> </Window>
我們給Grid 添加了左鍵抬起的事件,
using System.Windows; using System.Windows.Input; using System.Windows.Media; using System.Windows.Shapes; namespace WPFVisualTreeHelper { /// <summary> /// MainWindow.xaml 的互動邏輯 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Grid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { var ellipse = GetVisual(e.GetPosition(this)); ; MessageBox.Show(ellipse?.Fill?.ToString()); } private Ellipse GetVisual(Point point) { HitTestResult hitResult = VisualTreeHelper.HitTest(this, point); var ellipse = hitResult.VisualHit as Ellipse; return ellipse; } } }
如果在圓上抬起,我們會顯示圓的填充色,

我們簡單命中測驗使用VisualTreeHelper來進行命中測驗,HitTest方法傳入2個引數,一個引數在什么元素上查找,第二個引數是坐標,回傳的物件是命中測驗的回傳結果,是DependencyObject型別的物件,我們的ui元素都是繼承自DependencyObject的,找到的結果是HitTestResult,需要我們自己轉換VisualHit到我們需要的型別,我們的方法如果不是該型別就回傳為空,因為是簡單命中測驗,后面會封裝一個命中測驗的方法,因為在命中測驗程序中,會有元素的層級關系,多個同型別元素集合,等等的需求,所以這里只了解什么是命中測驗,能看懂代碼就可以了,理解什么是命中測驗,就可以了,
復雜命中測驗
我們通過在VisualTreeHelper的HitTest上F12我們可以看到有3個方法的多載,

VisualTreeHelper類使用其他多載版本可以執行更復雜的命中測驗,我們可以檢索位于特定點的所有可視化物件,也可以使用特定區域的所有可視化物件,我們使用第一個多載方法,
因為在視覺樹下,有層級和同級多個元素的問題,為了使用這個功能,我們需要創建回呼函式,也就是第一個HitTest的第三個引數resultCallback,我們通過自上而下遍歷所有可視物件,如果發現了匹配的物件就使用回呼函式傳遞相關的內容直到找到所有的物件,

我們寫個例子,我們在上面的例子上多添加幾個圓形,并且把他們疊加起來,然后我們創建一個10*10像素的形狀,去檢索這個形狀坐標下的所有圓形,為了讓他們顯示層疊關系我設定了opacity屬性,并使用滑鼠右鍵來執行復雜命中測驗, <Window x:Class="WPFVisualTreeHelper.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WPFVisualTreeHelper" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid MouseLeftButtonUp="Grid_MouseLeftButtonUp" MouseRightButtonUp="Grid_MouseRightButtonUp"> <Canvas> <Ellipse Canvas.Left="30" Canvas.Top="200" Width="130" Height="130" Fill="Blue"/> <Ellipse Opacity="0.6" Canvas.Left="70" Canvas.Top="50" Width="130" Height="130" Fill="Violet"/> <Ellipse Opacity="0.6" Canvas.Left="150" Canvas.Top="50" Width="130" Height="130" Fill="Orange"/> <Ellipse Opacity="0.6" Canvas.Left="110" Canvas.Top="0" Width="130" Height="130" Fill="Red"/> <Ellipse Canvas.Left="220" Canvas.Top="100" Width="130" Height="130" Fill="Yellow"/> <TextBlock Canvas.Left="0" Canvas.Top="0" Text="抬起滑鼠左鍵,開始對滑鼠所在點進行命中測驗" /> </Canvas> </Grid> </Window>
后臺代碼:
using System.Collections.Generic; using System.Text; using System.Windows; using System.Windows.Input; using System.Windows.Media; using System.Windows.Shapes; namespace WPFVisualTreeHelper { /// <summary> /// MainWindow.xaml 的互動邏輯 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); DrawingVisual v = new DrawingVisual(); } #region 簡單命中測驗 private void Grid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { var ellipse = GetVisual(e.GetPosition(this)); ; MessageBox.Show(ellipse?.Fill?.ToString()); } private Ellipse GetVisual(Point point) { HitTestResult hitResult = VisualTreeHelper.HitTest(this, point); var ellipse = hitResult.VisualHit as Ellipse; return ellipse; } #endregion #region 復雜命中測驗 private void Grid_MouseRightButtonUp(object sender, MouseButtonEventArgs e) { Point pt = e.GetPosition((UIElement)sender); //我們定義一個10*10大小的幾何 EllipseGeometry expandedHitTestArea = new EllipseGeometry(pt, 10.0, 10.0); var ellipses = GetVisual(expandedHitTestArea); StringBuilder stringBuilder = new StringBuilder(); foreach (var item in ellipses) { stringBuilder.Append(item.Fill.ToString() + ","); } MessageBox.Show(stringBuilder.ToString()); } private HitTestResultBehavior HitTestCallback(HitTestResult result) { GeometryHitTestResult geometryResult = (GeometryHitTestResult)result; Ellipse visual = result.VisualHit as Ellipse; if (visual != null) { hits.Add(visual); } return HitTestResultBehavior.Continue; } List<Ellipse> hits = new List<Ellipse>(); private List<Ellipse> GetVisual(Geometry region) { hits.Clear(); GeometryHitTestParameters parameters = new GeometryHitTestParameters(region); HitTestResultCallback callback = new HitTestResultCallback(this.HitTestCallback); //第一個引數是我們要在什么容器內查找(我們現在是在整個window),第二個引數是篩選回呼值的方法,我們目前不需要, //第三個引數是命中測驗回呼結果,第四個引數是需要檢測的區域, VisualTreeHelper.HitTest(this, null, callback, parameters); return hits; } #endregion } }
HitTestCallback就是我們基于搜索結果回呼的關鍵方法,我們查找了這個幾何圖形下的所有ellispe,
命中測驗的封裝
這里搞錯了,這里封裝的是查找元素的封裝,,這個在補完C#的知識文章后更改,
我創建了一個C#相關的交流群,用于分享學習資料和討論問題,歡迎有興趣的小伙伴:QQ群:542633085
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/270470.html
標籤:WPF
上一篇:[Azure Devops] 使用 Azure Boards 管理作業
下一篇:WPF教程五:附加依賴項屬性
