如何将自定义动画添加到 ContextMenuStrip?

How to add custom animations to a ContextMenuStrip?

有没有办法在打开 ContextMenuStrip 时创建动画,就像这张图片中那样?

这个动画是为 WhatsApp 设计的 click.I 不知道怎么做。

您可以将 AnimateWindow 功能与 ToolStrip 一起使用,其中包括 ContextMenuStrip class,因为它们都有句柄。
例如,您可以重现 OP 中显示的处理控件的 MouseDown 事件的动画:

Private Sub SomeControl_MouseDown(sender As Object, e As MouseEventArgs) Handles SomeControl.MouseDown
    If e.Button = MouseButtons.Right Then
        Dim ctrl = DirectCast(sender, Control)
        Dim cms = ContextMenuStrip1
        cms.Location = MousePosition
        AnimateWindow(cms.Handle, 250, AW_VER_POSITIVE Or AW_HOR_POSITIVE)
        cms.Show(ctrl, ctrl.PointToClient(MousePosition))
    End If
End Sub

这意味着您必须处理每个需要动画 ContextMenuStrip 的控件的 MouseDown 事件。如果只是一个,那可能是可以接受的。或者一堆可以共享相同 MouseDown 事件的控件。
但是您不能将动画应用于窗体中的所有控件,例如将 ContextMenuStrip 设置为窗体 class。

最好构建一个自定义控件,继承 ContextMenuStrip。
然后您可以添加一些允许配置 ContextMenuStrip 行为的属性,添加一些支持的动画。

我正在添加一些:

  • OpenDownwards:如 OP 的示例图片
  • OpenUpwards:相反
  • Expand: 从中心展开菜单

幻灯片(不言自明):

  • SlideToRightSlideToLeft
  • SlideDownwards, SlideUpwards

新 Public 属性:

  • AnimationTime:动画的速度,以毫秒为单位
  • AnimationType:动画类型。组合框选择器
  • AnimateInDesigner:在窗体设计器中为 ContextMenuStrip 设置动画。使配置更容易,因为动画是在设计时显示的。
    默认关闭,将其设置为 True 以将其打开。您可能希望在配置控件后将其关闭。

Imports System.ComponentModel
Imports System.Runtime.InteropServices
Imports System.Windows.Forms

<DesignerCategory("code")>
Public Class ContextMenuStripAnimated
    Inherits ContextMenuStrip

    ' Don't remove
    Public Sub New()
    End Sub

    Public Sub New(container As IContainer)
        Me.New()
        If container Is Nothing Then
            Throw New ArgumentNullException("container is null")
        End If
        container.Add(Me)
    End Sub

    Protected Overrides Sub OnOpening(e As CancelEventArgs)
        If Not DesignMode OrElse AnimateInDesigner Then
            Dim result = AnimateWindow(Handle, AnimationTime, AnimationType)
        End If
        MyBase.OnOpening(e)
    End Sub

    <DefaultValue(False)>
    Public Property AnimateInDesigner As Boolean = False

    <DefaultValue(AnimationMode.OpenDownwards)>
    Public Property AnimationType As AnimationMode = AnimationMode.OpenDownwards

    <DefaultValue(250)>
    Public Property AnimationTime As UInteger = 250

    <Flags()>
    Public Enum AnimationMode As UInteger
        OpenDownwards = AW_HOR_POSITIVE Or AW_VER_POSITIVE
        OpenUpwards = AW_HOR_NEGATIVE Or AW_VER_NEGATIVE
        Expand = AW_CENTER
        SlideToRight = AW_SLIDE Or AW_HOR_POSITIVE
        SlideToLeft = AW_SLIDE Or AW_HOR_NEGATIVE
        SlideDownwards = AW_SLIDE Or AW_VER_POSITIVE
        SlideUpwards = AW_SLIDE Or AW_VER_NEGATIVE
    End Enum

#Region "NativeMethods"

    Private Const AW_HOR_POSITIVE As UInteger = &H1
    Private Const AW_HOR_NEGATIVE As UInteger = &H2
    Private Const AW_VER_POSITIVE As UInteger = &H4
    Private Const AW_VER_NEGATIVE As UInteger = &H8
    Private Const AW_CENTER As UInteger = &H10
    Private Const AW_HIDE As UInteger = &H10000
    Private Const AW_ACTIVATE As UInteger = &H20000
    Private Const AW_SLIDE As UInteger = &H40000
    Private Const AW_BLEND As UInteger = &H80000

    <DllImport("user32.dll", SetLastError:=true)>
    Private Shared Function AnimateWindow(hwnd As IntPtr, time As UInteger, flags As AnimationMode) As Boolean
    End Function

#End Region
End Class

要将此新控件添加到工具箱:

  • 在项目中添加一个Class,命名为ContextMenuStripAnimated
  • 复制此处显示的所有代码,包括 Imports 指令
  • 将其粘贴到新的 Class 文件中,替换其中的所有内容
  • 构建项目
  • 在工具箱中找到新控件并将其添加到窗体

C#版本,以防万一:

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Windows.Forms;

[DesignerCategory("code")]
public class ContextMenuStripAnimated : ContextMenuStrip
{
    public ContextMenuStripAnimated() { }

    public ContextMenuStripAnimated(IContainer container) : this()
    {
        if (container == null) {
            throw new ArgumentNullException("container is null");
        }
        container.Add(this);
    }

    protected override void OnOpening(CancelEventArgs e)
    {
        if (!DesignMode || AnimateInDesigner) {
            var result = AnimateWindow(Handle, AnimationTime, AnimationType);
        }
        base.OnOpening(e);
    }

    [DefaultValue(false)]
    public bool AnimateInDesigner { get; set; } = false;

    [DefaultValue(250)]
    public uint AnimationTime { get; set; } = 250;

    [DefaultValue(AnimationMode.OpenDownwards)]
    public AnimationMode AnimationType { get; set; } = AnimationMode.OpenDownwards;

    [Flags]
    public enum AnimationMode : uint
    {
        OpenDownwards = AW_HOR_POSITIVE | AW_VER_POSITIVE,
        OpenUpwards = AW_HOR_NEGATIVE | AW_VER_NEGATIVE,
        Expand = AW_CENTER,
        SlideToRight = AW_SLIDE | AW_HOR_POSITIVE,
        SlideToLeft = AW_SLIDE | AW_HOR_NEGATIVE,
        SlideDownwards = AW_SLIDE | AW_VER_POSITIVE,
        SlideUpwards = AW_SLIDE | AW_VER_NEGATIVE
    }

    private const uint AW_HOR_POSITIVE = 0x1;
    private const uint AW_HOR_NEGATIVE = 0x2;
    private const uint AW_VER_POSITIVE = 0x4;
    private const uint AW_VER_NEGATIVE = 0x8;
    private const uint AW_CENTER = 0x10;
    private const uint AW_HIDE = 0x10000;
    private const uint AW_ACTIVATE = 0x20000;
    private const uint AW_SLIDE = 0x40000;
    private const uint AW_BLEND = 0x80000;

    [DllImport("user32.dll", SetLastError = true)]
    private static extern bool AnimateWindow(IntPtr hwnd, uint time, AnimationMode flags);
}