设为首页收藏本站Access中国

Office中国论坛/Access中国论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

返回列表 发新帖
查看: 6555|回复: 8
打印 上一主题 下一主题

【探索】可以改写RIBBON控件的事件嘛?

[复制链接]
跳转到指定楼层
1#
发表于 2014-3-14 23:03:42 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 faunus 于 2014-3-15 00:02 编辑

昨天上课,有学员问到:
老师,有没有办法来改变RIBBON控件的事件
比如为RIBBON控件新增一些事件。
其实这个问题,天平大哥以两天前就问了。

当时我告诉他的是,如果是WINDOWS控件,
那么可以做到,继承该控件,
然后重写他的消息机制

  1.         protected override void WndProc(ref Message m)
  2.         {
  3.             base.WndProc(ref m);
  4.             switch (m.Msg)
  5.             {
  6.                 case WM_LBUTTONUP:
  7.                     this.WmMouseUp(ref m, System.Windows.Forms.MouseButtons.Left, 1);
  8.                     break;
  9.             }
  10.         }
复制代码
然后,在重写的代码中将捕获到的自定义消息定义与事件(委托)进行挂钩。基类Control提供了消息机制
  1.             protected override void WndProc(ref Message m)
  2.             {
  3.                 switch (m.Msg)
  4.                 {
  5.                     case 0x200:
  6.                         if (!this.control.GetState(0x4000))
  7.                         {
  8.                             this.control.HookMouseEvent();
  9.                             if (!this.control.GetState(0x2000))
  10.                             {
  11.                                 this.control.SendMessage(System.Windows.Forms.NativeMethods.WM_MOUSEENTER, 0, 0);
  12.                             }
  13.                             else
  14.                             {
  15.                                 this.control.SetState(0x2000, false);
  16.                             }
  17.                         }
  18.                         break;
  19.                     
  20.                     case 0x20a:
  21.                         this.control.ResetMouseEventArgs();
  22.                         break;
  23.                     
  24.                     case 0x2a3:
  25.                         this.control.UnhookMouseEvent();
  26.                         break;
  27.                 }
  28.                 this.target.OnMessage(ref m);
  29.             }
复制代码

控件的消息继承自哪里?
  1. this.window = new ControlNativeWindow(this);
复制代码


那么同样的做法在RIBBON控件上还适用嘛?
我对天平说,只能试试吧,也许真不行。
真的不行嘛?!





分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享分享 分享淘帖 订阅订阅
2#
 楼主| 发表于 2014-3-14 23:06:17 | 只看该作者
本帖最后由 faunus 于 2014-3-14 23:26 编辑

真的可以嘛?我也不确定,探索从现在开始~~
Action Now !

首先来看一下最简单的 RibbonButton:
  1.     public interface RibbonButton : RibbonControl, RibbonComponent, IComponent, IDisposable
  2.     {
  3.         RibbonControlSize ControlSize { get; set; }
  4.         string Description { get; set; }
  5.         RibbonPosition Position { get; set; }
  6.         System.Drawing.Image Image { get; set; }
  7.         string ImageName { get; set; }
  8.         string KeyTip { get; set; }
  9.         string Label { get; set; }
  10.         string OfficeImageId { get; set; }
  11.         string ScreenTip { get; set; }
  12.         bool ShowImage { get; set; }
  13.         bool ShowLabel { get; set; }
  14.         string SuperTip { get; set; }
  15.         event RibbonControlEventHandler Click;
  16.     }
复制代码
哦,确实,他定义了 Click 事件。首先对他的继承的接口 查看一下,是否有些价值的东东。
RibbonControl, RibbonComponent, IComponent, IDisposable
参照普通的WINDOWS控件,最有可能落实在 RibbonComponent 身上:
  1.     public interface RibbonComponent : IComponent, IDisposable
  2.     {
  3.         [Browsable(false)]
  4.         RibbonComponent Parent { get; }
  5.         [Browsable(false)]
  6.         OfficeRibbon Ribbon { get; }
  7.         [Browsable(false)]
  8.         IRibbonUI RibbonUI { get; }
  9.         object Tag { get; set; }
  10.         string Name { get; set; }
  11.         void ResumeLayout(bool performLayout);
  12.         void PerformDynamicLayout();
  13.         void PerformLayout();
  14.         void ResumeLayout();
  15.         void SuspendLayout();
  16.     }
