Category Archives: Development

My development efforts ( mostly Microsoft .NET )

Dynamics CRM JavaScript: Ensure the current record is Saved before opening a new record

In yesterday’s post, Tracing the Dynamics CRM Form Data save Operation, I discussed identifying data that would be sent to the database when you saved a Dynamics CRM record.

Part of the issue I was facing was related to the form not completing the save operation before I opened a new form record. Since Dynamics CRM 2013 and 2015 user interface rarely opens a new window, the user was getting a warning that there was unsaved data when the page was being switched between the Account and, in this case, the Opportunity record.

So to prevent this from happening, I had to wait for the save to complete then open the new Opportunity record.

Here is the code where I do that:

……

var parameters = { customer_id: customerId, customer_name: customerName, customer_type: customerType, name: Xrm.Page.getAttribute("name").getValue() }; var windowOptions = { openInNewWindow: true }; setTimeout(function () { openNewForm(parameters); }, 2000); } function openNewForm(parameters) { var windowOptions = { openInNewWindow: true }; Xrm.Utility.openEntityForm("opportunity", null, parameters, windowOptions); }

The Details

We are using the JavaScript method setTimeout to wait for 2,000 milliseconds (2 seconds for those of us who are not computers).

At the end of the wait period, we will call a function called openNewForm which will use the Dynamics CRM method Xrm.Utility.openEntityForm, which opens a new Opportunity form.

You may need to adjust the wait period up or down, depending on your environment. The idea is to wait long enough for the save to complete, but not long enough to irritate the user.

Tracing the Dynamics CRM Form Data save Operation

I ran into an interesting issue this week where I was opening up a new Opportunity Entity form from the Account record via a Command Bar button.

The issue was that I was getting a warning that I was about to discard changes to the record I was leaving, and would l like to Continue or Cancel.

I verified all of the JavaScript and found that I was not performing any updates on form load, so there must be something else happening to modify a value on the form so that CRM thinks the record needs to be saved.

But what was being changed?

I thought about it for a bit and realized I could have CRM tell me what was happening, though a little known or used method called Xrm.Page.data.entity.getDataXml.

Dynamics CRM, by and large, does not send unaltered data to the database when a Save occurs. By using getDataXml, I can see exactly which field or fields were being modified.

Implementation

To implement this code, I created a small onSave method:

function onSave() {
    alert(Xrm.Page.data.entity.getDataXml());
}

Then created an OnSave event on the form properties:

image

Note: Make sure this event is the final OnSave event to ensure that all of the other OnSave events have completed.

This will display a message box showing what fields have been changed and the new values. Basically the same thing that will be sent to the database.

Resolution

It turns out that in this particular case, I was programmatically:

  1. Setting a field value
  2. Saving the record
  3. Opening a new related record

The issue was that the Save operation had not completed so the field I had modified was still considered dirty.

This allowed me to correct that issue and now things work as expected.

Up Next

My next article will outline how to work around the saving of the record while navigating to another record issue I ran into above.

Dynamics CRM JavaScript: Working with Tabs and Sections-The right way

Back in March I wrote the following article: Dynamics CRM Developer Tip O’ the Day: Working with Tabs.

Today I was working on some pre-existing customer JavaScript and ran across this:

Xrm.Page.ui.tabs.get(0).sections.get(6).setVisible(false); // Hide section

Don’t ever do that! Bad developer! Bad!

So again, never, ever use the numerical value to reference a Tab or a Section, unless you have no choice. 

We don’t want to use numbers because if you move or add Tabs or Sections, your code will no longer work.

Do this instead:

Xrm.Page.ui.tabs.get("General").sections.get("Details").setVisible(false); // Hide section

As long as your Tabs and Sections are named properly, you should not have an issue.

DFW Mobile .NET meeting this week: Mobile Design Patterns with Xamarin Forms

I am presenting at the DFW Mobile .NET meeting on Wednesday, August 12th.  You can find out more, and register for the meeting here.

In this discussion we will cover both user interface and code–based design patterns many developers may encounter while working with Xamarin Forms to develop cross–platform mobile applications.

I hope to see you there.

Mitch

Source code recovery tip: Was this written in c# or VB.NET?

Every now and again I run into the unfortunate circumstance where someone has lost the source code to a Dynamics CRM plugin (or something similar).

When you encounter this type of situation your only hope is to decompile the source using the plugin assembly’s DLL file and spend a good deal of time modifying the source code to get close to what the original author wrote.

Today my tool of choice is JustDecompile from the fine folks at Telerik.

Decompilation is not an exact science simply because the code the compiler ended up generating is not exactly like the code the author wrote. Most of the time this is caused by optimizations that the compiler has inserted which replace the original author’s code with code that it thinks is “better” or in some cases, just because.

So when reviewing a decompilation, it is sometimes hard to make a determination if what you see is compiler-generated or user-generated, especially when it is not your code. And I should note that I have seen a lot of code written by people whose main job was not that of a developer, who grabbed some code out of the Dynamics CRM SDK or off of the Internet to solve a problem, without really knowing what they were doing.

