Adding controls to the ribbon

Discussions related to custom development with Select.
Post Reply
ckootg
Posts: 122
Joined: Fri Jan 06, 2012 6:10 pm

Adding controls to the ribbon

Post by ckootg »

Can I get an example on adding controls to the ribbon using the manager? And handling the click event. The controls I'm trying to add are not tied to a specific window. They should be available on startup.

Looks like I can still create a CTD to add to the ribbon, but the RegisterHandler method to handle the click event is no longer part of Shell.Package
John Morris
Posts: 411
Joined: Thu Sep 11, 2008 11:35 am
Location: Raleigh, NC, USA
Contact:

Re: Adding controls to the ribbon

Post by John Morris »

You can register a command handler using the new "RegisterHandler" extension method, found in the SoftPro.Select.Controls.Commands namespace. Previous builds would use code like this:

Code: Select all

base.RegisterHandler(...)
Using Cameron, you'll need to replace the "base" keyword with "this". Also make sure to include the namespace mentioned in order to gain access to the new extension methods.
John Morris
Sr. Software Architect
SoftPro
ckootg
Posts: 122
Joined: Fri Jan 06, 2012 6:10 pm

Re: Adding controls to the ribbon

Post by ckootg »

Thanks. I was missing the SoftPro.Select.Controls.Commands namespace.
ckootg
Posts: 122
Joined: Fri Jan 06, 2012 6:10 pm

Re: Adding controls to the ribbon

Post by ckootg »

Now, I'm trying to hide certain controls based on permission set.

I registered a QueryStatus handler and tried to hide the control in the handler, but it errors out. I know the control exists because I see if I remove the QueryStatus handler.

Code: Select all

this.RegisterHandler(AdminPackage.NewRibbonTab, null, this.QueryStatusHandler);
...
private void QueryStatusHandler(object sender, QueryStatusEventArgs e)
{
    e.Visible = false;
}
This is the error log

Code: Select all

System.NullReferenceException: Object reference not set to an instance of an object.
Stack Trace: 
   at SoftPro.Select.Controls.RibbonTab.Command_CommandChanged(Object sender, EventArgs e)
   at SoftPro.Select.Shell.Commands.Command.OnCommandChanged()
   at SoftPro.Select.Managers.CommandManager.UpdateCommandState(QueryStatusEventArgs eventArgs)
   at SoftPro.Select.Managers.CommandManager.QueryStatus(QueryStatusEventArgs eventArgs)
   at SoftPro.Select.Controls.RibbonTab.QueryStatus(HashSet`1 queried)
   at SoftPro.Select.Controls.Ribbon.SoftPro.Select.Shell.ICommandWidget.QueryStatus(HashSet`1 queried)
   at SoftPro.Select.Managers.CommandManager.UpdateCommandUI(Boolean force, Boolean delayed)
   at SoftPro.Select.Managers.CommandManager.Application_Idle(Object sender, EventArgs e)
   at System.EventHandler.Invoke(Object sender, EventArgs e)
   at System.Windows.Forms.Application.ThreadContext.System.Windows.Forms.UnsafeNativeMethods.IMsoComponent.FDoIdle(Int32 grfidlef)
   at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
   at SoftPro.Select.Runtime.OnRun()
   at SoftPro.Select.Runtime.Start()
   at SoftPro.Select.Automation.EnvICE.Start()