复制代码
这里没有我们要的东西。
而 RibbonButton 本身不过一个接口,他的实例是什么呢?

3#
 楼主| 发表于 2014-3-14 23:32:16 | 只看该作者
通过这个调用
  1. System.Windows.Forms.MessageBox.Show(sender.GetType().ToString());
复制代码

我们得知了 RIBBONBUTTON 的真正类型

  1. Microsoft.Office.Tools.Ribbon.RibbonButtonImpl
复制代码
看看他的定义
  1. internal sealed class RibbonButtonImpl : RibbonControlImpl, RibbonButton, RibbonControl, RibbonComponent, IComponent, IDisposable
复制代码
从中找到对应的事件
  1.                 [RibbonEvent("onAction"), StringsDescription("RibbonDescription_Event_ButtonClick")]
  2.                 public event RibbonControlEventHandler Click
  3.                 {
  4.                         add
  5.                         {
  6.                                 this.properties.ControlAction += value;
  7.                         }
  8.                         remove
  9.                         {
  10.                                 this.properties.ControlAction -= value;
  11.                         }
  12.                 }
复制代码
这里有个重要的属性:RibbonEvent("onAction")
  1.         [AttributeUsage(AttributeTargets.Event, AllowMultiple = false)]
  2.         internal sealed class RibbonEventAttribute : Attribute
  3.         {
  4.                 private string callback;
  5.                 public string Callback
  6.                 {
  7.                         get
  8.                         {
  9.                                 return this.callback;
  10.                         }
  11.                 }
  12.                 public RibbonEventAttribute(string callback)
  13.                 {
  14.                         this.callback = callback;
  15.                 }
  16.         }
复制代码

目前为止,有了一点小进展:
找到了 Click 事件的声明。

然后,是不是问题快接近真相了?


4#
 楼主| 发表于 2014-3-14 23:37:19 | 只看该作者
关键可能还在 ControlAction 上,
  1.                 internal event RibbonControlEventHandler ControlAction
  2.                 {
  3.                         add
  4.                         {
  5.                                 this.controlAction = (RibbonControlEventHandler)Delegate.Combine(this.controlAction, value);
  6.                         }
  7.                         remove
  8.                         {
  9.                                 this.controlAction = (RibbonControlEventHandler)Delegate.Remove(this.controlAction, value);
  10.                         }
  11.                 }
复制代码
继续追踪 controlAction
  1. private RibbonControlEventHandler controlAction;
复制代码
下一步,当然,我们要看看谁在用他。


5#
 楼主| 发表于 2014-3-14 23:41:49 | 只看该作者
上节课程在委托中讲到了消息发布者,会通过消息来实际操作被订阅成功的方法。
看看谁在引用 controlAction,还好只有一处:
  1.                 private void ControlActionRaise(Microsoft.Office.Core.IRibbonControl control)
  2.                 {
  3.                         RibbonControlEventHandler ribbonControlEventHandler = this.controlAction;
  4.                         if (ribbonControlEventHandler != null)
  5.                         {
  6.                                 object container = this.component;
  7.                                 if (this.component.Container is IRibbonComponent)
  8.                                 {
  9.                                         container = this.component.Container;
  10.                                 }
  11.                                 ribbonControlEventHandler(container, new RibbonControlEventArgsImpl(control));
  12.                         }
  13.                 }
复制代码
到这么很显然,调用问题己经解决:
  1. ribbonControlEventHandler(container, new RibbonControlEventArgsImpl(control))
