Easy asynchronous data loading with ASP.NET / AJAX

I will freely admit – there is a hole in my skill set as a developer. As I’ve mentioned earlier in this blog, I was trained as a “classic” developer, writing console applications in C++, and while I’ve done plenty of web-based work, HTML5, JavaScript, and AJAX are not my friends. I like ASP.NET because the majority of the “guts” are done in C#, which is my bread and butter. However, doing everything server side is not very performant, nor is it a great user experience. This is where AJAX comes into play.

Luckily, ASP.NET provides some tools that shields us javascriptaphobic types from these things. I presume you already know about the UpdatePanel. Using these, you can do AJAX-style updates to your page, without a full refresh. They work like this:

ASP:


<asp:ScriptManager ID="PageScriptManager" runat="server" />

<asp:Button ID="MyButton" Text="Click Me" runat="server" onClick="MyButton_Clicked" />

<asp:UpdatePanel ID="MyUpdatePanel" runat="server">
   <Triggers>
      <asp:AsyncPostBackTrigger ControlID="MyButton" EventName="Click" />
   </Triggers>

   <ContentTemplate>
      <asp:Label ID="MyLabel" Text="OldText" runat="server" />
   </ContentTemplate>
</asp:UpdatePanel>

C# CodeBehind:


private void MyButton_Clicked(object sender, EventArgs e)
{
   MyLabel.Text = "NewText!";
}

Without the UpdatePanel, clicking the button requires a full post back, causing an unslightly flicker as the page reloads. With the UpdatePanel, the text box will seamlessly change from “OldText” to “NewText!” in AJAX style. Very nice, very easy.

That’s all well and good – but what if your page is heavily data driven? Techniques like caching certainly help reduce page loading time, but on the first page view, there is an inevitable waiting period while the data loads, and even that cached data takes some time to render. If you put that in your PageLoad event, the browser will sit on a white screen waiting for the query to complete – an experience that users just don’t like. I ran into this with deekfit.com – the more you used DeekFit, the longer and longer pages loads were taking as your data history grew. Definitely not the experience I wanted. Still hesitent to really dive into the HTML5 / JavaScript head first, I figured there must be a way I can get ASP.NET to do this for me…and after a little research, I discovered, there is!

