Brendan Enrick

Daily Software Development

Silverlight Web Analytics: First Look

Anyone who has ever run a web site or a blog has probably taken a look at analytics packages to see who is visiting. I’ve recently been working on a very exciting project with Telerik, developing an application to provide this valuable information to bloggers and content creators.

To get things started we decided to tackle some of the basics first by asking these few questions:

  • “How many people are visiting my site?”
  • “Which locations drive the most traffic to my site?”
  • “What is my most popular content?”
  • “Who are the people visiting my site?”

From these simple questions we were able to start the application and get the cool demo set up that we were showing at the Telerik booth during PDC 2009.

The application is based around answering these four fundamental questions which are used to present the information to the users.

VisitorDashboard

Site Visit Information

RegionsDashboard

Regional Information

FavoritesDashboard

Most Trafficked Portions

ReferralsDashboard

Referring Locations

I had a great opportunity to speak with Todd Anglin about the project at PDC last month. My discussion of the Silverlight Web Analytics Application is available for viewing on Telerik TV. This demo of the application is a great way to see what we’re working on and give you an idea of what is coming soon.

Subscribe to my RSS feed and come back to read more as I will be posting about the architecture and some of the design decisions we made about the application.

Working with the Default Layout of Silverlight RadCharts

The default layout for a RadChart works for most situations. It has a ChartArea, a ChartLegend, and a ChartTitle. These are easy to work with, and if you want you can break from the norm and create your own custom Silverlight Rad Chart layout. If you’re sticking with the default you almost certainly have some settings and properties to which you will want to make adjustments. In order to do that you might define these in the XAML.

<UserControl x:Class="MyApplication.UI.Charts.SuperSweetChart"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:telerikChart="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Charting" 
    xmlns:chart="clr-namespace:Telerik.Windows.Controls.Charting;assembly=Telerik.Windows.Controls.Charting" 
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot" Background="White">
        <telerikChart:RadChart x:Name="Chart1" 
                               Loaded="Chart1_Loaded">
            <telerikChart:RadChart.DefaultView>
                <chart:ChartDefaultView>
                    <chart:ChartDefaultView.ChartArea>
                        <chart:ChartArea LegendName="CustomLegend" NoDataString="">
                            <chart:ChartArea.DataSeries>
                                <chart:DataSeries x:Name="DataSeries1" >
                                    <chart:DataSeries.Definition>
                                        <chart:PieSeriesDefinition 
                                            LabelOffset="0.6d" 
                                            ShowItemToolTips="True" 
                                            ItemToolTipFormat = "#XCAT" 
                                            DefaultLabelFormat = "#%{p0}" />
                                    </chart:DataSeries.Definition>
                                    <chart:DataPoint YValue="35" />
                                    <chart:DataPoint YValue="15" />
                                    <chart:DataPoint YValue="55" />
                                </chart:DataSeries>
                            </chart:ChartArea.DataSeries>
                        </chart:ChartArea>
                    </chart:ChartDefaultView.ChartArea>
                    
                    <chart:ChartDefaultView.ChartLegend>
                        <chart:ChartLegend x:Name="CustomLegend" 
                                           UseAutoGeneratedItems="True" />
                    </chart:ChartDefaultView.ChartLegend>
                    
                    <chart:ChartDefaultView.ChartTitle>
                        <chart:ChartTitle>
                            <TextBlock Text="Traffic Sources"/>
                        </chart:ChartTitle>
                    </chart:ChartDefaultView.ChartTitle>
                    
                </chart:ChartDefaultView>
            </telerikChart:RadChart.DefaultView>
        </telerikChart:RadChart>
    </Grid>

Notice the three parts are the ChartArea, the ChartLegend, and the ChartTitle. Use the properties of these to make your adjustments. If you don’t want to change these in the XAML then don’t include them. If you’re going to work from the code behind it doesn’t hurt to have these here, but it can be useful.

You can access these in the code behind by either declaring their x:Name property or by referencing them from the RadChart’s x:Name like this. Chart1.DefaultView.ChartArea.

Plus keeping things in here keeps designers happy, and we design-challenged people really appreciate happy designers willing to assist us.

How to Add a ToolTip to a RadGridView

So I was using a RadGridView to display some data and I wanted to add a tooltip with more information. Shouldn’t be too difficult. I of course checked through intellisense to see if there were any obviously named properties for setting a tooltip.

I checked on the RadGridView tag for the properties and I also checked the GridViewDataColumn for it. Didn’t come up with anything so I Binged until I found a potential solution. It didn’t really make sense to me and the solution had me doing a lot of what seemed like extra stuff. It was adjusting the tooltip using the styles for the cell. So, then it hit me that I was thinking about this the wrong way. Since I just wanted text and a tooltip why don’t I just add that in?

So I took control of things and I just used a template for the column instead. Here is the quick and easy way to add a tooltip into a RadGridView.