复制代码

那么消息的来源在哪里呢?
革命尚未成功,同志还需努力啊!



6#
 楼主| 发表于 2014-3-14 23:59:05 | 只看该作者
本帖最后由 faunus 于 2014-3-15 00:08 编辑

再进一步,找到这个位置:
  1.                 private static object ButtonClickCallback(RibbonComponentImpl component, object[] args)
  2.                 {
  3.                         if (component != null)
  4.                         {
  5.                                 Microsoft.Office.Core.IRibbonControl parameter = RibbonMethodInfo.GetParameter<Microsoft.Office.Core.IRibbonControl>(args, 0);
  6.                                 component.properties.ControlActionRaise(parameter);
  7.                         }
  8.                         return null;
  9.                 }
复制代码
ControlActionRaise 被这里调用了,又是谁在发起?
  1.                 internal static void RegisterCallbacks(RibbonManagerImpl manager)
  2.                 {
  3.                         manager.AddCallback(RibbonMethodInfo.GetValueMethod(manager, "ButtonClick", new RibbonComponentCallback(RibbonPropertyStorage.ButtonClickCallback)));
  4.                         manager.AddCallback(RibbonMethodInfo.CheckBoxActionMethod(manager, "CheckBoxClick", new RibbonComponentCallback(RibbonPropertyStorage.CheckBoxClickCallback)));
  5.                         manager.AddCallback(RibbonMethodInfo.GetValueMethod(manager, "GetChecked", new RibbonComponentCallback(RibbonPropertyStorage.CheckedCallback)));
  6.                         manager.AddCallback(RibbonMethodInfo.GetValueMethod(manager, "GetDescription", new RibbonComponentCallback(RibbonPropertyStorage.DescriptionCallback)));
  7.                         manager.AddCallback(RibbonMethodInfo.GetValueMethod(manager, "GetEnabled", new RibbonComponentCallback(RibbonPropertyStorage.EnabledCallback)));
  8.                         manager.AddCallback(RibbonMethodInfo.GetValueMethod(manager, "GetImage", new RibbonComponentCallback(RibbonPropertyStorage.ImageCallback)));
  9.                         manager.AddCallback(RibbonMethodInfo.GetValueMethod(manager, "GetKeyTip", new RibbonComponentCallback(RibbonPropertyStorage.KeyTipCallback)));
  10.                         manager.AddCallback(RibbonMethodInfo.GetValueMethod(manager, "GetLabel", new RibbonComponentCallback(RibbonPropertyStorage.LabelCallback)));
  11.                         manager.AddCallback(RibbonMethodInfo.GetValueMethod(manager, "GetScreenTip", new RibbonComponentCallback(RibbonPropertyStorage.ScreenTipCallback)));
  12.                         manager.AddCallback(RibbonMethodInfo.GetValueMethod(manager, "GetShowImage", new RibbonComponentCallback(RibbonPropertyStorage.ShowImageCallback)));
  13.                         manager.AddCallback(RibbonMethodInfo.GetValueMethod(manager, "GetShowLabel", new RibbonComponentCallback(RibbonPropertyStorage.ShowLabelCallback)));
  14.                         manager.AddCallback(RibbonMethodInfo.GetValueMethod(manager, "GetSize", new RibbonComponentCallback(RibbonPropertyStorage.SizeCallback)));
  15.                         manager.AddCallback(RibbonMethodInfo.GetValueMethod(manager, "GetSuperTip", new RibbonComponentCallback(RibbonPropertyStorage.SuperTipCallback)));
  16.                         manager.AddCallback(RibbonMethodInfo.GetValueMethod(manager, "GetText", new RibbonComponentCallback(RibbonPropertyStorage.TextCallback)));
  17.                         manager.AddCallback(RibbonMethodInfo.TextChangedMethod(manager, "TextChanged", new RibbonComponentCallback(RibbonPropertyStorage.TextChangedCallback)));
  18.                         manager.AddCallback(RibbonMethodInfo.GetValueMethod(manager, "GetTitle", new RibbonComponentCallback(RibbonPropertyStorage.TitleCallback)));
  19.                         manager.AddCallback(RibbonMethodInfo.GetValueMethod(manager, "GetVisible", new RibbonComponentCallback(RibbonPropertyStorage.VisibleCallback)));
  20.                 }
