处理程序委托无法强制转换

时间:2018-10-29 10:12:31

标签: c# wpf casting

我正在尝试通过鼠标滚轮/触摸板启用侧面滚动,并启用此功能,我已经从此stack overflow answer修改了代码。

这很好。

但是,当我尝试将RoutedEventHandler换成特定于我在类中使用的事件的自定义处理程序时,我得到了InvalidCastException。当代码尝试引发事件element.RaiseEvent(ev);时,将引发此错误。

引发的异常如下。

Exception thrown: 'System.InvalidCastException' in PresentationCore.dll
An unhandled exception of type 'System.InvalidCastException' occurred in PresentationCore.dll
Unable to cast object of type 'SideScrollViewer.MouseHorizontalWheelEventHandler' to type 'System.Windows.Input.MouseEventHandler'.

如何解决?任何帮助或指针都表示赞赏


XAML实施

<Window x:Class="SideScrollViewer.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:SideScrollViewer"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <local:ScrollViewerSide HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
            <Button Content="Hello" Width="1000" Height="1000"/>
        </local:ScrollViewerSide>
    </Grid>
</Window>

启用侧面滚动的C#代码

using System;
using System.Collections.Generic;
using System.Security;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;

namespace SideScrollViewer
{
    public class ScrollViewerSide : ScrollViewer
    {
        #region Constructor
        static ScrollViewerSide()
        {
            RegisterNewEvents();
            DefaultStyleKeyProperty.OverrideMetadata(typeof(ScrollViewerSide), new FrameworkPropertyMetadata(typeof(ScrollViewerSide)));
        }

        public ScrollViewerSide()
        {
            Loaded += OnLoaded;
        }

        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            Window win = Window.GetWindow(this);
            if (win != null)
            {
                EnableMouseHorizontalWheelSupport(win);
            }
        }
        #endregion

        #region Default Values
        private const double HORIZONTAL_SCROLL_OFFSET = 48;
        #endregion

        #region RoutedEvents
        public static readonly RoutedEvent PreviewMouseHorizontalWheelEvent =
            EventManager.RegisterRoutedEvent("PreviewMouseHorizontalWheel", RoutingStrategy.Tunnel, typeof(MouseHorizontalWheelEventHandler), typeof(ScrollViewerSide));

        public static readonly RoutedEvent MouseHorizontalWheelEvent =
            EventManager.RegisterRoutedEvent("MouseHorizontalWheel", RoutingStrategy.Bubble, typeof(MouseHorizontalWheelEventHandler), typeof(ScrollViewerSide));
        #endregion

        #region Events
        public event MouseHorizontalWheelEventHandler PreviewMouseHorizontalWheel {
            add => AddHandler(PreviewMouseHorizontalWheelEvent, value);
            remove => RemoveHandler(PreviewMouseHorizontalWheelEvent, value);
        }

        public event MouseHorizontalWheelEventHandler MouseHorizontalWheel {
            add => AddHandler(MouseHorizontalWheelEvent, value);
            remove => RemoveHandler(MouseHorizontalWheelEvent, value);
        }
        #endregion

        #region Class handling methods for scroll events
        [SecurityCritical]
        [SecuritySafeCritical]
        private static void RegisterNewEvents()
        {
            EventManager.RegisterClassHandler(typeof(ScrollViewerSide), PreviewMouseHorizontalWheelEvent, new MouseHorizontalWheelEventHandler(OnPreviewMouseHorizontalWheelThunk), false);
            EventManager.RegisterClassHandler(typeof(ScrollViewerSide), MouseHorizontalWheelEvent, new MouseHorizontalWheelEventHandler(OnMouseHorizontalWheelThunk), false);
        }

        [SecurityCritical]
        private static void OnPreviewMouseHorizontalWheelThunk(object sender, MouseHorizontalWheelEventArgs e)
        {
            if (!e.Handled && sender is ScrollViewerSide view)
            {
                view.OnPreviewMouseHorizontalWheel(e);
            }
        }

        protected virtual void OnPreviewMouseHorizontalWheel(MouseHorizontalWheelEventArgs e) { }

