Show or Hide a VSTO Addin Ribbon - ms-office

My goal is to simply offer my addin if Office application is launched with a certain argument.
Unfortunately, I could not find anything to help me do this. I have tried to use the Office Application Load Addin swtich /lc:Addin.dll with no success. One option I entertained was to create all of the Office addin registry entries upon time of wish to launch addin however this seemed extremely clumsy and way to much overhead. Also, the deal breaker for me was requiring registry creation elevated privileges in order to initialize the addin.
I decided to have my addin not do much of anything at startup unless a certain environment variable exists.
In order to do it this way I need to either set the ribbon to non-visible by default and show the Ribbon upon discovering the env variable. Or the opposite have the ribbon visible by default and hide the ribbon upon discovering env variable.
Things I have tried
Setting ribbon's tab Globals.Ribbons.MyRibbon.MyTab.visible = false.
Invalidating the Ribbon Globals.Ribbons.MyRibbon.RibbbonUi.Invalidate().
Invalidating the tab after setting visible to false Globals.Ribbons.MyRibbon.RibbbonUi.InvalidateControl(tabCtrlId).
The things tried dont include the dozens of thing to try to only load addin in certain circumstances.

I figured out a solution.
after digging into the base class AddinBase I discovered some methods available for me to override.
So I overrode the CreateRibbonExtensibilityObject method.
protected override IRibbonExtensibility CreateRibbonExtensibilityObject( )
{
if( Environment.GetCommandLineArgs( ).ToList( ).FirstOrDefault( a => a.ToLower( ).Contains( "/launchmyaddin" ) ) != null )
{
return null;
}
return base.CreateRibbonExtensibilityObject( );
}
What this does is prevent the ribbon from even being created if my switch is present and if it is present then I just pass off to the base class implementation in order to have the Addin create my ribbon like normal.
Also, CreateRibbonExtensibilityObject() returns an object that has a GetCustomUI( ribbonXml ) so we can create our custom ribbon from xml.
This gives us more power.
My solution only needed to show/hide a ribbon once only at startup. I did think about how this might be toggled on and off so I went poking around for other members I could override.
I believe you can override the CreateRibbonObjects( ) member which I think will get called upon every time the invalidate of a ribbon is called. Here you may be able to remove the item from the collection the base class returns that represents your ribbon you wish to hide.

If you use custom tab(s) (this is, ControlIdType=Custom) you may set visibility via:
foreach (var tab in Globals.Ribbons.Ribbon1.Tabs)
{
tab.Visible = false;
}

Related

How do I add a Lead Source option

I'm trying to add options to the Source dropdown on the Leads screen. The field uses the CRMSourcesAttribute class to define the existing list. After simply creating a new attribute class to add my own items, I extend the CRLead.Source CacheAttached event to use my new attribute class instead. The result is that there is no change - the new dropdown items are not shown. After doing this, if I inspect the field and select the Drop Down Values button, I do actually see the new options in the Drop Down Values popup window. Any ideas on what could be preventing the new options from displaying in the dropdown itself?
Here's how I configure it in my LeadMaint graph extension:
[PXMergeAttributes(Method = MergeMethod.Append)]
[PXRemoveBaseAttribute(typeof(CRMSourcesAttribute))]
[CRMSourcesExt] // list with old + new options
protected virtual void _(Events.CacheAttached<CRLead.source> e) { }
(v20R2)
Another option is to use code and create a new attribute class with new options, then override the DAC field to use it instead. However, as far as I know, you still have to go through the steps of activating the new options in the workflow customizations UI for each of the fields you're overriding. But then you can refer to the new options in code without having to search the list's AllowedValues at runtime.
With help from a colleague, I was able to get it working, and “without code” through screen workflow customization. I removed my code, then customized it with a new custom Workflow copied from the default. Not sure yet of the ramifications of this, like if we can access the new options in other real code or not, but I see how to create these options now. It has to also be done for both Lead and Opportunity in this scenario. It’s a bit tedious and ethereal (good word) though. I can see this easily causing problems and unexpected results for us we’ll want to watch out for.
Update 5/28/21: I added the options in the Workflow customizations for the field, then copied the default workflow to a new workflow, activated it, and activated the new options for each state/transition. I don't like it, but Acumatica tells me "that's just the way it is now". Note: you'll want to do this for other places referencing CRMSourcesAttribute as well, such as Opportunity.

MdiParent property of child forms