<grid:RadGridView x:Name="RadGridView1" AutoGenerateColumns="False">            
    <grid:RadGridView.Columns>
        <grid:GridViewColumn Header="Person">
            <grid:GridViewColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding NickName}" 
                       ToolTipService.ToolTip="{Binding FullName}" 
                       VerticalAlignment="Center" />
                </DataTemplate>
            </grid:GridViewColumn.CellTemplate>
        </grid:GridViewColumn>
        <grid:GridViewDataColumn Header="Place" 
          DataMemberBinding="{Binding Place}" />
        <grid:GridViewDataColumn Header="Thing" 
          DataMemberBinding="{Binding Thing}" />
    </grid:RadGridView.Columns>
</grid:RadGridView>

 

And there you have a tooltip on the text of a cell in a RadGridView. You can obviously add different content items into the DataTemplate so that you can support more advanced templates. Just throw the tooltip on the root element inside the DataTemplate and you’ll be set.

Build OpenAccess Project Using MSBuild on a Machine Without OpenAccess Installed

OpenAccess ORM is a powerful ORM tool that integrates very well into Visual Studio. It uses wizards and is tightly integrated, which makes it easy to pick up and start using. However, this same asset also provides a challenge when using this tool along with MSBuild on a machine without OpenAccess installed. There are some adjustments that needs to be made, since it is designed to be used with Visual Studio, so we have to add a couple of extra bits to get MSBuild to be able to build a project correctly.

So if you’re using the OpenAccess ORM tool and you want to use a build server you’ll need to make a couple of configuration changes to allow you to achieve this. Since I use a continuous integration server on pretty much every project I work on, I ran into this issue. There are a few things you need to do to get this working.

The first step to getting this working is to get the required dlls copied to a local directory. This is important because the build server will need to have these references for the project. To get these files all you need to do is look to see where the reference is coming from in your projects. In my case the location is.

C:\Program Files (x86)\Telerik\OpenAccess ORM\bin

So I copied the files being referenced by my project from here and put them into a lib folder in a relative directory for my project. I then removed all of the references to the libraries in their install directories and added the references back in this time pointing to the local directory.

AddLocalReferences

Once you have everything local you’ll want to make sure you got all of the required libraries by building your solution. The build should succeed.

When working with OpenAccess, it is important to know that the assembly used for persistence is going to be Enhanced by OpenAccess ORM. After Visual Studio builds that project it kicks off VEnhance.exe to inject IL into the library with added functionality.

This is the step that we next need to add in to our build so that we can achieve the desired result.

Find the VEnhance.exe file on your development machine. It should be located in a directory similar to this one.

C:\Program Files (x86)\Telerik\OpenAccess ORM\sdk

Copy that directory somewhere local to your project. We will need it in a minute. You also need to copy your Telerik.OpenAccess.dll into the local directory you created with the VEnhance.exe file. The executable will need that dll to be there in order for it to do the enhancement.

We will need to create a targets file which we will import into our project file. You can copy the one I have listed here and customize it to your needs. It is basically just the instructions to tell your project how to call the VEnhance executable. You will need to change the relative directory to your OpenAccessToolsDirectory in this targets file to reflect where you put the VEnhance.exe file.

enahnce.targets

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
 <PropertyGroup>
   <OpenAccessToolsDirectory>$(SolutionDir)..\util\Telerik</OpenAccessToolsDirectory>
</PropertyGroup>
 
 <Target Name="EnhanceAssembly" Condition="'$(_AssemblyTimestampBeforeCompile)'!='$(_AssemblyTimestampAfterCompile)'">
   <Copy SourceFiles="$(TargetPath)" DestinationFiles ="$(TargetPath).notenhanced"/>
   <Copy SourceFiles="$(PdbFile)" DestinationFiles ="$(PdbFile).notenhanced"/>
   <Exec IgnoreExitCode="false"
         WorkingDirectory="$(TargetDir)"
         Command="&quot;$(OpenAccessToolsDirectory)\venhance.exe&quot; -verboseMode:1 -signAssembly &quot;-keyFile:$(ProjectDir)$(AssemblyOriginatorKeyFile)&quot; &quot;-assembly:$(TargetPath)&quot;"
         Condition="'$(AssemblyOriginatorKeyFile)'!=''" />
   <Exec IgnoreExitCode="false"
         WorkingDirectory="$(TargetDir)"
         Command="&quot;$(OpenAccessToolsDirectory)\venhance.exe&quot; -verboseMode:1 &quot;-assembly:$(TargetPath)&quot;"
         Condition="'$(AssemblyOriginatorKeyFile)'==''" />
   <Copy SourceFiles="$(TargetPath)" DestinationFolder ="$(IntermediateOutputPath)"/>
   <Copy SourceFiles="$(PdbFile)" DestinationFolder ="$(IntermediateOutputPath)"/>
 </Target>
 <Target Name="PeVerify" Condition="'$(_AssemblyTimestampBeforeCompile)'!='$(_AssemblyTimestampAfterCompile)'">
   <GetFrameworkSdkPath>
     <Output TaskParameter="Path" PropertyName="SdkPath" />
   </GetFrameworkSdkPath>
   <Exec WorkingDirectory="$(SdkPath)bin" Command="peverify.exe /nologo &quot;$(TargetPath)&quot;" />
 </Target>
 <PropertyGroup>
   <PdbFile>$(OutputPath)\$(AssemblyName).pdb</PdbFile>
   <RunPostBuildEvent>OnOutputUpdated</RunPostBuildEvent>
   <PrepareForRunDependsOn>
     $(PrepareForRunDependsOn);
     EnhanceAssembly;
     PeVerify
   </PrepareForRunDependsOn>
 </PropertyGroup>