The solution is actually quite simple – insert a timer to your UpdatePanel that fires a PostBack immediately after the page is loaded, removes itself from the page, and does your loading there. Coupled with the ASP progress indicator and an animated gif (I used http://ajaxload.info/ to create one), you get a much nicer experience. Like so:

ASP:


<asp:UpdatePanel ID="MyUpdatePanel" runat="server">
   <ContentTemplate>
      <asp:Panel ID="TimerPanel" runat="server">
         <asp:Timer ID="PageLoadTimer" runat="server" interval="1" OnTick="timerTick" />
      </asp:Panel>

      <asp:UpdateProgress id="PageProgressIndicator" runat="server">
         <ProgressTemplate>
            <p><img src="ajax-loader.gif" /> Loading your data...</p>
         </ProgressTemplate>
      </asp:UpdateProgress>

      <asp:DataGrid ID="MyDataGrid" runat="server" />
   </ContentTemplate>
</asp:UpdatePanel>

C# CodeBehind:

private void timerTick(object sender,   EventArgs e)
{
   TimerPanel.Controls.Remove(TimerPanel.FindControl("PageLoadTimer"));

   MyDataGrid.DataSource = LongRunningLoadingOperation();
   MyDataGrid.DataBind();
}

That does it! Your page will load immediately, with the progress indicator displayed until the loading operation completes. Make sure the timer is inside the UpdatePanel, otherwise the method will fail to remove it from the page – I learned this one the hard way!

There you go – very easy, clean, does the job, no JavaScript required. Sure…some day we’ll all have to learn HTML5, CSS3, and JavaScript….but if you’re an ASP.NET holdout, you don’t have to just yet 🙂

Advertisement

Apparently, Jeff Bezos thinks DeekFit Gym is magical…

…or at least he would, if he used it!

When I first started designing DeekFit Gym, my goal was to make something unique. I had used lots of gym tracking apps on Android, Windows Mobile, and the internet. Nothing really did what I wanted as a serious weight lifter. So, rather than petition those other developers to change, I did what any entrepreneurial young developer with too much free time would do….I made my own.

One thing that was very important to me was longevity of my workout data. I’ve come to accept the fact that I buy phones, and switch platforms, frequently. Even when I keep a phone for a period of time, I’ll probably flash ROMs here and there too. I might even be using multiple devices at once. So one requirement I had was proper multi-device syncing. I needed my data stored in a database on the web, accessible from a website, as well as any number of phones/devices. Just storing it directly on the web (like you do with, say, Twitter) wasn’t good enough. Not all gyms have cell service – and some devices are WiFi only (think iPod Touch). So you need to be able to access – and edit – your data from anywhere, and keep it all up to date.

With this in mind, I built DeekFit Gym Pro’s sync system. Your data is stored on your device(s), and either automatically or manually synced to the server. You can also add new data from deekfit.com. Wherever you make new entries, they’ll be synced to all the others, with a concurrency system that handles collisions. You can access your data anywhere, from any number of devices, and it all keeps in sync with the others.

Now, back to Bezos.

In response to an earnings call, Bezos was talking about some of Amazon’s high-technology ventures – it’s a story we’ve heard before, (rightfully) making the case for Amazon being a technology company, not just a website/retail store. In this note, he talks about the Kindle, Amazon’s e-reader, and the wireless system for keeping your books up to date – Whispersync:

One example is Whispersync, our Kindle service designed to ensure that everywhere you go, no matter what devices you have with you, you can access your reading library and all of your highlights, notes, and bookmarks, all in sync across your Kindle devices and mobile apps. The technical challenge is making this a reality for millions of Kindle owners, with hundreds of millions of books, and hundreds of device types, living in over 100 countries around the world—at 24×7 reliability. At the heart of Whispersync is an eventually consistent replicated data store, with application defined conflict resolution that must and can deal with device isolation lasting weeks or longer. As a Kindle customer, of course, we hide all this technology from you. So when you open your Kindle, it’s in sync and on the right page. To paraphrase Arthur C. Clarke, like any sufficiently advanced technology, it’s indistinguishable from magic.

(full text: http://www.businessinsider.com/why-i-jeff-bezos-keep-spending-billions-on-amazon-rd-2011-4#ixzz1Kkxqrjfd)

My first thought was “Wow, magical huh, that sure is Jobsian of you”. Then, I thought, “hmm…that sounds awfully familiar!” At its core, DeekFit Gym does the same thing as Whispersync – dare I say, with a slightly more complex data set. Interesting. I guess Bezos thinks I’m magical 🙂

Now, before Amazon supporters get on my case about it – I realize Whispersync deals with a scale DeekFit Gym does not, and for numerous reasons is likely a more sophisticated solution. However, Whispersync is written by a team of full-time engineers, and I make DeekFit Gym in my spare time, so I’m still ok with the comparison. Besides, I would understand Amazon’s scale as well as anyone – I did work for their Ordering and Payments departments.

Styling Silverlight Charts with automatic colors

The Silverlight Toolkit provides some pretty nice data visualization tools. There are a plethora of different chart types available, and for the most part, they’re pretty easy to use, and very customizable to your needs. One of the nice things it does to make your life easier, each series you add to the chart gets automatically color-coded with attractive gradients. Its a nice touch.

One of the frustrating things about the charting tools is that if you style your series, that automatic color disappears – everything is orange. You can manually set colors – but its extra effort, and you need to create a new style for every color. Irritating, to say the least, especially if you dynamically add your series. I had a series that I wanted to style – specifically, I wanted to add a custom ToolTip, like I describe here. And the number of series is dynamic, which didn’t help things.

The charting toolkit is open source, so I went poking around, trying to find how and where they set these automatic colors. I never did find it – but I did find a cache of styles, that looked to be exactly the automatic colors that get applied. See for yourself – the code is installed by default to C:\Program Files (x86)\Microsoft SDKs\Silverlight\v4.0\Toolkit\Apr10\Source\Source code.zip. Unzip that, then open up Controls.DataVisualization.Toolkit\Themes\generic.xaml. About line 270, you’ll see this….

<Style TargetType="charting:Chart">
    <Setter Property="BorderBrush" Value="Black" />
    <Setter Property="BorderThickness" Value="1" />
    <Setter Property="IsTabStop" Value="False" />
    <Setter Property="Padding" Value="10" />
    <Setter Property="Palette">
        <Setter.Value>
            <datavis:ResourceDictionaryCollection>
                <!-- Blue -->
                <ResourceDictionary>
                    <RadialGradientBrush x:Key="Background" GradientOrigin="-0.1,-0.1" Center="0.075,0.015" RadiusX="1.05" RadiusY="0.9">
                        <GradientStop Color="#FFB9D6F7" />
                        <GradientStop Color="#FF284B70" Offset="1" />
                    </RadialGradientBrush>
                    <Style x:Key="DataPointStyle" TargetType="Control">
                     .....

Jackpot! It seems what I need is under Chart.Palatte. Now its just a matter of applying…which, of course, was not as easy as it seems.

First, I tried making a style, adding the background color to it, and applying it to my series. Like this:

XAML:

<Style x:Name="MyStyle" TargetType="charting:LineDataPoint">
  <Setter Property="Template" Value="{StaticResource MyTemplate}" />
</Style>

Code:

//Create a new setter using the chart palatte and apply it to my existing style
Setter bgSetter = new Setter(LineDataPoint.BackgroundProperty, MyChart.Palatte[0]["Background"]);
Style myStyle = Resources["MyStyle"] as Style;
myStyle.Setters.Add(bgSetter);

//Create a new series using that style and add it to the chart
LineSeries newSeries = new LineSeries();
newSeries.DataPointStyle = myStyle;

MyChart.Series.Add(newSeries);

Looks good right? Compiles ok….and you get a CATASTROPHIC FAILURE COM Exception. Yikes! That’s not what you want to hear. Turns out, there’s a bug deep in the bowels of Silverlight that doesn’t like when you change a DependencyProperty after its already been set. So, you have to create the new style from scratch.

//Clear the previous series
MyChart.Series.Clear();
int palatteIndex = 0;

foreach (Object myData in myDataList)
{
  //Create a new BackgroundProperty setter, using the current index of the chart palatte
  Setter bgSetter = new Setter(LineDataPoint.BackgroundProperty, MyChart.Palatte[palatteIndex]["Background"]);
  //create other setters

  //Create the new style
  Style newStyle = new Style(typeof(LineDataPoint));
  newStyle.Setters.Add(bgSetter);
  //add other setters

  //Create your new series and apply the new style
  LineSeries newSeries = new LineSeries();
  newSeries.DataPointStyle = newStyle;
  //create bindings and add data to series

  MyChart.Series.Add(newSeries);

  //Increment the palatte
  //If we've reached the total number of presets, reset back to the beginning
  //If you'd prefer, you could use some sort of random number generator here instead.
  ++palatteIndex;
  if(palatteIndex > MyChart.Palatte.Count)
  {
    palatteIndex = 0;
  }
}

And there you have it! You can style your series to your heart’s content, but still maintain the nice, attractive automatic colors for the series! Note that while I used LineSeries, this will work for any of them – ColumnSeries, BarSeries, PieSeries, you name it.

Using a custom ToolTip in Silverlight charting

This has been done plenty of places before – in itself, it isn’t too complicated. However, it provides the set up for my next blog post, so I figured I would write this out separately.

I’m using the charting tools from the Silverlight toolkit. In general, they are solid – easy to use and attractive. However, its not always so simple if you need to customize things beyond a simple binding expression. In this post, I will explain how to create a custom ToolTip for data in a LineSeries.

By default, Silverlight displays the numeric value of the bound data as the ToolTip. Like this:

Silverlight Charting - Default ToolTip

This is nice – but not what we want. We want to expand this to display more detailed information. Within Silverlight, setting a ToolTip is usually pretty easy:

XAML:

<TextBlock Name="MyTextBlock" Text="SomeText">
  <ToolTipService.SetToolTip>
    <ToolTip>Hello From My ToolTip!</ToolTip>
  </ToolTipService.SetToolTip>
</TextBlock>

Code:

TextBlock text = new TextBlock()
{
  Name = "MyTextBlock",
  Text = "SomeText",
};
ToolTipService.SetToolTip(text, "Hello From My ToolTip!");

Unfortunately, it isn’t quite as easy for charting. The ToolTip is buried in the ControlTemplate of the DataPointStyle of the LineSeries. So, you need to create a new style with that template, and update the ToolTip there. You can get the full default style from the toolkit source code or from Expression Blend. Below is the default style for the ControlTemplate, with the altered ToolTip:

<ControlTemplate x:Key="ModifiedToolTipTemplate" TargetType="charting:LineDataPoint">
    <Grid x:Name="Root" Opacity="0">
        <ToolTipService.ToolTip>
            <ContentControl Content="{Binding Converter={StaticResource MyDataConverter}}"/>
        </ToolTipService.ToolTip>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="CommonStates">
                <VisualStateGroup.Transitions>
                    <VisualTransition GeneratedDuration="0:0:0.1"/>
                </VisualStateGroup.Transitions>
                <VisualState x:Name="Normal"/>
                <VisualState x:Name="MouseOver">
                    <Storyboard>
                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="MouseOverHighlight">
                            <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0.24"/>
                        </DoubleAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>
            </VisualStateGroup>
            <VisualStateGroup x:Name="SelectionStates">
                <VisualStateGroup.Transitions>
                    <VisualTransition GeneratedDuration="0:0:0.1"/>
                </VisualStateGroup.Transitions>
                <VisualState x:Name="Unselected"/>
                <VisualState x:Name="Selected">
                    <Storyboard>
                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="SelectionHighlight">
                            <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0.18"/>
                        </DoubleAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>
            </VisualStateGroup>
            <VisualStateGroup x:Name="RevealStates">
                <VisualStateGroup.Transitions>
                    <VisualTransition GeneratedDuration="0:0:0.5"/>
                </VisualStateGroup.Transitions>
                <VisualState x:Name="Shown">
                    <Storyboard>
                        <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="Root"/>
                    </Storyboard>
                </VisualState>
                <VisualState x:Name="Hidden">
                    <Storyboard>
                        <DoubleAnimation Duration="0" To="0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="Root"/>
                    </Storyboard>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        <Ellipse Fill="{TemplateBinding Background}" Stroke="{TemplateBinding BorderBrush}"/>
        <Ellipse RenderTransformOrigin="0.661,0.321">
            <Ellipse.Fill>
                <RadialGradientBrush GradientOrigin="0.681,0.308">
                    <GradientStop Color="#00FFFFFF"/>
                    <GradientStop Color="#FF3D3A3A" Offset="1"/>
                </RadialGradientBrush>
            </Ellipse.Fill>
        </Ellipse>
        <Ellipse x:Name="SelectionHighlight" Fill="Red" Opacity="0"/>
        <Ellipse x:Name="MouseOverHighlight" Fill="White" Opacity="0"/>
    </Grid>
</ControlTemplate>

Then, apply this template to a LineDataPoint style:

<Style x:Key="ModifiedDataPointStyle" TargetType="charting:LineDataPoint">
    <Setter Property="Background" Value="Blue" />
    <Setter Property="Width" Value="8" />
    <Setter Property="Height" Value="8" />
    <Setter Property="Template" Value="{StaticResource ModifiedToolTipTemplate}" />
</Style>

Finally, apply that to your LineSeries:

<charting:LineSeries Name="MySeries" DataPointStyle="{StaticResource ModifiedDataPointStyle}" />

…and that’s it! You can put whatever you want in the custom ToolTip – I use a Silverlight data converter to take the bound value and create a custom TextBlock. The end result looks like this:

Custom ToolTip

Setting image sizes in Actionscript

So I’m not an Actionscript developer. My background is in C/C++/C#, with some Java thrown in for good measure. My UI experience is mainly Silverlight. So, when picking up Actionscript to write an app for the Blackberry Playbook, I’ve learned a few interesting…quirks about the language.

One that caused me to bang my head against my desk for far too long was setting image sizes. Did you know that if you set an image size or scale value before its rendered, that Adobe Air will ignore it, and go with the image file’s size? I sure didn’t.

Considering my application dynamically adds lots of images that are dynamically sized based on various factors, this was a very frustrating issue. For starters – its completely unintuitive. If it weren’t for some random forum post that it took me hours to find (thanks, poster, if I remembered where I read this I’d cite you), I’d never have figured it out. Secondly, with the dynamic nature of applications, its hard to keep track of what the size should be. Here is what I did to solve the issue. I’m not saying its eloquent. I’m not saying its ideal. But, it worked, which is really all I cared about.

1) Create an a class that extends Image, and lets you store the width/height as properties:


import qnx.ui.display.Image;

public class ExImage extends Image
{
public var storedWidth:int;
public var storedHeight:int;

public function ExImage()
{
      super();
   }
}

2) Create the ExImage object, set the width/height to the custom fields, and capture the onLoad event, and set the Image’s size there

private function CreateImage(width:int, height:int, path:String) : Image
{
   var myImage:ExImage = new ExImage();

   myImage.addEventListener(Event.COMPLETE, onImageLoad);
   myImage.storedWidth = width;
   myImage.storedHeight = height;
   myImage.setImage(path);

   return myYmage;
}

private function onImageLoad(e:Event) : void
{
   var myImage:ExImage = (e.target as ExImage);
   myImage.setSize(myImage.storedWidth, myImage.storedHeight);
   var container:Container = myImage.parent as Container;

   if(container != null)
   {
      container.layout();
   }
}

…and that does the trick. Its ugly, it doesn’t make sense why they designed it this way…but there ya go.

My dev blog

I write a lot of code. I do it at my job by day, and I do it for side projects at night. I work on a wide variety of projects, for different platforms, in different programming languages.

My favorite way to learn how to do new things is to search the internet and find someone else who has done it before. Why reinvent the wheel, right? It never occurred to me until now that I should probably be posting my own stuff too – hopefully other developers can get something out of it, plus its good reference for me, too.

So that’s what this blog will be. Random musings and things I’ve learned about programming. Enjoy!