Learn to Use a Label Creator Add-in Extension in Dynamics 365 for Finance & Operations

By Mark Nelson | April 2, 2018

Learn to use a label creator add-in extension in dynamics 365 for finance & operations

Creating labels has always been considered a chore by developers, and the task hasn’t really gotten any easier with Dynamics 365. Wouldn’t it be nice if you could type your value into the property and let Visual Studio move it to a label file? Luckily, we can. I’ll show you how to use the label creator Add-in extension in Dynamics 365 for Finance & Operations.

Start by creating a new Dynamics 365 Developer Tools Add-in project.

Create new Dynamics 365 Developer Tools Addin project

This will create a new class library project that contains two source files. The first, DesignerContextMenuAddIn.cs, contains boilerplate code for creating a designer add-in. The second, MainMenuAddIn.cs, contains a menu add-in.

Label Creator Addin Extension

The first thing we will need to do is to add a few references to our project:

  • C:\Program Files (x86)\Common Files\Microsoft Shared\MSEnv\PublicAssemblies\
    • EnvDTE.dll
    • EnvDTE80.dll
  • C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\Extensions\5hutepyf.xp2\
    (Your folder may vary slightly)

    • Microsoft.Dynamics.Framework.Tools.LabelEditor.dll
    • Microsoft.Dynamics.Framework.Tools.ProjectSystem.dll
    • Microsoft.Dynamics.Framework.Tools.ProjectSupport.dll
  • C:\AOSService\PackagesLocalDirectory\bin\
    • Microsoft.Dynamics.AX.Metadata.dll
    • Microsoft.Dynamics.Framework.Tools.AutomationObjects.dll

Our designer’s context menu is controlled by the DesignerMenuExportMetadata attribute on the class. You can see in the screenshot that by default our add-in will appear in both the Form and Table designers.

The OnClick method is called when the user clicks on our menu. We will modify this method to convert the text contained in the Label property to an actual label. To do this, create regular expression to distinguish between a label identifier and a normal string.

private static Regex newLabelMatcher = 
    new Regex("\\A(?<AtSign>\\@)(?<LabelFileId>[a-zA-Z]\\w*):(?<LabelId>[a-zA-Z]\\w*)", 
    RegexOptions.Compiled | RegexOptions.CultureInvariant);
 
private static Regex legacyLabelMatcher = 
    new Regex("\\A(?<LabelId>(?<AtSign>\\@)(?<LabelFileId>[a-zA-Z][a-zA-Z][a-zA-Z])\\d+)", 
    RegexOptions.Compiled | RegexOptions.CultureInvariant);

These expressions will be used by the following methods.
private Boolean IsValidLabelId(String labelId)
{
    bool result = false;
 
    if (String.IsNullOrEmpty(labelId) == false)
    {
        result = DesignerContextMenuAddIn.IsLegacyLabelId(labelId);
        if (result == false)
        {
            result = (DesignerContextMenuAddIn.newLabelMatcher.Matches(labelId).Count > 0);
        }
    }
 
    return result;
}
 
private static Boolean IsLegacyLabelId(String labelId)
{
    bool result = false;
 
    if (String.IsNullOrEmpty(labelId) == false)
    {
        result = (DesignerContextMenuAddIn.legacyLabelMatcher.Matches(labelId).Count > 0);
    }
 
    return result;
}

Handle the click event in the OnClick method.  Here we will test the selected object, get the object’s model and label file, and create the label.
public override void OnClick(AddinDesignerEventArgs e)
{
    ModelInfoCollection modelInfoCollection = null;
 
    IMetaModelService metaModelService = null;
    // Get the metamodel provider
    IMetaModelProviders metaModelProvider = ServiceLocator.GetService(typeof(IMetaModelProviders)) as IMetaModelProviders;
 
    if (metaModelProvider != null)
    {
        // Get the metamodel service
        metaModelService = metaModelProvider.CurrentMetaModelService;
    }
 
    try
    {
        // Is the selected element a table?
        if (e.SelectedElement is ITable)
        {
            ITable table = e.SelectedElement as ITable;
 
            modelInfoCollection = metaModelService.GetTableModelInfo(table.Name);
            AxLabelFile labelFile = this.GetLabelFile(metaModelProvider, metaModelService, modelInfoCollection);
 
            this.createLabel(table, labelFile);
        }
    }
    catch (Exception ex)
    {
        CoreUtility.HandleExceptionWithErrorMessage(ex);
    }
}

