Published Wednesday, January 07, 2009 10:10 AM by crmblog
This month I am pleased to introduce Jeremy Hofmann, a Manager in Crowe Horwath's Dynamics CRM practice and is responsible for many successful deployments of Dynamics CRM across North America. In his usual style Jeremy steps out of the box and shows how Microsoft Windows Presentation Foundation can be leveraged in Microsoft Dynamics CRM 4.0.
Microsoft CRM 4.0 allows your organization to track accounts, contacts, leads, opportunities, activities, support cases, and a vast array of additional business information users need. In addition, you can leverage the platform to create your own related custom entities that model your business information and allow your users to manage day to day customer interactions.
From a user’s perspective, the power and flexibility to extend the system is incredible, but it has the potential to cause usability issues. How do busy users quickly and easily get access to the most relevant information? How can you provide a targeted, personalized user experience that presents CRM information and relationships directly to the user in time of need?
The answer is to customize the user experience by bringing together the most frequently used, and most relevant data all in one central location – to build a CRM “dashboard”. This is where the challenge presents itself – defining the information to be displayed, and determining how to build a fast and responsive solution to provide the best user experience possible.
In this article, I’ll show you how to build your own custom CRM dashboards, using Microsoft’s Windows Presentation Foundation to construct the user interface.
What You Will Need
You can design Windows Presentation Foundation (WPF) applications using Microsoft Visual Studio 2005 or 2008. I prefer to use VS 2008 since it’s the latest and greatest. Feel free to use the Visual Studio 2008 Express edition, as it is free to download and easy to install.
If you use Visual Studio 2005, you will need to download and install the Visual Studio 2005 Extensions for .NET 3.0.
The Dashboard
Here’s a sample dashboard we’ll build together. This dashboard lists CRM accounts on the left side. When an account is selected on the left, the dashboard will display the related record information on the right. You can add, remove, or customize the panels as you see fit for your users. In a moment, you’ll see how easy it is to bind CRM data to a WPF template in order to display this information.
Getting Started
Let’s get started by creating a new Windows Presentation Foundation project in Visual Studio. Select the “WPF Application” template and give your project a name.
Visual Studio will load the template and present you with a brand new Window1.xaml file for you to work with. Add a web reference to the CRM web server. I called mine CRMService. If you need additional information on this topic, there is more information in the CRM 4.0 SDK on how to do this.
Hint: If you are using Visual Studio 2008, you may have a little trouble finding the “Add Web Reference” option. It’s still there – first use the “Add Service Reference” menu, and then click the Advanced button.
We’ll also need to reference the Microsoft CRM SDK .dlls, microsoft.crm.sdk.dll, and microsoft.crm.sdktypeproxy.dll, which can be found in the bin folder of the Microsoft CRM SDK. Go ahead and set a reference to those assemblies as well.
Actually, we’ll be referencing the CRM web service and the SDK assemblies quite a bit, so let’s set up a using statement to make things a little cleaner. Now we’re ready to go!
using CRM.Dashboard.CRMService;
The Account List
The first thing we’ll do is create the account list on the left side. This will allow the user to select an account. In this section, we’ll connect to the CRM web server, query all accounts, and then create the WPF markup to display the accounts. Finally, we’ll show how to bind the accounts to the WPF control.
View the code behind the Window1.xaml, add a class level variable to reference the CRMService, and modify the default Window1 constructor to create a new instance of to the CRM service.
1: public partial class Window1 : Window
2: {
3: // reference to the CRM web service
4: CrmService crmService = null;
5:
6: ...
7: }
8:
9: public Window1()
10: {
11: // default code – leave this here
12: InitializeComponent();
13:
14: // create an authentication token
15: CrmAuthenticationToken token = new CrmAuthenticationToken();
16: token.AuthenticationType = 0;
17: token.OrganizationName = "MicrosoftCRM";
18:
19: // set up the CRM web service
20: crmService = new CrmService();
21: crmService.Url = "http://crm/mscrmservices/2007/crmservice.asmx";
22: crmService.CrmAuthenticationTokenValue = token;
23: crmService.Credentials =
24: System.Net.CredentialCache.DefaultCredentials;
You may have to change the CRM service URL to point to your CRM server. If you’re using the Microsoft Virtual PC image, change it to http://localhost:5555/....
So far this is just standard CRM development. We’ve added a web reference to our CRM web service and we’ve created an instance of the class.
Now we need to connect to the CRM web service and retrieve the CRM accounts. We’ll retrieve the accounts as BusinessEntity objects so that we can bind to them using WPF. In the Window1 constructor, add the following code after the code to connect to the CRM web service.
1: // Create the QueryExpression object.
2: QueryExpression query = new QueryExpression();
3: query.EntityName = EntityName.account.ToString();
4: query.ColumnSet = new AllColumns();
5:
6: // Retrieve the contacts.
7: BusinessEntityCollection accounts = crmService.RetrieveMultiple(query);
Great! This by itself should compile and run. However, it does not yet do anything too spectacular. Next is the fun part – creating your own XAML markup to render the account list any way you’d like.
WPF Designer
Switch over to the Window1.xaml designer. You should see the following design pane:
Notice here there are two main design panes. The top pane is a graphical preview of the changes you make in the markup section in the bottom pane. You can collapse either pane if you prefer to work in one or the other. I prefer to work in the text (bottom) pane since I like to torture myself…I mean, have total control…over the markup design. It’s also a great way to learn WPF markup if you are just getting started with it.
Notice also that there is a top-level Window element and a single Grid element within it. I won’t go into all the details here of the Window element, just know that it is the default element when creating WPF applications and that you can change some of the basics within it, such as the title bar text, height, and width of the window.
WPF Layout
Now some background – WPF is a little like HTML. The idea is for you to markup the layout and presentation for what you’d like to show and the system will render this markup for you when the program runs. It’s also a bit like HTML in that you can put elements and controls inside of one another, and “build up” your user interface as a sort of tree.
One of the main elements in WPF is the Grid. You can think of a Grid almost like a table, with rows and columns. Within the Grid, you can pin down controls so that they are rendered at an exact row and column.
In our dashboard, we’ll want to have two main columns. The first column will contain our accounts list, and the second column will contain all of our panels and related information.
Within the Grid element, place the following markup.
1: <Grid Name="grdMain" Background="DimGray">
2: <Grid.ColumnDefinitions>
3: <ColumnDefinition Width="40*"></ColumnDefinition>
4: <ColumnDefinition Width="60*"></ColumnDefinition>
5: </Grid.ColumnDefinitions>
6: </Grid>
The Width values tell WPF how wide to make each column. As a best practice, I like to make all of my column widths add up to 100. This allows you to treat each column width as a percentage, similar to HTML table design. In this example, the first column will take up 40% of the width, and the second column will take up the remaining 60%.
Another common layout element in WPF is the DockPanel. A DockPanel allows you to put other controls inside of it, and to “dock”, or fix, each control to either the top, bottom, left, or right side of the DockPanel. This allows for great flexibility and automatic stretching and resizing of the child controls.
Let’s throw down a new DockPanel in the first column of the Grid. Place the following markup in the Grid element, after the Grid.ColumnDefinitions element.
<DockPanel Grid.Column="0">
</DockPanel>
Back to the Accounts List
Now we can add our list view to display the list of accounts. Add the following markup within the DockPanel that we just created.
1: <DockPanel>
2:
3: <DockPanel DockPanel.Dock="Top">
4: <TextBlock DockPanel.Dock="Left" Text="Accounts"
5: Foreground="White" FontWeight="Bold"
6: FontSize="14">
7: </TextBlock>
8: </DockPanel>
9:
10: <ListView Name="lstMain" Foreground="Orange" Background="DimGray">
11: <ListView.View>
12: <GridView AllowsColumnReorder="true">
13: <GridViewColumn
14: DisplayMemberBinding="{Binding Path=name}"
15: Header="Name"/>
16: </GridView>
17: </ListView.View>
18: </ListView>
19:
20: </DockPanel>
Some things to point out here – first, there’s another DockPanel with a TextBlock inside. This is just a section header label we’ll use so the user can identify the section.
Second, we’re using the ListView control. This is just a WPF control that works very similar to a .NET data grid. You define one or more columns and then bind those columns to a field in your data.
In this example, we are defining a GridViewColumn element within the GridView, and then we are setting the DisplayMemberBinding attribute to the “name” property from our data. This is all it takes to tell WPF which field from the CRM record you want to display in that column! Feel free to add additional columns from the CRM account record if you’d like.
Finally, we need to tell this ListView where to get its data from. Back in the code view, add a line of code immediately after retrieving the CRM accounts from the CRM web service.
// Bind the accounts to the WPF list view
lstMain.ItemsSource = accounts.BusinessEntities;
Now if you compile and run, your CRM accounts list should be populating with a list of accounts from your CRM system. Fantastic!
Related Panels
What kind of dashboard would we have if we didn’t display related information? In the following sections, we’ll add some additional panels to display the general information from the account, associated activities, and even mix in a CRM web page just for fun.
General Information Panel
In this panel, we’ll display a few of the account record details. Begin by adding the following markup to the designer, immediately after the first DockPanel that we added above.
1: <DockPanel Name="panMain" Grid.Column="1" LastChildFill="True">
2: <StackPanel DockPanel.Dock="Top">
3: <StackPanel>
4: <TextBlock Text="General Information" Foreground="White" FontWeight="Bold" FontSize="14"></TextBlock>
5: <TextBlock Text="{Binding Path=name}"
6: Foreground="Orange"></TextBlock>
7: <TextBlock Text="{Binding Path=address1_city}" Foreground="Orange"></TextBlock>
8: <TextBlock Text="{Binding Path=address1_state}"
9: Foreground="Orange"></TextBlock>
10: <Canvas Height="20"></Canvas>
11: </StackPanel>
12: </StackPanel>
13: </DockPanel>
This is just another DockPanel. This time we threw a StackPanel inside of it, placed a StackPanel inside of that, then added four TextBlock controls inside of that. The first StackPanel just anchors the following controls to the top of the DockPanel. The second StackPanel then tells WPF to render the following controls in order, top to bottom. The first TextBlock just displays some fixed text for the section header. Each TextBlock after that is bound to a specific field on the account record.
Don’t worry too much about the binding syntax for now – it looks a little funny at first but there are plenty of samples out there for you to reference. The key is to set the Path property to the attribute that you want to display.
The real magic comes when we bind the DockPanel to the account record. To do this, first we need to add an event handler to the account list, telling it to run some code whenever anyone clicks an account. Add a MouseUp property to the lstMain ListView control that we created earlier.
<ListView Name="lstMain" MouseUp="GridViewColumn_MouseUp" ...>
Now switch back to the code and add the following event handler code.
1: private void GridViewColumn_MouseUp(object sender,
2: MouseButtonEventArgs e)
3: {
4: ListView c = (ListView)sender;
5: account a = (account)c.SelectedItem;
6: panMain.DataContext = a;
7: }
Now whenever someone clicks an account in the account list, the program will pull the account out of the list and bind the DockPanel to that account. It’s that easy.
Run the program again and select an account to make sure you’re on the right track.
Activities Panel
In the next panel, we’ll display a list of the open and closed activities for the account. Add the following markup to the designer, immediately under the last StackPanel that we added for the “General Information” section, and within the DockPanel.
1: <StackPanel DockPanel.Dock="Top">
2: <StackPanel>
3: <TextBlock Text="Activities" Foreground="White" FontWeight="Bold"
4: FontSize="14">
5: </TextBlock>
6: <ListView Name="lstActivity" Background="DimGray"
7: Foreground="Orange">
8: <ListView.View>
9: <GridView>
10: <GridViewColumn DisplayMemberBinding="{Binding
11: Path=subject}" Header="Subject"/>
12: <GridViewColumn DisplayMemberBinding="{Binding
13: Path=scheduledend.date}"
14: Header="Due Date"/>
15: </GridView>
16: </ListView.View>
17: </ListView>
18: <Canvas Height="20"></Canvas>
19: </StackPanel>
20: </StackPanel>
This is just like the ListView that we added to display the accounts. The only difference here is that we are displaying fields from the activity entity.
Back in the code behind, we’ll need to grab the activities for the selected account and bind the results to the ListView. Place this code in the GridViewColumn_MouseUp handler, immediately after the existing code.
1: // Create the query object
2: QueryByAttribute query = new QueryByAttribute();
3: query.ColumnSet = new AllColumns();
4: query.EntityName = EntityName.activitypointer.ToString();
5:
6: // This query will retrieve all activities for the account
7: query.Attributes = new string[] { "regardingobjectid" };
8: query.Values = new string[] { a.accountid.Value.ToString() };
9:
10: // Execute the retrieval
11: BusinessEntityCollection retrieved =
12: crmService.RetrieveMultiple(query);
13:
14: // Bind the activities to the list view
15: lstActivity.ItemsSource = retrieved.BusinessEntities;
Run the program again to make sure everything is coded properly. You now have a working WPF dashboard!
Embedding a Web Page
The final dashboard component we’ll add is an embedded web page to display the CRM activity. When the user clicks the activity in the activity list, the panel will display the actual CRM activity web page directly in the WPF application.
First, let’s add the event handler markup for the MouseUp event to the lstActivity ListView.
<ListView Name="lstActivity" MouseUp="lstActivity_MouseUp" ... >
Now add the markup to display the web page, immediately after the last StackPanel but still within the main DockPanel.
1: <DockPanel>
2: <DockPanel>
3: <TextBlock DockPanel.Dock="Top" Text="Record Details"
4: Foreground="White" FontWeight="Bold"
5: FontSize="14"></TextBlock>
6: <Frame Name="frmCRM"
7: ScrollViewer.CanContentScroll="True"></Frame>
8: </DockPanel>
9: </DockPanel>
This works a bit like a CRM iFrame control. Our job is to set the Frame’s source property in the click event. Add the following in the code behind.
private void lstActivity_MouseUp(object sender, MouseButtonEventArgs e)
1: private void lstActivity_MouseUp(object sender, MouseButtonEventArgs e)
2: {
3: ListView c = (ListView)sender;
4: activitypointer a = (activitypointer)c.SelectedItem;
5: string activityId = a.activityid.Value.ToString();
6: this.frmCRM.Source = new
7: Uri("http://crm/activities/task/edit.aspx?id={"
8: + activityId + "}");
9: }
Now when a user clicks the activity, the Frame will switch to the CRM web page for that activity.
Give it a try.
Decorating the Panels
A WPF application wouldn’t be a WPF application unless you spiced up the style a bit. I this section, I’ll show you some tricks you can use to give the dashboard a little pizzazz.
Let’s add a rounded border to our panels. WPF makes this very easy if you use a Border element. You can decorate your DockPanels and StackPanels with a Border element, and set the CornerRadius and Margin properties so that they will render with a rounded table effect.
Add the following markup just inside the first DockPanel.
1: <DockPanel Grid.Column="0">
2: <Border BorderBrush="White" BorderThickness="1"
3: CornerRadius="5" Margin="5">
4:
5: ...
6:
7: </Border>
8: </DockPanel>
Notice the results. You can control the “roundness” of the border by adjusting the CornerRadius property. Also, play around with the BorderThickness property to increase or decrease the border’s width.
Repeat the Border markup inside of the General Information, Activities List, and Activity Frame panels. For the General Information and Activities list, you will want to place the border directly inside of the StackPanel element. For the Activity Frame, place it directly inside of the first DockPanel.
1: <DockPanel Name="panMain" Grid.Column="1" LastChildFill="True">
2: <StackPanel DockPanel.Dock="Top">
3: <Border BorderBrush="White" BorderThickness="1"
4: CornerRadius="5" Margin="5">
5: ...
6: </Border>
7: </StackPanel>
8:
9: <StackPanel DockPanel.Dock="Top">
10: <Border BorderBrush="White" BorderThickness="1"
11: CornerRadius="5" Margin="5">
12: ...
13: </Border>
14: </StackPanel>
15:
16: <DockPanel>
17: <Border BorderBrush="White" BorderThickness="1"
18: CornerRadius="5" Margin="5">
19: ...
20: </Border>
21: </DockPanel>
22: </DockPanel>
Finally, we’ll set the title property, startup size, and start up location of the Window element at the very top of the markup designer.
1: <Window x:Class="CRM.Dashboard.Window1"
2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4: Title="CRM Dashboard" Height="400" Width="600"
5: WindowState="Maximized" WindowStartupLocation="CenterScreen">
Next Steps
By now you’ve seen how to create a basic WPF application, how to add markup to display CRM information, and how to connect your application to your CRM data.
You could take this application and add additional panels, display geographic or map information, embed SQL Reporting Service reports, SharePoint parts, or any other data that your users would find relevant.
You could also extend the dashboard to allow your users to import and arrange custom panels that you publish to a central location. The sky’s the limit.
Conclusion
We’ve seen how CRM 4.0 can be combined with WPF to give your users a rich client experience, with relevant CRM information at their fingertips. All of this takes place inside of a rich client-side application that will provide your users with a speedy, interactive application that can be enhanced over time.
I encourage you to try building a dashboard of your own, and to learn more about the display capabilities of Windows Presentation Foundation. I think you’ll find it provides a powerful new interface for building rich client applications that will make your users more productive, well-organized, and happy.
Cheers,
Jeremy is a Manager within Crowe Horwath’s CRM practice and specializes in combining Microsoft CRM with related technologies. He lives in the Chicago area and can be contacted at jeremy.hofmann@crowehorwath.com.
No comments:
Post a Comment