XML Log: 
<ILExceptionData>
  <Point module="Select.exe" classFull="SoftPro.Select.Controls.RibbonTab" methodName="Command_CommandChanged" methodSignature="Void Command_CommandChanged(System.Object, System.EventArgs)" methodToken="0x600072c" ILOffset="28" />
  <Point module="SoftPro.Select.Shell.dll" classFull="SoftPro.Select.Shell.Commands.Command" methodName="OnCommandChanged" methodSignature="Void OnCommandChanged()" methodToken="0x600047b" ILOffset="8" />
  <Point module="Select.exe" classFull="SoftPro.Select.Managers.CommandManager" methodName="UpdateCommandState" methodSignature="Void UpdateCommandState(SoftPro.Select.Controls.QueryStatusEventArgs)" methodToken="0x600068d" ILOffset="0" />
  <Point module="Select.exe" classFull="SoftPro.Select.Managers.CommandManager" methodName="QueryStatus" methodSignature="Boolean QueryStatus(SoftPro.Select.Controls.QueryStatusEventArgs)" methodToken="0x600068b" ILOffset="587" />
  <Point module="Select.exe" classFull="SoftPro.Select.Controls.RibbonTab" methodName="QueryStatus" methodSignature="Void QueryStatus(System.Collections.Generic.HashSet`1[System.ComponentModel.Design.CommandID])" methodToken="0x6000734" ILOffset="87" />
  <Point module="Select.exe" classFull="SoftPro.Select.Controls.Ribbon" methodName="SoftPro.Select.Shell.ICommandWidget.QueryStatus" methodSignature="Void SoftPro.Select.Shell.ICommandWidget.QueryStatus(System.Collections.Generic.HashSet`1[System.ComponentModel.Design.CommandID])" methodToken="0x60006e0" ILOffset="34" />
  <Point module="Select.exe" classFull="SoftPro.Select.Managers.CommandManager" methodName="UpdateCommandUI" methodSignature="Void UpdateCommandUI(Boolean, Boolean)" methodToken="0x6000692" ILOffset="125" />
  <Point module="Select.exe" classFull="SoftPro.Select.Managers.CommandManager" methodName="Application_Idle" methodSignature="Void Application_Idle(System.Object, System.EventArgs)" methodToken="0x600066d" ILOffset="8" />
  <Point module="CommonLanguageRuntimeLibrary" classFull="System.EventHandler" methodName="Invoke" methodSignature="Void Invoke(System.Object, System.EventArgs)" methodToken="0x6000c3d" ILOffset="-1" />
  <Point module="System.Windows.Forms.dll" classFull="System.Windows.Forms.Application+ThreadContext" methodName="System.Windows.Forms.UnsafeNativeMethods.IMsoComponent.FDoIdle" methodSignature="Boolean System.Windows.Forms.UnsafeNativeMethods.IMsoComponent.FDoIdle(Int32)" methodToken="0x6001344" ILOffset="8" />
  <Point module="System.Windows.Forms.dll" classFull="System.Windows.Forms.Application+ComponentManager" methodName="System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop" methodSignature="Boolean System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr, Int32, Int32)" methodToken="0x6001308" ILOffset="476" />
  <Point module="System.Windows.Forms.dll" classFull="System.Windows.Forms.Application+ThreadContext" methodName="RunMessageLoopInner" methodSignature="Void RunMessageLoopInner(Int32, System.Windows.Forms.ApplicationContext)" methodToken="0x6001337" ILOffset="478" />
  <Point module="System.Windows.Forms.dll" classFull="System.Windows.Forms.Application+ThreadContext" methodName="RunMessageLoop" methodSignature="Void RunMessageLoop(Int32, System.Windows.Forms.ApplicationContext)" methodToken="0x6001336" ILOffset="19" />
  <Point module="Select.exe" classFull="SoftPro.Select.Runtime" methodName="OnRun" methodSignature="Void OnRun()" methodToken="0x60008d6" ILOffset="59" />
  <Point module="Select.exe" classFull="SoftPro.Select.Runtime" methodName="Start" methodSignature="Void Start()" methodToken="0x60008d2" ILOffset="75" />
  <Point module="Select.exe" classFull="SoftPro.Select.Automation.EnvICE" methodName="Start" methodSignature="Void Start()" methodToken="0x600048e" ILOffset="197" />
</ILExceptionData>