private AxLabelFile GetLabelFile(IMetaModelProviders metaModelProviders, IMetaModelService metaModelService, ModelInfoCollection modelInfoCollection)
{
    // Choose the first model in the collection
    ModelInfo modelInfo = ((System.Collections.ObjectModel.Collection<ModelInfo>)modelInfoCollection)[0];
 
    // Construct a ModelLoadInfo
    ModelLoadInfo modelLoadInfo = new ModelLoadInfo
    {
        Id = modelInfo.Id,
        Layer = modelInfo.Layer,
    };
 
    // Get the list of label files from that model
    IList<String> labelFileNames = metaModelProviders.CurrentMetadataProvider.LabelFiles.ListObjectsForModel(modelInfo.Name);
 
    // Choose the first
    // What happens if there is no label file?
    AxLabelFile labelFile = metaModelService.GetLabelFile(labelFileNames[0], modelLoadInfo);
 
    return labelFile;
}
 
private void createLabel(ITable table, AxLabelFile labelFile)
{
    if (this.IsValidLabelId(table.Label) == false)
    {
        table.Label = this.FindOrCreateLabel(table.Label, table.Name, "Label", labelFile);
    }
 
    if (this.IsValidLabelId(table.DeveloperDocumentation) == false)
    {
        table.DeveloperDocumentation = this.FindOrCreateLabel(table.DeveloperDocumentation, table.Name, "DeveloperDocumentation", labelFile);
    }
}
 
private String FindOrCreateLabel(String labelText, String elementName, String propertyName, AxLabelFile labelFile)
{
    string newLabelId = String.Empty;
 
    // Don't bother if the string is empty
    if (String.IsNullOrEmpty(labelText) == false)
    {
        // Construct a label id that will be unique
        string labelId = $"{elementName}{propertyName}";
 
        // Get the label factor
        LabelControllerFactory factory = new LabelControllerFactory();
 
        // Get the label edit controller
        LabelEditorController labelController = factory.GetOrCreateLabelController(labelFile, DesignerContextMenuAddIn.Context);
 
        // Make sure the label doesn't exist.
        // What will you do if it does?
        if (labelController.Exists(labelId) == false)
        {
            labelController.Insert(labelId, labelText, String.Empty);
            labelController.Save();
 
            // Construct a label reference to go into the label property
            newLabelId = $"@{labelFile.LabelFileId}:{labelId}";
        }
    }
 
    return newLabelId;
}

Lastly, open the resource file and change the DesignerAddinCaption. This value will appear in the context menu item.

DesignerAddinCaption

To test your add-in, press F5 and Visual Studio will build your project and copy the dll to the appropriate folder. On my computer, the add-in folder is at C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\Extensions\5hutepyf.xp2\AddinExtensions\.

Create a Dynamics 365 Unified Operations project.

Unified Operations project

Add a new table to the project.

Add a new table to the project

Add a label file.

Add a label file

Open your table in the designer and type a value into the Label property.

Type a value into the Label property

Right-click the table node in the designer and move your mouse to the Add-ins menu.

Addins menu

Click the Create labels option and your add-in will create a label resource and update the Label property.

Label property

Label property

 

Related Posts

Recommended Reading:

Managing Your Business Through Uncertain Times Using Dynamics 365 Finance and Operations

  Dynamics 365 Finance and Operations (F&O) can help you make informed decisions on how to move your business forward. […]

Read the Article
5.13.22 Power Platform

Using Power BI Object Level Security

  The following article will demonstrate how to use Power BI Object Level Security to disable column data based on […]

Read the Article
5.12.22 Dynamics CRM

How to Use the Stoneridge Support Portal

Stoneridge Software’s support portal is an intuitive and useful function that makes it easy for you to access resources to […]

Read the Article
5.6.22 Dynamics GP

Dynamics GP Transaction Removal: Purchase Orders

  Are you having performance issues with Purchase Orders?  Do you find that there are old Purchase Orders on your […]

Read the Article
5.5.22 Dynamics GP

The Real Story about the Long-Term Future of Dynamics GP Support

I’ve seen a number of people put forward comment that Dynamics GP is going away and you have to get […]

Read the Article

New Features in Dynamics 365 Business Central 2022 Wave 1 Release – Financial Enhancements

The Dynamics 365 Businses Central 2022 Wave 1 Release has a lot of new and exciting features to help your […]

Read the Article
4.29.22 Dynamics GP

Dynamics GP Transaction Removals: Bank Reconciliation

  This is part 2 of a 3 part series on Dynamics GP Transaction Removals. These quick tips will hopefully […]

Read the Article
4.28.22 Dynamics GP

Uncommonly Used Features – Integrate Purchasing and Payables to Fixed Assets in Dynamics GP

Being able to integrate Purchasing and Payables to Fixed Assets in Dynamics GP will help you create visibility for your […]

Read the Article

New Features in the Dynamics 365 Business Central 2022 Wave 1 Release – Ease-of-Use Features

  There are many new features in the Dynamics 365 Business Central 2022 Wave 1 Release to get excited about! […]

Read the Article

Start the Conversation

It’s our mission to help clients win. We’d love to talk to you about the right business solutions to help you achieve your goals.

Subscribe To Our Blog

Sign up to get periodic updates on the latest posts.

Thank you for subscribing!

X