c# - 增加控件 WPF 上单击事件的余地
问题描述
我的 Line 控件上有一个单击事件,它打开了一个上下文菜单。我必须设置线条的方式意味着您在必须单击以显示上下文菜单的位置方面没有太多余地,所以我想知道是否有办法增加“半径” ' 对于控件是允许的,因此即使它稍微偏离了线控件本身,它也会触发 click 事件。提前致谢。
解决方案
您可以PreviewMouseDown
在您的父级上处理该事件Lines
,然后使用“扩展的命中区域”进行命中测试....并查看是否与您的任何一条线相交。
您也可以通过用另一个元素(例如Grid
.
这是一些示例代码:
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="348" Width="483" PreviewMouseDown="Window_PreviewMouseDown">
<StackPanel VerticalAlignment="Center">
<Line x:Name="line1" X1="0" Y1="10" X2="200" Y2="10" Stroke="Yellow" StrokeThickness="20" Width="200" Height="20" Tag="yellow"/>
<Line x:Name="line2" X1="0" Y1="10" X2="200" Y2="10" Stroke="Red" StrokeThickness="20" Width="200" Height="20" Tag="#red"/>
<Line x:Name="line3" X1="0" Y1="10" X2="200" Y2="10" Stroke="Green" StrokeThickness="20" Width="200" Height="20" Tag="#green"/>
<Line x:Name="line4" X1="0" Y1="10" X2="200" Y2="10" Stroke="Blue" StrokeThickness="20" Width="200" Height="20" Tag="#blue"/>
<Line x:Name="line5" X1="10" Y1="20" X2="180" Y2="20" Stroke="Cyan" StrokeThickness="20" Width="200" Height="40" Tag="cyan" MouseDown="Line_MouseDown"/>
<Line x:Name="line6" X1="10" Y1="20" X2="180" Y2="20" Stroke="Magenta" StrokeThickness="20" Width="200" Height="40" Tag="magenta" MouseDown="Line_MouseDown"/>
<Grid Width="200" Height="40" MouseDown="Grid_MouseDown" Background="Transparent">
<Line x:Name="line7" X1="10" Y1="10" X2="180" Y2="10" Stroke="Black" StrokeThickness="20" Width="200" Height="20" Tag="black" IsHitTestVisible="False"/>
</Grid>
</StackPanel>
</Window>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApp1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
ContextMenu m_menu = new ContextMenu();
Line m_hitline = null;
public MainWindow()
{
InitializeComponent();
m_menu.Items.Add("hit");
m_menu.Items.Add("MenuItem1");
m_menu.Items.Add("MenuItem2");
}
private void Window_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
Line linehit = null;
Point pthit = e.GetPosition((UIElement)sender);
HitTestResult htresult = VisualTreeHelper.HitTest(this, pthit);
linehit = htresult.VisualHit as Line;
if (linehit == null)
{
// Expand the hit test area by creating a geometry centered on the hit test point.
//
// You could use a RectangleGeometry if you like....or even something more complicated
EllipseGeometry expandedHitTestArea = new EllipseGeometry(pthit, 5.0, 5.0);
m_hitline = null;
// Set up a callback to receive the hit test result enumeration.
VisualTreeHelper.HitTest(this, null,
new HitTestResultCallback(MyHitTestResultCallback),
new GeometryHitTestParameters(expandedHitTestArea));
linehit = m_hitline;
}
if (linehit != null)
{
// mouse hit directly on line so show context menu for it
m_menu.Items[0] = "hit " + linehit.Tag;
m_menu.IsOpen = true;
}
}
public HitTestResultBehavior MyHitTestResultCallback(HitTestResult result)
{
// Retrieve the results of the hit test.
IntersectionDetail intersectionDetail = ((GeometryHitTestResult)result).IntersectionDetail;
switch (intersectionDetail)
{
case IntersectionDetail.FullyContains:
case IntersectionDetail.FullyInside:
case IntersectionDetail.Intersects:
Line linethatinterestedwithhitarea = result.VisualHit as Line;
// We ignore testing against none-line visuals (you may or may not want to do that)
//
// We will only do expanded hit testing against "lines" that have set a tag beginning with #
if (linethatinterestedwithhitarea != null)
{
// We will skip "expanded" hit testing on lines that have no Tag set
// i.e. the Yellow lines - just shows one way to "control" which lines you want
// to test against....you could also compare against a predefined list of lines
// you know you want to add this behaviour to.
if (!string.IsNullOrEmpty(linethatinterestedwithhitarea.Tag.ToString()) && linethatinterestedwithhitarea.Tag.ToString().StartsWith("#"))
{
m_hitline = linethatinterestedwithhitarea;
return HitTestResultBehavior.Stop;
}
}
return HitTestResultBehavior.Continue;
default:
return HitTestResultBehavior.Stop;
}
}
private void Line_MouseDown(object sender, MouseButtonEventArgs e)
{
Line linehit = sender as Line;
if (linehit != null)
{
m_menu.Items[0] = "hit " + linehit.Tag;
m_menu.IsOpen = true;
}
}
private void Grid_MouseDown(object sender, MouseButtonEventArgs e)
{
m_menu.Items[0] = "hit black";
m_menu.IsOpen = true;
}
}
}
推荐阅读
- java - JavaFX - 从gui接收事件后返回主线程
- php - Cakephp - 应用程序执行功能
- c# - 如何为 MonoGame 设置最小/最大窗口大小?
- java - log4j 多进程将日志内容写入一个日志文件
- wso2is - 使用 Rest API 将角色分配给用户时出错
- datatables - 数据表输入范围和 PDF 导出
- c# - 当设置为静态时,HttpClient 将如何处理多个地址?
- php - 对象属性不变
- php - webhook woocommerce 通过 php 到 mysql
- postgresql - PostgreSQL - 根据字段的值将表上的删除/更新限制为特定角色