I'm trying to do some practices on a MDI application in Microsoft Visual Studio 2012 but it seems that I'm doing something wrong.
I made a new Windows Forms Application project and changed the IsMdiContainer property of the first form to True. Then I add a new form by right clicking the project in solution explorer and selecting Add - New Item - Windows Form.
I'm trying to set MdiParent property of form2 to the MDI Container form, but it seems that form2 has no MdiParent property.
Here's the properties of form2 sorted alphabetically.
Of course I can open form2 as a child form this way:
form2 newMDIChild = new form2();
newMDIChild.MdiParent = this;
newMDIChild.Show();
I just wondered why the forms has no MDIParent property.
According to this page the child forms have a MdiParent property.
Any kind of help is much appreciated.
You're doing it right, instantiating the new form and assigning MdiParent there. Really there IS such a property, and in your code you're using it:
newMDIChild.MdiParent = this;
This is perfectly fine and the normal way of doing it.
Now, what you're pointing is that the designer doesn't shows it, which is also true. That don't means that the property don't exist (it does, and you're using it in fact!). Why it isn't in the designer property sheet is another history, totally irrelevant, but I suppose that it has to do with the fact that it's difficult, if not impossible to properly set MdiParent at design time. If you think about it, it must contain an instance of another form (with it's IsMdiContainer set to true). At design time, you don't have any instances of anything, as they're created as your program runs. There is no way that you can set it beforehand, as you must normally pass another form to it, which you can't know while designing.

Determining which Visual Studio context menu was selected?

I'm writing a VS2012 add-in, adding a command to Build Explorer context menu (see related question). The command gets added to 2 different context menus:
Build Explorer
Team Explorer, Builds page, My Builds section
When my one callback is called, how do I know which of these it is?
I tried get the focused control (using P/Invoke as this question suggests). However, it gets me a Tabs container for (1), and null for (2). I could try to cast the control to the tabbed container, but that sounds pretty bad...
Any better alternative?
My new/other idea - it is similar to yours:
You should try to monitor which window was activated lastly.
If you create an eventhandler for your command, then you may be able to check which window is active when your command fired. A simple evenent handler for a command:
void cmdEvents_BeforeExecute( string guid, int ID, object customIn, object customOut, ref bool cancelDefault )
{
Window2 teamExplorer = _applicationObject.Windows.Item("Team Explorer") as Window2;
if (_applicationObject.ActiveWindow.Caption == teamExplorer.Caption)
{
//You are called from Team Explorer
}
else
{
//Somewhere else
}
}
And the way you can subscribe:
static _dispCommandEvents_BeforeExecuteEventHandler _myHandler;
static CommandEvents _cmdEvents;
public void OnConnection(...)
{
Command command = ...; // Init your command
int ID = command.ID;
string GUID = command.Guid;
CommandEvents _cmdEvents = _applicationObject.Events.get_CommandEvents(GUID, ID);
_myHandler = new _dispCommandEvents_BeforeExecuteEventHandler(cmdEvents_BeforeExecute);
_cmdEvents.BeforeExecute += _myHandler;
}
You may find a better way to identify the window(s) by GUID. You should keep at least _cmdEvents as static because when it will be desroyed, your event handler could vanish (least for internal commands).
In OnDisconnection you should unsubscribe.
Reworked by the comment, and founded links:
As the menu item is shown every place it seems there is no way to distinct between them from an Add-In, you should add two command and distinct them by their context.
The way instead of converting the Add-In to a VS-Package MZ-Tools HOWTO: Controlling the state of command in a Visual Studio add-in, try MZ-Tools HOWTO: Use the IVsMonitorSelection ... you can also get it from an Add-In.
But:
Neither the AddNamedCommand nor the QueryStatus methods honor the
invisible state: the button that must be invisible ...
remains disabled rather than invisible.
I think this makes it impossible to do it from an Add-In on a suitable way, but maybe you can check the contexts.
Other way you could get further, if you try to migrate your command/menu into a VSPackage and create a custom UIContext for the menu items or find a suitable predefined one. I have no access to a Studio enhanced with Build Explorer so I can't try it.
The following discussion is about custom contexts for vs-packages:
http://davedewinter.com/2008/04/05/dynamic-menu-commands-in-visual-studio-packages-part-3/
Sadly the links are broken from the post, and I can't reach Part 1. and Part 2. which is about the discussion of the problem from the beginning.
But there is no guarantee you can create a context which suits you.
Only context ID I found for Team Explorer is the guidTeamProjectCmdUIContext.
It is placed at vsshilds.h in Visual Studio 2010 SDK, vsshell*.h are also contain several others.
MSDN: Vsct files to define command, menus, ect. from packages.
Condition attribute for items:
http://msdn.microsoft.com/en-us/library/bb491718.aspx
http://msdn.microsoft.com/en-us/library/bb166515.aspx
MSDN: VisibilityItem element for commands and toolbars.
VisibilityItem element determines the static visibility of commands and toolbars.
... After the VSPackage is loaded, Visual Studio expects command visibility to be determined by the VSPackage rather than the VisibilityItem.
And finally about predefined Context Guids:
http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.shell.interop.uicontextguids80.aspx
http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.shell.interop.uicontextguids.aspx