复制代码
在这里注册登记先,看来是消息中心所在地了...
  1.                 internal Dictionary<string, RibbonMethodInfo> Callbacks
  2.                 {
  3.                         get
  4.                         {
  5.                                 if (this.callbacks == null)
  6.                                 {
  7.                                         this.callbacks = new Dictionary<string, RibbonMethodInfo>();
  8.                                         OfficeRibbonImpl.RegisterCallbacks(this);
  9.                                         RibbonDialogLauncherImpl.RegisterCallbacks(this);
  10.                                         RibbonDropDownItemCollection.RegisterCallbacks(this);
  11.                                         RibbonGalleryImpl.RegisterCallbacks(this);
  12.                                         RibbonMenuImpl.RegisterCallbacks(this);
  13.                                         RibbonPropertyStorage.RegisterCallbacks(this);
  14.                                 }
  15.                                 return this.callbacks;
  16.                         }
  17.                 }
复制代码
他又是在这里登记的,Callbacks备用啊。看到 消息 机制定然在 以下这个类中安家了:
  1. internal sealed class RibbonManagerImpl : RibbonManager, Microsoft.Office.Core.IRibbonExtensibility, IReflect, IDisposable
复制代码
泥马,这个类好庞大的说,不太好跟了...





7#
 楼主| 发表于 2014-3-15 00:32:53 | 只看该作者
碎叫 先 ...
回复

使用道具 举报

8#
发表于 2014-3-20 10:34:25 | 只看该作者

老师,看了所有AppEvents_Event事件,参数都是excel封装好的对象。要扩展Application的mouse事件,比如Application.ActiveWindow.MouseHover,需要在Application里或是Application.Window里添加一个鼠标停滞事件观察者。这个理解对么?但是Application类怎么改写啊?操作系统发来的消息怎么才能截取到?
————————————————————————
汪老师的解答:
这个只能用API去接管ActiveWindow的WndProc
WndProc还记得吗
先用GetWindowLong 获取出原来的WndProc指针
存起来
然后用SetWindowLong 设置新的WndProc进去
然后新的WndProc优先处理系统消息
比如Hover
处理完以后
再次丢回给老的WndProc处理
全是API动作,所以很麻烦
而且牵扯到函数指针
C#内一定要用Unsafe括起来
(在excel对象模型里是做不到的,是吧?)
做不到的
好吧,我个人觉得,做不到
(校长在Excel对像模型内追踪事件源,汪老师的办法是从excel外部处理了,是这意思吧?)
是的
还有一个方法,创建玻璃窗口,盖在ActiveWindow上
透明窗口
然后你自然比ActiveWindow更快拦截到鼠标消息
不过这个绕的太远了
我是觉得如果一定要API来做的事情,超过5个步骤的话,就有点难维护了
指针如果指飞了 Excel就挂掉了
中间产生的所有的 堆块 和 指针 你都要存起来
一旦丢了,就完蛋了



9#
 楼主| 发表于 2014-3-20 10:54:59 | 只看该作者
tianping 发表于 2014-3-20 10:34
老师,看了所有AppEvents_Event事件,参数都是excel封装好的对象。要扩展Application的mouse事件,比如Ap ...

RIBBON中没采用winproc
您需要登录后才可以回帖 登录 | 注册

本版积分规则

QQ|站长邮箱|小黑屋|手机版|Office中国/Access中国 ( 粤ICP备10043721号-1 )  

GMT+8, 2024-5-3 07:26 , Processed in 0.186084 second(s), 32 queries .

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表