As part of the SP1 SDK update for Microsoft Dynamics CRM 2013, we got the new WPF Application for CRM Visual Studio Template. I was recently doing some research on this at TSG and thought I would write about what it is and how to use it.
Well the WPF Application for CRM is a Visual Studio project which comes as part of the CRM SDK Templates (which are part of the CRM 2013 SP1 SDK). It is a neat little bootstrap project that enables us to quickly create an application that connects to CRM.
First we need to download the CRM 2013 SP1 SDK, and extract to say C:\CRM-SDK, then we need to run C:\CRM-SDK\SDK\Templates\CRMSDKTemplates.vsix
- NuGet Package Manager
To resolve this we simply install the NuGet Package Manager from Visual Studio Gallery
here, and ensure the latest update is applied to Visual Studio, at the time of writing this is Update 3 and available
here.
We can also resolve the error by extracting the CRMSDKTemplates.vsix file, and edit the extension.vsixmanifest file and remove the following line:
<Dependency Id="NuPackToolsVsix.Microsoft.67e54e40-0ae3-42c5-a949-fddf5739e7a5" DisplayName="NuGet Package Manager" Version="[2.8.50126.400,3.0)" />
Then we need to zip the extracted files back up, rename to .vsix and re-run the VSIX file.
Now we can create a WPF Application for CMR Project, which is under CRM SDK Templates, in Visual Studio.
What does the project consist of?
![]()
As part of the Visual Studio project that is created we get a number of things. Within the bin\Debug\tools folder we get the standard CRM SDK files that we would expect when we need to connect to CRM.
We also get a LoginWindow folder, along with a Xaml CRM Login Form. The login form can also be added to an existing WPF application by right-clicking, then clicking New > Add Item and selecting it from CRM SDK Templates in the Add New Item dialog.
We get the standard App.config and App.xaml files that we would expect to see from a WPF application, along with a MainWindow.xaml form which would be the starting point of our application.
Note: The packages.config file is just a standard NuGet package file.
When trying to running the application we may receive an error in the CrmLogin.xaml that it can’t find the following resource:
<ResourceDictionary Source="pack://application:,,,/Microsoft.Xrm.Tooling.Ui.Resources;component/Resources/Button/Styles.xaml"/>
This is fixed by adding the
Microsoft.Xrm.Tooling.Ui.Resources.dll reference.
A look around the CRM Login Form
The CRM login form contains a user control from the Tooling library, which contains all of the standard fields we would normally have to put on to a form and maintain to provide the user with a login form.
![]()
We get a few events within the forms code behind to check the status of connection, to close the form if the user clicks cancel etc. but one of the key events is the window loaded event.
///<summary>
///<summary>
/// Raised when the window loads for the first time.
///</summary>
///<param name="sender"></param>
///<param name="e"></param>
privatevoid Window_Loaded(object sender, RoutedEventArgs e)
{
/*
This is the setup process for the login control,
The login control uses a class called CrmConnectionManager to manage the interaction with CRM, this class and also be queried as later points for information about the current connection.
In this case, the login control is referred to as CrmLoginCtrl
*/
// Set off flag.
bIsConnectedComplete = false;
// Init the CRM Connection manager..
mgr = newCrmConnectionManager();
// Pass a reference to the current UI or container control, this is used to synchronize UI threads In the login control
mgr.ParentControl = CrmLoginCtrl;
// if you are using an unmanaged client, excel for example, and need to store the config in the users local directory
// set this option to true.
mgr.UseUserLocalDirectoryForConfigStore = true;
// if you are using an unmanaged client, you need to provide the name of an exe to use to create app config key's for.
//mgr.HostApplicatioNameOveride = "MyExecName.exe";
// CrmLoginCtrl is the Login control, this sets the CrmConnection Manager into it.
CrmLoginCtrl.SetGlobalStoreAccess(mgr);
// There are several modes to the login control UI
CrmLoginCtrl.SetControlMode(ServerLoginConfigCtrlMode.FullLoginPanel);
// this wires an event that is raised when the login button is pressed.
CrmLoginCtrl.ConnectionCheckBegining += newEventHandler(CrmLoginCtrl_ConnectionCheckBegining);
// this wires an event that is raised when an error in the connect process occurs.
CrmLoginCtrl.ConnectErrorEvent += newEventHandler<ConnectErrorEventArgs>(CrmLoginCtrl_ConnectErrorEvent);
// this wires an event that is raised when a status event is returned.
CrmLoginCtrl.ConnectionStatusEvent += newEventHandler<ConnectStatusEventArgs>(CrmLoginCtrl_ConnectionStatusEvent);
// this wires an event that is raised when the user clicks the cancel button.
CrmLoginCtrl.UserCancelClicked += newEventHandler(CrmLoginCtrl_UserCancelClicked);
// Check to see if its possible to do an Auto Login
if (!mgr.RequireUserLogin())
{
if (MessageBox.Show("Credentials already saved in configuration\nChoose Yes to Auto Login or No to Reset Credentials", "Auto Login", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
{
// If RequireUserLogin is false, it means that there has been a successful login here before and the credentials are cached.
CrmLoginCtrl.IsEnabled = false;
// When running an auto login, you need to wire and listen to the events from the connection manager.
// Run Auto User Login process, Wire events.
mgr.ServerConnectionStatusUpdate += newEventHandler<ServerConnectStatusEventArgs>(mgr_ServerConnectionStatusUpdate);
mgr.ConnectionCheckComplete += newEventHandler<ServerConnectStatusEventArgs>(mgr_ConnectionCheckComplete);
// Start the connection process.
mgr.ConnectToServerCheck();
// Show the message grid.
CrmLoginCtrl.ShowMessageGrid();
}
}
}
Here we can see that we are using the new CrmConnectionManager which is part of the Microsoft.Xrm.Tooling.CrmConnectControl namespace. We set up the parent control to be the Login control, set whether we want to store the config in the user local user directory which can be used for persisting connection details. We can set whether we want to display the Full Login Panel or just the Config Panel, then we set up a number of event handlers to respond to connection status changes, and user cancellation event.
We can see that we can auto login if we have saved the configuration, quite useful for a background task console application or saving the user from having to type in everytime!
///<summary>
/// Complete Event from the Auto Login process
///</summary>
///<param name="sender"></param>
///<param name="e"></param>
privatevoid mgr_ConnectionCheckComplete(object sender, ServerConnectStatusEventArgs e)
{
// The Status event will contain information about the current login process, if Connected is false, then there is not yet a connection.
// Unwire events that we are not using anymore, this prevents issues if the user uses the control after a failed login.
((CrmConnectionManager)sender).ConnectionCheckComplete -= mgr_ConnectionCheckComplete;
((CrmConnectionManager)sender).ServerConnectionStatusUpdate -= mgr_ServerConnectionStatusUpdate;
if (!e.Connected)
{
// if its not connected pop the login screen here.
if (e.MultiOrgsFound)
MessageBox.Show("Unable to Login to CRM using cached credentials. Org Not found",
"Login Failure");
else
MessageBox.Show("Unable to Login to CRM using cached credentials", "Login Failure");
resetUiFlag = true;
CrmLoginCtrl.GoBackToLogin();
// Bad Login Get back on the UI.
Dispatcher.Invoke(DispatcherPriority.Normal,
new System.Action(() =>
{
this.Title = "Failed to Login with cached credentials.";
MessageBox.Show(this.Title, "Notification from ConnectionManager",
MessageBoxButton.OK, MessageBoxImage.Error);
CrmLoginCtrl.IsEnabled = true;
}
));
resetUiFlag = false;
}
else
{
// Good Login Get back on the UI
if (e.Connected && !bIsConnectedComplete)
ProcessSuccess();
}
}
Another event we get is the Connection Complete Check event, which is called from the auto login process, and allows us to validate that CRM Organisation was found etc.
///<summary>
/// Login control connect check status event.
///</summary>
///<param name="sender"></param>
///<param name="e"></param>
privatevoid CrmLoginCtrl_ConnectionStatusEvent(object sender, ConnectStatusEventArgs e)
{
//Here we are using the bIsConnectedComplete bool to check to make sure we only process this call once.
if (e.ConnectSucceeded && !bIsConnectedComplete)
ProcessSuccess();
}
///<summary>
/// This raises and processes Success
///</summary>
privatevoid ProcessSuccess()
{
resetUiFlag = true;
bIsConnectedComplete = true;
CrmSvc = mgr.CrmSvc;
CrmLoginCtrl.GoBackToLogin();
Dispatcher.Invoke(DispatcherPriority.Normal,
new System.Action(() =>
{
this.Title = "Notification from Parent";
CrmLoginCtrl.IsEnabled = true;
}
));
// Notify Caller that we are done with success.
if (ConnectionToCrmCompleted != null)
ConnectionToCrmCompleted(this, null);
resetUiFlag = false;
}
If we did log in OK via the user entering the credentials, then we get the connection status event, and call the Process Success method to clean up and notify the caller.
A look around the Main Window
The main window is fairly simple, it just contains a login button and you can’t get much simpler than that!
The code behind is also fairly simple and contains two events
///<summary>
/// Button to login to CRM and create a CrmService Client
///</summary>
///<param name="sender"></param>
///<param name="e"></param>
privatevoid LoginButton_Click(object sender, RoutedEventArgs e)
{
#region Login Control
// Establish the Login control
CrmLogin ctrl = newCrmLogin();
// Wire Event to login response.
ctrl.ConnectionToCrmCompleted += ctrl_ConnectionToCrmCompleted;
// Show the dialog.
ctrl.ShowDialog();
// Handel return.
if (ctrl.CrmConnectionMgr != null&& ctrl.CrmConnectionMgr.CrmSvc != null&& ctrl.CrmConnectionMgr.CrmSvc.IsReady)
MessageBox.Show("Good Connect");
else
MessageBox.Show("BadConnect");
#endregion
}
///<summary>
/// Raised when the login form process is completed.
///</summary>
///<param name="sender"></param>
///<param name="e"></param>
privatevoid ctrl_ConnectionToCrmCompleted(object sender, EventArgs e)
{
if (sender isCrmLogin)
{
this.Dispatcher.Invoke(() =>
{
((CrmLogin)sender).Close();
});
}
}
Here we can see that the On Click event of the button is wired up with some code to instantiate the Login form and handle the return event. The thing to note is that the Login form is show modally.
How do we Enable Tracing?
If we want to enable tracing then we simply modify the App.config file and add or edit the following lines :
<switches>
<!--
Possible values for switches: Off, Error, Warning, Info, Verbose
Verbose: includes Error, Warning, Info, Trace levels
Info: includes Error, Warning, Info levels
Warning: includes Error, Warning levels
Error: includes Error level
-->
<add name="Microsoft.Xrm.Tooling.Connector.CrmServiceClient" value="Verbose" />
<add name="Microsoft.Xrm.Tooling.CrmConnectControl" value="Verbose"/>
<add name="Microsoft.Xrm.Tooling.WebResourceUtility" value="Verbose" />
</switches>
This is great if you have a problem in the field and need a trace of what is going on with the application to diagnose an enviromental problem!
What else is available as part of the new Tooling namespace?
As part of the CRM 2013 SP1 SDK Spring ’14 Update we now also get the Microsoft.Xrm.Tooling namespace, which contains a few useful classes for connecting to CRM, but also for getting resources from CRM.
The following shows the classes that are available in the new Microsoft.Xrm.Tooling.WebResourceUtility namespace:
Class | Description |
| Web Resource actions for dealing with Image Resources. |
| This class provides an override for the default trace settings. These settings must be set before the components in the control are used for them to be effective. |
| |
| This class is used to access and retrieve web |
The following shows the notable methods from the ImageResources class:
Name | Description |
| Returns BitMap Image Resource from CRM |
The following shows the notable methods from the XmlResources class:
Name | Description |
| Returns Xml Resource from CRM |
Full details of the Web Resource Utility classes along with other helper classes on MSDN can be found
here and full details of Building Windows client applications using the XRM tools can be found
here.
This should give a good idea of the new features of the WPF Application for CRM and how to use it.
Until next time…
@simonjen1