</Project>

This file needs to be imported at the end of your project file. So you’ll want to open your project file using a text editor not Visual Studio. Remember that project files are really just XML so we can go in and made changes to their source code. We need to do this if we want to get our targets file included.

At the bottom of the file you should see XML that looks roughly like this.

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" 
  xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Pretend this is all of the rest of the project file -->
  <ProjectExtensions>
    <VisualStudio>
      <UserProperties OpenAccess_ConfigFile="App.config" 
          OpenAccess_ConnectionId="SilverlightAnalyticsExpressConnection" 
          OpenAccess_Enhancing="true" 
          OpenAccess_UpdateDatabase="false" 
          OpenAccess_EnhancementOutputLevel="1" 
          OpenAccess_UseMSBuild="True" />
    </VisualStudio>
  </ProjectExtensions>
</Project>

 

All we have to do is add in one line and change it to this using a relative path to where we placed the targets file. I admit I didn’t really know the best place to put it so I put it with the Telerik class libraries for my project.

<Import Project="..\..\lib\Telerik\enhance.targets" />
<ProjectExtensions>
  <VisualStudio>
    <UserProperties OpenAccess_ConfigFile="App.config" 
        OpenAccess_ConnectionId="SilverlightAnalyticsExpressConnection" 
        OpenAccess_Enhancing="true" 
        OpenAccess_UpdateDatabase="false" 
        OpenAccess_EnhancementOutputLevel="1" 
        OpenAccess_UseMSBuild="True" />
  </VisualStudio>
</ProjectExtensions>

And now we should be able to commit our changes and everything should work correctly on the build server. Cross your fingers. Make sure that all of your relative paths are correct. If you have any questions just post a comment below, on the Telerik forums, or both.

How to Change or Remove the No Data Series Message in a RadChart for Silverlight

I’ve been working with the charts from Telerik’s RadControls for Silverlight. I am of course not blocking the UI when I make my requests to get my data for the charts. Since I am doing this late-loading of the data into the charts it causes them to initially show a message, “No Data Series.” This message is not a bad default message since it provides adequate information about why the chart is not displaying data.

NoDataSeries

However, since my charts will always start with no data, that message is quite silly. Luckily, changing or removing that message is easy. There is a convenient property on the ChartArea object called NoDataString, and in my case I set that as an empty string and I receive this nice blank chart now. I could potentially change the message to something else.

BlankChart

Snippet of Relevant Code

<UserControl x:Class="MyProject.UI.Charts.MyChart"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:telerikChart=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Charting" 
    xmlns:telerikCharting=
"clr-namespace:Telerik.Windows.Controls.Charting;assembly=Telerik.Windows.Controls.Charting" 
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot">
        <telerikChart:RadChart x:Name="Chart1" 
           UseDefaultLayout="False">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="*" />
                </Grid.RowDefinitions>
 
                <telerikCharting:ChartTitle Content="My Chart" 
                  HorizontalAlignment="Center"/>
                <telerikCharting:ChartArea x:Name="ChartArea1" 
                  Grid.Row="1" NoDataString="" />
            </Grid>
        </telerikChart:RadChart>
    </Grid>
</UserControl>

Create a Custom Layout in a RadChart for Silverlight

The default layout for the RadChart for Silverlight is a very common one. It has a title at the top, a legend on the right, and an area in the middle reserved for the chart. It looks roughly like this.

DefaultLayout 

If you started by using the default layout for the chart then you probably used these properties in some way.

Chart1.DefaultView.ChartArea
Chart1.DefaultView.ChartLegend
Chart1.DefaultView.ChartTitle

When you switch you’ll be adding in your own custom content inside of the RadChart tag in the XAML. You can only have one piece of content inside of the RadChart tag, so make sure you put in something like a Grid to do the layout. In this case I am removing the Legend and am going to just have the Title and Area in the chart, so I add this XAML for my chart. With this in place I am now able to see a spiffy new chart.

UsingDefaultLayout

One more thing to do here. I’ve got my new layout in there but the chart is still using the old layout. Notice that the space for the chart and the legend still exist. I have to tell the chart that I don’t want it using the default layout anymore. There is an all-too-obvious name for the Boolean property to disable the default layout. Just set UseDefaultLayout to False in the XAML and you’ll be using the correct layout.

CustomLayout

XAML for Custom RadChart Layout

<telerikChart:RadChart x:Name="Chart1" 
        UseDefaultLayout="False">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
 
        <telerikCharting:ChartTitle
            Content="Number of Hamburgers Eaten" 
            HorizontalAlignment="Center"/>
        <telerikCharting:ChartArea 
            x:Name="ChartArea1" Grid.Row="1" 
            NoDataString="" />
    </Grid>
</telerikChart:RadChart>