So when you look at the decompiled code, you are always asking: did they write it this way, or is this the way it was compiled?

Today I ran into something quite different.

First off, the using statements look very strange:

image

And some of the comparison operations used methods that I had never seen before:

image

Which is in the Microsoft.VisualBasic.CompilerServices namespace. 

Very odd.  Why would a developer writing C# use Visual Basic.NET assemblies?

The answer is: They would not.  This code was originally written in VB.NET, not C#.

So I am still going to use the C# decompilation code, but knowing that this was originally written in VB.NET helps me understand a little bit better some of the code that I am seeing.

Just another note to put into your book somewhere.

Upcoming Training: JavaScript Development with Dynamics CRM 2015

Hi Everyone,

I am holding my JavaScript development workshop in conjunction with the CRMUG Academy.

Visit this page to register.

Here are the details:

The goal of this 2 day class is to provide every student with a very thorough introduction to using JavaScript within Dynamics CRM. This is an Internet-based, hands-on workshop with each student provided their own virtual development environment for the duration of the class. Each classroom day will run from 10:45am to 5:00pm Eastern with the virtual environments available for student use until midnight on the second day.

And thanks to our virtual development environments, the majority of our time will be spent actually developing JavaScript solutions for Dynamics CRM. Think labs. Lots and lots of labs. And homework. There will be homework.

We’ll cover the following topics:

  • Creating a development environment ?Setup

    • Source control

    • Working in teams

    • Working with Visual Studio

  • Working with Web Resources

  • Working with Solutions

  • Working with Forms ?JavaScript libraries

    • Form events

    • Form Event Handler Execution Context Reference

  • Working with the Xrm.Page Object Model ?Working with Collections

    • Data operations

    • Tabs and Sections

    • Working with Controls

    • Working with iFrames

    • Working with Navigation Items

  • Ribbon button and JavaScript connection

  • Opening Dynamics CRM Forms and Web Resources via JavaScript

  • Using the XrmSrcToolkit to CRM-related data operations

We will be using about 75 of the methods found in the Xrm.Page object model so you should leave class with a fairly good understanding of where things are and how to access them. If we have time, we will also cover some of the freely available JavaScript components that can be used to aid in your development efforts and to increase your user’s productivity.

Students will also receive a draft copy of my upcoming book on Dynamics CRM JavaScript development along with sample code and utility web resources that should help you kick start your CRM JavaScript development efforts.

Using the XrmSvcToolkit to perform the following operations using JavaScript:

  • Create

  • Retrieve

  • Update

  • Delete

We will also be covering advanced topics such as working with the Dynamics CRM tablet client.

Student Prerequisite Knowledge:

  • Each student must have working knowledge of Dynamics CRM 2011, 2013, or 2015.

  • Knowledge of JavaScript is also required.

Preparation: The detailed instructions for connecting and attending the class will be sent one to two days prior to class. Students have the option of connecting to the class via conference call or VOIP. If using VOIP, a headset with a microphone is strongly recommended. Your instructor will use a hands-on training environment. You will receive a separate email with the setup instructions, and a dual monitor is strongly recommended to facilitate navigation.

Let me know if you have any questions.

Thanks, Mitch

Dynamics CRM Developer Tip O’ the Day: Working with Tabs

I learned an important lesson yesterday, again, on working with tabs with Dynamics CRM forms.

There are several ways to reference tabs when writing JavaScript and using the Xrm.Page model.  You may reference the tab by number, like this:

Xrm.Page.ui.tabs.get(2)

Note: Tabs are numbered starting with zero for the first tab.

Or by name, like this:

Xrm.Page.ui.tabs.get("tab2Tab")

It is always a best practice to use the name of the tab instead of the number because if you ever move the tab around the form, then the number will no longer be valid and any JavaScript you have written which references a particular tab in such a manner will start to cause some very strange user experiences.

By using the name of the tab, you will always be referring to the same tab, no matter where it may be located on the form.

In my particular case, this was a Dynamics CRM 2013 installation that had been upgraded from 4.0.  My JavaScript conversion tool, Transformer!, automatically converts the tab references using the number because of the naming convention applied to the converted forms, which is a GUID.

I had merged the original Information form with the new Entity form and in doing so, I had inadvertently moved the tabs out of place.  The logic in JavaScript involved changing the title of a tab as well as showing and hiding one of two different tabs, based on the value of an Option Set field.

I worked around my issue by renaming my tabs from the GUID value to tabxTab, which was the original CRM 4.0 naming format then modified my JavaScript to use that name instead of the number.

By the way, the same practice also applies to form Sections, which are named and referenced in a similar manner.

Visual Studio: Where did my macros go

 

So I have this open-source project that I created a couple of years ago that helps the Dynamics CRM .NET developer upgrade their code from Dynamics CRM 4.0 to the programming model we now use with Dynamics CRM, which started with Dynamics CRM 2011.