        [SecurityCritical]
        private static void OnMouseHorizontalWheelThunk(object sender, MouseHorizontalWheelEventArgs e)
        {
            if (!e.Handled && sender is ScrollViewerSide view)
            {
                view.OnMouseHorizontalWheel(e);
            }
        }

        protected virtual void OnMouseHorizontalWheel(MouseHorizontalWheelEventArgs e)
        {
            if (e.HorizontalDelta < 0)
            {
                ScrollToHorizontalOffset(HorizontalOffset + HORIZONTAL_SCROLL_OFFSET);
            }
            else
            {
                ScrollToHorizontalOffset(HorizontalOffset - HORIZONTAL_SCROLL_OFFSET);
            }
        }
        #endregion

        #region Stuff needed for horizontal scrolling
        private static readonly HashSet<IntPtr> HOOKED_WINDOWS = new HashSet<IntPtr>();

        private static void EnableMouseHorizontalWheelSupport(Window window)
        {
            if (window == null)
            {
                throw new ArgumentNullException(nameof(window));
            }

            if (window.IsLoaded)
            {
                IntPtr handle = new WindowInteropHelper(window).Handle;
                EnableMouseHorizontalWheelSupport(handle);
            }
            else
            {
                window.Loaded += (sender, args) => {
                    IntPtr handle = new WindowInteropHelper(window).Handle;
                    EnableMouseHorizontalWheelSupport(handle);
                };
            }
        }

        private static void EnableMouseHorizontalWheelSupport(IntPtr handle)
        {
            if (!HOOKED_WINDOWS.Contains(handle))
            {
                HOOKED_WINDOWS.Add(handle);
                HwndSource source = HwndSource.FromHwnd(handle);
                source?.AddHook(WndProcHook);
            }
        }

        private static void HandleMouseHorizontalWheel(IntPtr wParam)
        {
            int tilt = -Win32.HiWord(wParam);
            if (tilt == 0)
            {
                return;
            }

            IInputElement element = Mouse.DirectlyOver;
            if (element != null)
            {
                MouseHorizontalWheelEventArgs ev = new MouseHorizontalWheelEventArgs(Mouse.PrimaryDevice, Environment.TickCount, tilt)
                {
                    RoutedEvent = PreviewMouseHorizontalWheelEvent
                };

                element.RaiseEvent(ev);
                if (ev.Handled)
                {
                    return;
                }

                ev.RoutedEvent = MouseHorizontalWheelEvent;
                element.RaiseEvent(ev);
            }
        }

        private static IntPtr WndProcHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            if (msg == Win32.WM_MOUSEHWHEEL)
            {
                HandleMouseHorizontalWheel(wParam);
            }

            return IntPtr.Zero;
        }

        private static class Win32
        {
            public const int WM_MOUSEHWHEEL = 0x020E;

            private static int GetIntUnchecked(IntPtr value)
            {
                return IntPtr.Size == 8 ? unchecked((int)value.ToInt64()) : value.ToInt32();
            }

            public static int HiWord(IntPtr ptr)
            {
                return unchecked((short)((uint)GetIntUnchecked(ptr) >> 16));
            }
        }
        #endregion
    }

    #region Handler / Event Args
    public delegate void MouseHorizontalWheelEventHandler(object sender, MouseHorizontalWheelEventArgs e);

    public class MouseHorizontalWheelEventArgs : MouseEventArgs
    {
        public int HorizontalDelta { get; }

        public MouseHorizontalWheelEventArgs(MouseDevice mouse, int timestamp, int horizontalDelta)
            : base(mouse, timestamp)
        {
            HorizontalDelta = horizontalDelta;
        }
    }
    #endregion
}

1 个答案:

答案 0 :(得分:2)

在自定义InvokeEventHandler类中覆盖MouseEventArgs方法:

public class MouseHorizontalWheelEventArgs : MouseEventArgs
{
    public int HorizontalDelta { get; }

    public MouseHorizontalWheelEventArgs(MouseDevice mouse, int timestamp, int horizontalDelta)
        : base(mouse, timestamp)
    {
        HorizontalDelta = horizontalDelta;
    }

    protected override void InvokeEventHandler(Delegate genericHandler, object genericTarget)
    {
        MouseHorizontalWheelEventHandler handler = (MouseHorizontalWheelEventHandler)genericHandler;
        handler(genericTarget, this);
    }
}