Call Stack: 
   at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo)
   at System.Environment.get_StackTrace()
   at System.Diagnostics.TraceEventCache.get_Callstack()
   at SoftPro.Select.LogTraceListener.OnLog(TraceEventCache eventCache, String message, TraceEventType eventType)
   at SoftPro.Select.LogTraceListener.TraceEvent(TraceEventCache eventCache, String source, TraceEventType eventType, Int32 id, String message)
   at System.Diagnostics.TraceInternal.TraceEvent(TraceEventType eventType, Int32 id, String format, Object[] args)
   at System.Diagnostics.Trace.TraceError(String message)
   at SoftPro.Select.Automation.EnvICE.Start()
   at System.Runtime.Remoting.Messaging.Message.Dispatch(Object target, Boolean fExecuteInContext)
   at System.Runtime.Remoting.Messaging.StackBuilderSink.SyncProcessMessage(IMessage msg, Int32 methodPtr, Boolean fExecuteInContext)
   at System.Runtime.Remoting.Messaging.StackBuilderSink.SyncProcessMessage(IMessage msg)
   at System.Runtime.Remoting.Messaging.ServerObjectTerminatorSink.SyncProcessMessage(IMessage reqMsg)
   at SoftPro.Select.Shell.SynchronizationContextAspect.SyncProcessMessage(IMessage msg)
   at System.Runtime.Remoting.Lifetime.LeaseSink.SyncProcessMessage(IMessage msg)
   at System.Runtime.Remoting.Messaging.ServerContextTerminatorSink.SyncProcessMessage(IMessage reqMsg)
   at System.Runtime.Remoting.Channels.CrossContextChannel.SyncProcessMessageCallback(Object[] args)
   at System.Threading.Thread.CompleteCrossContextCallback(InternalCrossContextDelegate ftnToCall, Object[] args)
   at System.Threading.Thread.InternalCrossContextCallback(Context ctx, IntPtr ctxID, Int32 appDomainID, InternalCrossContextDelegate ftnToCall, Object[] args)
   at System.Runtime.Remoting.Channels.CrossContextChannel.SyncProcessMessage(IMessage reqMsg)
   at System.Runtime.Remoting.Proxies.RemotingProxy.CallProcessMessage(IMessageSink ms, IMessage reqMsg, ArrayWithSize proxySinks, Thread currentThread, Context currentContext, Boolean bSkippingContextChain)
   at System.Runtime.Remoting.Proxies.RemotingProxy.InternalInvoke(IMethodCallMessage reqMcmMsg, Boolean useDispatchMessage, Int32 callType)
   at System.Runtime.Remoting.Proxies.RemotingProxy.Invoke(IMessage reqMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
   at SoftPro.Select.Automation.EnvICE.Start()
   at SoftPro.Select.Automation.EnvICE.Run(String rootSuffix, Boolean noSplash, Boolean setupMode, Boolean log, Boolean embedding, Dictionary`2 args, String freeArg)
   at SoftPro.Select.Program.Main(String[] args)
ckootg
Posts: 122
Joined: Fri Jan 06, 2012 6:10 pm

Re: Adding controls to the ribbon

Post by ckootg »

Any ideas?
John Morris
Posts: 411
Joined: Thu Sep 11, 2008 11:35 am
Location: Raleigh, NC, USA
Contact:

Re: Adding controls to the ribbon

Post by John Morris »

Ribbon tabs do not support the QueryStatus routine in the normal fashion. It is against ribbon usage guidelines to hide a tab once the application is up and running, unless that tab is a contextual tab. (See Microsoft's Ribbon Guidelines for more details.)

Once you hide a ribbon tab using a QueryStatus call like this, you have to Unregister the handler, or else it will create issues.

Try this:

Code: Select all

private void QueryStatusHandler(object sender, QueryStatusEventArgs e)
{
    e.Visible = false;
    this.UnregisterHandler(RibbonTabID, null, this.QueryStatusHandler);
}
John Morris
Sr. Software Architect
SoftPro
ckootg
Posts: 122
Joined: Fri Jan 06, 2012 6:10 pm

Re: Adding controls to the ribbon

Post by ckootg »

I understand that it does not meet the ribbon usage guidelines, but is there another way to add a non-contextual ribbon tab without defining them in CTD file? I had asked this in my original post. Or perhaps there is a way to define them in the CTD, but not create them on start up and allow it to be added later???

In the QueryStatus handler, I am unregistering the handler but did not add that in my example. The exception, however, is thrown on "e.Visible = false;" on it's first occurrence. In Boylan, the sender was actually the Command object, so we did the following.

Code: Select all

private void QueryStatusHandler(object sender, QueryStatusEventArgs e)
{
    Command cmd = (Command)sender;
    cmd.Visible = false;
    this.UnregisterHandler(RibbonTabID, null, this.QueryStatusHandler);
}
John Morris
Posts: 411
Joined: Thu Sep 11, 2008 11:35 am
Location: Raleigh, NC, USA
Contact:

Re: Adding controls to the ribbon

Post by John Morris »

RibbonTabs must be defined in a CTD.

Ah, I see the issue with your code snippet now. Instead of using the sender argument, use the EventArgs argument. This was one of the breaking changes in the shell. Sorry for the confusion.
John Morris
Sr. Software Architect
SoftPro
ckootg
Posts: 122
Joined: Fri Jan 06, 2012 6:10 pm

Re: Adding controls to the ribbon

Post by ckootg »

John, in Cameron, I am using the visible property on the QueryStatusEventArgs argument, which is throwing an error. The previous post (using the sender argument) was the code we currently use in Boylan.
John Morris
Posts: 411
Joined: Thu Sep 11, 2008 11:35 am
Location: Raleigh, NC, USA
Contact:

Re: Adding controls to the ribbon

Post by John Morris »

OK.

I'm not seeing that behavior on my latest builds. Let me look into getting an updated build to you and see if the issue is already resolved.
John Morris
Sr. Software Architect
SoftPro
Post Reply