Canvas上批量创建可视对象(DrawingVisual)管理,获取鼠标悬浮图形状态,并控制鼠标右键快捷菜单等...

近期公司有个新的定制,先简要说明下:

窗口上有个播放区域,区域上悬浮了很多可视对象(DrawingVisual),全部是动态生成的....

现在的需求是在这些矩形框上需要添加右键快捷菜单...

 

需求知道了,懂wpf的都知道,DrawingVisual是极其简约的一个视图对象,是没有属性可以绑定鼠标右键菜单,所以我的思路是,在Canvas上绑定快捷菜单,通过鼠标位置判断当前是否在矩形框里面,如果是,则显示对于的菜单,否则就隐藏起来

 

好了,需求和解决方案整理完成,那么就开始吧!

先看下整体效果:

<Window x:Class="DrawingHelper.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:DrawingHelper"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <Window.Resources>
        <ResourceDictionary>

            <ContextMenu x:Key="right">
                <MenuItem Header="默认的" />
                <MenuItem Header="单击框" Style="{DynamicResource item}" Click="MenuItem_Click" />
            </ContextMenu>

            <Style TargetType="MenuItem" x:Key="item">
                <Setter Property="Visibility" Value="Collapsed" />
                <Style.Triggers>
                    <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:MainWindow},Path=IsOverRect}" Value="true">
                        <Setter Property="Visibility" Value="Visible" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
            
        </ResourceDictionary>
    </Window.Resources>
    <Grid>
        <Grid x:Name="grid" Margin="10">
            <local:CustomCanvas x:Name="canvas" 
                                Background="#7d7d7d" 
                                ContextMenu="{StaticResource right}"  
                                MouseLeftButtonDown="canvas_MouseLeftButtonDown"
                                MouseMove="canvas_MouseMove"
                                MouseLeftButtonUp="canvas_MouseLeftButtonUp"/>
        </Grid>

        <StackPanel Orientation="Horizontal" VerticalAlignment="Top">
            <Button Content="生成" Click="Button_Click" />
        </StackPanel>
    </Grid>