I have only had 68 downloads of this project since it was uploaded, and I have always thought that was rather low.  This week, when I went to use it myself, I found out why:  Microsoft decided to remove the Macros capability in Visual Studio 2012.

Their official statement was that their usage data showed that less than 1% of developers used that feature.  I guess this is one of those few times in life that being in the 1% is just not good, huh?

Alternatives

It does, however, look like there may be some help circumventing this problem, in the form of Visual Studio extensions:

I have yet to try either of these, and I am sure there are others, so if anyone has any experience with running and/or editing macros created with Visual Studio 2010 or before, I’d like to know about it.

Vote It Up!

Here is the UserVoice topic was created to give feedback on bringing back macros as built-in functionality. You can add your vote , if you so wish:

https://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/2650757-bring-back-macros

Recommendation: Aspose Words and Cells

Several of my tools generate Microsoft Excel and Word documents as part of their output and for the longest time, I have used various open-source libraries to provide me with OpenDocumentXML functionality to generate real worksheets and documents, instead of just CSV and plain text files.

The only problem, especially with my SnapShot! documentation utility, was the amount of time it was taking to generate documents. SnapShot! generates a ton of worksheets and documents and I was growing increasing concerned with the generation time.

So I started looking around for alternatives and I kept coming back to a company celled Aspose.  I have been looking at their components for years and they have two products that I needed:

Aspose.Cells, for generating Microsoft Excel files and Aspose.Words for generating Microsoft Word documents.

Full disclosure: They offer a free copy of their component libraries to Microsoft MVPs so I took them up on their offer.

Let me just say that I am extremely impressed with both Cells and Words.  Extremely impressed.

Not only are their components easy to use, but they are blazingly fast.

Working on SnapShot!, I took me about about 1.5 hours to move both my worksheet generation and document generation from the OpenDocumentXML libraries to Cells and Words. And that is including reading their documentation to figure out how to do things like:

  • Automatically generate tables of content
  • Insert figures into a Word document
  • Word headers and footers
  • Turning on filtering on an Excel worksheet
  • Creating a workbook containing multiple worksheets
  • etc.

And did I mention fast?  Oh My Gosh! Fast.

Cells generates Excel worksheets like it is being paid by the row and it’s almost 5:00pm on Friday night.

The combination of Cells and Words sped up the document generation process between 40% and 50%, depending on the data being extracted from Dynamics CRM.

Again, I am very impressed with the quality and speed of this product.  I don’t give out recommendations often, but this is surely one of them.

And don’t forget that Cells and Words are just two components in their library. I can’t wait to get to use some of the others, like Aspose.Diagram, which generates Microsoft Visio diagrams. Think about that as you are creating your own documentation…

Anyway, that’s my recommendation for today.  Take a look if you have time and interest.

Developing applications for multiple versions of Dynamics CRM

As most of you know, I have a several add-on products for Dynamics CRM developers, administrators, and consultants.

They all share common user interface and application frameworks that I developed several years ago.

When I first started writing these tools all I had to worry about was Dynamics CRM 4.0, which was pretty easy. Then Dynamics CRM 2011 shipped, which made things a bit more difficult, but not impossible.

I just had to separate my internal components so that one section worked with CRM 4.0, and the other with CRM 2011.  Once I got the unified connection user interface component working correctly, everything else just fell into place and things were all warm and cozy.

CRM 4.0 and CRM 2011 communicate over very different means so even though you ask for a single set of information from the user, you must appropriately handle the connection specific to the platform on the back-end.  That took a little bit of work to make happen flawlessly every time.

Then Microsoft shipped Dynamics 2013.  Still not a big deal as I could use the CRM 2011 SDK assemblies to communicate without any issue.

Then Microsoft shipped Dynamics 2015 and I started thinking that maybe I need to re-examine my methodology.  And I am quite happy with my solution.

 

Requirements

I need to communicate with Dynamics CRM 2011, 2013, and 2015 while changing the smallest amount of code possible.

I also had to stay with .NET 4.0, since my user interface framework does not support .NET 4.5 and I was not in a position to write the entire UI for a handful of products.

 

Solution

The solution was to upgrade my SDK .NET assemblies to the Dynamics CRM 2013 level. 

That gave me access to all three platforms as long as I kept in mind that I could never ask for something that did not exist on a specific platform. 

Like SLAs or Entitlements from CRM 2011. Since those do not exist until 2013, it would throw an error.

 

Further Thoughts

For some reason, I had never tried connecting to a lower-version environment using the newest SDK assemblies.  Turns out that works just fine. (silly me for never asking the question till a month ago).

If my UI framework was .NET 4.5 compatible, I would have jumped up to the CRM 2015 assemblies because that is what they are written with.

The only side-effect to this is that I am stuck using Visual Studio 2010 because that same UI framework doesn’t work with Visual Studio 2013, my normal environment.

 

Conclusion

This process was quite eye-opening for me and just about as painless as it could be.  The Microsoft SDK team has done a huge amount of really great work that makes our jobs as developers, much less work than it could be, or even once was.

Let me know if you have had similar or even different experiences.