MS CRM 2011 forms: "visible by default" Field Property checkbox is not working

I recently migrated a crm 4 database to crm 2011. We are using the on premise version.
I am now adapting scripts and forms to crm 2011. With one of my forms I have the following issue: I am trying to hide the owner from the form.
To do this I have unchecked the "Visible by default" check box on the Field Properties form.
This works on most forms, but on one of the forms the owner is displayed no matter what I do. I've been able to move the field around in the form, remove the label etc. All this works, but changes to the visibility will not work.
Has anybody seen this? Why is it not working? Does anybody have an idea on how to fix the issue without writing custom javascript (this probably would work, but I'm interested in getting the "Visible by default" checkbox to work again.
Update
The custom entity only has a single form defined.
There is no custom javascript on this form but I checked for javascript errors anyway and there were none. Even more strange, if I look at the html code I see
the following code if the check box is NOT ticked (style visiblity set to visible!):
<td class="ms-crm-FieldLabel-LeftAlign ms-crm-Field-Required" id="ownerid_c" style="visibility: visible;">
whereas is the checkbox IS ticked I will get (no style attribute at all!):
<td class="ms-crm-FieldLabel-LeftAlign ms-crm-Field-Required" id="ownerid_c">
needless to say that I did publish my changes.
On a form where the check box is working as expected I see the following html (style attribute display set to none):
<td class="ms-crm-FieldLabel-LeftAlign ms-crm-Field-Required" id="ownerid_c" style="display: none;">
Update 2
This issue is happening in the update form of the entity (so the owner is not null).
I moved the owner to a section and tried hiding the section, but unfortunately the section
will not hide. *But if I move the owner out of the section, the section will hide. I wonder what this owner field has, that won't let me hide it.
This plagued me for ages until I realized what was going on. I was trying to make an update form completely readonly. The CRM form requires at least one readable field on an update form. When it doesn't find one, it leaves one visible and readable (normally the owner or name field or whatever happens to be the last field on the form). I found the following javascript code somewhere and it works well to disable all the fields on the form without showing the pesky owner field that you have set not visible by default. (Props to the author as I don't remember where I got it)
function DisableFormFields() {
Xrm.Page.ui.controls.forEach(function(control, index) {
if (doesControlHaveAttribute(control)) {
control.setDisabled(true);
}
});
}
function doesControlHaveAttribute(control) {
var controlType = control.getControlType();
return controlType != "iframe" && controlType != "webresource" && controlType != "subgrid";
}
Because this is such a basic part of forms in CRM, I'm going to guess that your true problem is something else that results in the field not getting hidden. If you push F12 in IE, and go to the script tab to debug, do you see any javascript errors?
Also ensure you're looking at the correct from. In CRM 2011, you can have different forms for different roles, so be sure to check the forms drop down on the upper left.
And don't forget to publish your customizations!
Edit
I do know that if a field is marked as required, it will display it anyway if you attempt to save the form and it is null. Is this happening on a create of the entity, or an update of the entity with the value already populated?
I'd try moving the field to it's own section, and making the whole section not visible. This is also a good practice because if you add additional fields to the form, the hidden ones will take up space in the form.
Are you using the RTM version of CRM 2011? there is a known bug that has been fixed with rollup 1
You cannot set field visibility to "true" through the Client API when the Visible by Default field is not selected.
maybe your problem is related/connected to this bug
you can read the changelog here:
Update Rollup 1 for Microsoft Dynamics CRM 2011 is available

Accessing Ribbon Controls Programatically in an XML Ribbon

For programming Office Add-ins using C# 4.0, Microsoft provides two different ways of creating and/or modifying the Ribbon interface: you can use the Ribbon Designer or define the Ribbon's layout in Ribbon XML.
If you create a ribbon using the Ribbon designer, the class generated in the code behind has visibility to all the controls you've placed on the ribbon. So if I've placed a RibbonDropDown called "dropdown1", I could use the following code to add an item to it:
RibbonDropDownItem item = Factory.CreateRibbonDropDownItem();
item.Label = submatrix.Name;
item.Tag = submatrix;
this.dropDown1.Items.Add(item);
However, if you create the same Ribbon using Ribbon XML, dropDown1 or Factory aren't found ("The name does not exist in the current context").
Is there a way to access the items added to a Ribbon XML-defined ribbon in code?
Might be a little late, but hopefully this helps someone.
I was utterly confused about this same issue. Turns out, you can only access these controls as string ids, and the model is heavy on invalidation events. So for example, when you get a button click via onAction method, you only have the sender's id from the control object, however, in this event handler, you can invalidate the other controls and have their events called using
ribbon.InvalidateControl("MyCtl");
check out this MS Lab, it has everything you need to get up and running

Resources