</Window>
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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 DrawingHelper
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        List<Visual> vsOver = new List<Visual>();
        bool ismove = false;
        Visual slectedVisual;
        Vector vectorDownOffice;

        public MainWindow()
        {
            InitializeComponent();

            //注册事件
            //EventManager.RegisterClassHandler(typeof(CustomCanvas),
            //    CustomCanvas.RightContextMenuOpeningEvent,
            //    new RoutedEventHandler(RightContextMenuOpening), true);

            canvas.RightContextMenuOpening += RightContextMenuOpening;
        }

        /// <summary>
        /// 是否悬浮在框上
        /// </summary>
        public bool IsOverRect
        {
            get { return (bool)GetValue(IsOverRectProperty); }
            set { SetValue(IsOverRectProperty, value); }
        }

        public static readonly DependencyProperty IsOverRectProperty =
            DependencyProperty.Register("IsOverRect", typeof(bool), typeof(MainWindow), new PropertyMetadata(false));

        /// <summary>
        /// 单击生成
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Rect rect = new Rect();
            rect.Size = new Size(100, 100);

            Random r = new Random(DateTime.Now.Millisecond);
            var x = r.Next(0, (int)(canvas.ActualWidth - rect.Size.Width));
            var y = r.Next(0, (int)(canvas.ActualHeight - rect.Size.Width));

            rect.Location = new Point(x, y);

            canvas.AddVisual(rect);
        }

        /// <summary>
        /// 单击鼠标悬浮的框时
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MenuItem_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show($"当前选中:{vsOver.Count}");
        }

        /// <summary>
        /// 左键被按下时
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            ismove = true;
            var point = e.GetPosition(canvas);

            var vs = canvas.GetVisualByPoint(point);
            slectedVisual = vs.FirstOrDefault();

            if (slectedVisual is DrawingVisual drawing)
            {
                vectorDownOffice = point - drawing.Drawing.Bounds.Location;
            }
        }

        /// <summary>
        /// 移动时
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void canvas_MouseMove(object sender, MouseEventArgs e)
        {
            if (ismove && slectedVisual != null)
            {
                var point = e.GetPosition(canvas) - vectorDownOffice;

                Rect rect = new Rect();
                rect.Size = new Size(100, 100);
                rect.X = point.X;
                rect.Y = point.Y;

                canvas.MoveVisual(slectedVisual, rect);
            }
        }

        /// <summary>
        /// 左键被抬起时
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void canvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            ismove = false;
            slectedVisual = null;
        }

        /// <summary>
        /// 快捷菜单打开之前
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void RightContextMenuOpening(object sender, RoutedEventArgs e)
        {
            //获取相对面板的位置
            var point = Mouse.GetPosition(canvas);

            var vs = canvas.GetVisualByPoint(point);
            vsOver = new List<Visual>(vs);

            IsOverRect = vs.Length > 0;
        }
    }

}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace DrawingHelper
{
    /// <summary>
    /// 自定义面板
    /// </summary>
    class CustomCanvas : Canvas
    {
        /// <summary>
        /// 透明度
        /// </summary>
        double opacity = 0.8;

        public CustomCanvas()
        {
            ContextMenuOpening += CustomCanvas_ContextMenuOpening;
        }

        /// <summary>
        /// 菜单打开之前
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CustomCanvas_ContextMenuOpening(object sender, ContextMenuEventArgs e)
        {
            //RaiseEvent(new RoutedEventArgs(RightContextMenuOpeningEvent, e));//事件推送

            RightContextMenuOpening?.Invoke(this, e);
        }

        /*
        /// <summary>
        /// 声明自定义事件
        /// </summary>
        public static readonly RoutedEvent RightContextMenuOpeningEvent =
            EventManager.RegisterRoutedEvent(
                            "RightContextMenuOpeningEvent",
                            RoutingStrategy.Direct,
                            typeof(EventHandler<ContextMenuEventArgs>),
                            typeof(CustomCanvas));

        /// <summary>
        /// Raised when the VideoCellContextMenuOpeningEvent changed.
        /// </summary>
        public event RoutedEventHandler RightContextMenuOpening
        {
            add { AddHandler(RightContextMenuOpeningEvent, value); }
            remove { RemoveHandler(RightContextMenuOpeningEvent, value); }
        }
        */

        /// <summary>
        /// 右键菜单打开之前
        /// </summary>
        public event EventHandler<ContextMenuEventArgs> RightContextMenuOpening;

        /// <summary>
        /// 当前所有的图形
        /// </summary>
        List<Visual> vs = new List<Visual>();

        /// <summary>
        /// 边框画刷
        /// </summary>
        Pen pen = new Pen(new SolidColorBrush(Colors.Black), 2);

        /// <summary>
        /// 添加个图形
        /// </summary>
        /// <param name="rect"></param>
        public void AddVisual(Rect rect)
        {
            DrawingVisual dv = new DrawingVisual();

            using (var drawingContext = dv.RenderOpen())
            {
                drawingContext.PushOpacity(opacity);
                drawingContext.DrawRectangle(null, pen, rect);
            }

            vs.Add(dv);

            this.AddVisualChild(dv);
            this.AddLogicalChild(dv);
        }

        /// <summary>
        /// 图形总数
        /// </summary>
        protected override int VisualChildrenCount => vs.Count;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        protected override Visual GetVisualChild(int index)
        {
            return vs[index];
        }

        /// <summary>
        /// 根据坐标返回图形
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        public Visual[] GetVisualByPoint(Point point)
        {
            List<Visual> vis = new List<Visual>();
            vs.ForEach(c =>
            {
                if (c is DrawingVisual dv)
                {
                    var dr = dv.Drawing;

                    var x = dr.Bounds.X;
                    var y = dr.Bounds.Y;
                    var w = dr.Bounds.Width;
                    var h = dr.Bounds.Height;

                    if (point.X >= x && point.X <= x + w && point.Y >= y && point.Y <= y + h)
                    {
                        vis.Add(c);
                    }
                }
            });
            return vis.ToArray();
        }

        /// <summary>
        /// 移动指定的
        /// </summary>
        /// <param name="visual"></param>
        /// <param name="point"></param>
        public void MoveVisual(Visual visual, Rect rect)
        {
            if (visual is DrawingVisual drawing)
            {
                using (var dc = drawing.RenderOpen())
                {
                    dc.PushOpacity(opacity);
                    dc.DrawRectangle(null, pen, rect);
                }
            }
        }
    }
}

有需要的朋友,也可以移步下载:点击下载

发布了7 篇原创文章 · 获赞 1 · 访问量 3875

猜你喜欢

转载自blog.csdn.net/u010438205/article/details/104963997