« Why I Love Working with the Betas... | Main | Downloadable implementation plans for PerformancePoint server 2007 »

Migrating Comparis Silverlight Challenge App to Silverlight 2.0 Beta 2

With the Silverlight 2.0 Beta 2 now available, I finally decided to take the plunge. So far, I had been focusing on WPF, but knowing that Silverlight is the next big wave for RIA development, I could not stay away longer.

A few fellow colleagues have been working with Silverlight 2.0 Beta 1 and I heard from them that Beta 2 has been a bit of a challenge considering the various breaking changes from Beta 1 to Beta 2. Fortunately these were mostly functional/API issues with Silverlight controls and not like the interesting issue faced by my colleague Kishore with his attempt to work with .net 3.5 SP1 beta. Since I was new, I started with the Comparis Silverlight Challenge Application and the HOL created by Swiss MSDN Team.

The lab proved to be pretty useful both in terms of quickly knowing the way around Silvelight, Expression (which wasn't much different from what I was used to doing in WPF) and also understanding the issues when moving from Beta 1 to Beta 2. Here I document the various things I did to get the lab working. If you want to follow along, you can download the Lab document and code from the site mentioned above and start on.

To get the existing application to compile and run as is, on my VS 2008 RTM, .NET framework 3.5 (not SP1 beta as yet) Silverlight 2.0 Beta 2 and Expression Blend 2.5 June Preview, I had to do the following.

1. When I opened the solution in VS 2008, It prompted me for upgrade, which I readly accepted and did.

2. Changed the ComparisSilverlightChallengeTestPage.html page to point to Silverlight version Beta 2. See bold faced content below

 <object data="data:application/x-silverlight," type="application/x-silverlight-2-b2" width="100%" height="100%">

3. Changed the ComparisSilverlightChallengeTestPage.aspx page by removing the Version="2.0" and replacing it with MinimumVersion="2.0.30523".

4. Next comes setting the database. Since I didn’t have SQL Express, I had to attach the ComparisCarfinder.mdf to my local SQL 2005 Instance. While attaching, it took the name of the database as the full file path ("C:\Silverlight-HOL\Silverlight Challenge Solution Kit CSharp\ComparisSilverlightChallenge_Web\App_Data\ComparisCarfinder.mdf"). I thus had to rename this to ComparisCarfinder. After this I added the BUILTIN\Administrators as dbowner to this database. This is not a good security setting, but to keep things simple for now, this is what I did.

Due to these changes, the DBML (LINQ To SQL file) had to undergo some changes since my data was no longer coming from the SQL Express edition. I had to modify the connection string that was currently being used. For this I right clicked and opened ComparisCarfinderDB.dbml with XML editor. The connection string is available right toward the top and I changed this from

<Connection Mode="ConnectionString" ConnectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\ComparisCarfinder.mdf;Integrated Security=True;User Instance=True" Provider="System.Data.SqlClient" />

to

<Connection Mode="WebSettings" ConnectionString="Data Source=.;Initial Catalog=ComparisCarfinder;Integrated Security=True" SettingsObjectName="System.Configuration.ConfigurationManager.ConnectionStrings" SettingsPropertyName="ComparisCarfinderConnectionString" Provider="System.Data.SqlClient" />

The ComparisCarfinderDB.designer.cs also had to be updated. The current

    public ComparisCarfinderDBDataContext() :

        base("Data Source=.\\SQLEXPRESS;AttachDbFilename=|DataDirectory|\\ComparisCarfinder.mdf;"+

                        "Integrated Security=True;User Instance=True", mappingSource)

    {

        OnCreated();

    }

 was replaced with

    public ComparisCarfinderDBDataContext() :

        base(global::System.Configuration.ConfigurationManager.ConnectionStrings["ComparisCarfinderConnectionString"].ConnectionString, mappingSource)

    {

        OnCreated();

    }

In the Web.config file just before the <appSettings/> tag inside of <configuration> tag, add the following new section

    <connectionStrings>

      <add name="ComparisCarfinderConnectionString" connectionString="Data Source=.;Initial Catalog=ComparisCarfinder;Integrated Security=True"

            providerName="System.Data.SqlClient" />

    </connectionStrings>

Note that doing all these changes manually can be a bit difficult and prone to error. Another easier way is to remove the existing ComparisCarfinderDB.dbml and related files from the Solution and add a new LINQ To SQL file and give it the same name. However with only this, things will not work and you will most likely get a 404 runtime error. This happens since in the existing ComparisCarfinderDB.designer.cs file, the Tables are additionally also tagged as DataContracts which goes missing in the newly generated files. This causes run time exception since the service is no longer aware of how to serialize the data. You can in such a case, replace the newer designer.cs file with the older one, but don’t forget to reset the connection string part as shown above.


With these changes in place, I was able to successfully build and execute the application and see the site as shown on page 9 of the HOL. So far so good and now beguns the actual fun of working with Silverlight 2.0.

The HOL mentioned about a security warning that you might get when initially opening the solution in VS. I didn’t get one there, but while working with Step 1, when I tried to open Page.xaml in Expression Blend 2.5 June Preview, I did get the warning. Post this working with HOL was fairly simple, till I hit Step 7.

HOL Step 7
This step talks about using WatermarkTextBox control. With Silverlight 2.0 Beta 2, this control is no longer available. I can't understand why? Has anyone come across any explanation for why this happened? Anyway, I now had an option of using TextBox control or build a watermark type TextBox on my own. Since I am still learning Silverlight I didn't want to get into writing custom controls, do I reverted to regular TextBox control.

Post this I was faced with another issue. For some reason, Expression Blend didn’t show DataGrid in the asset library and I tried to add it from VS I would get a FOMATETC error. Forums are saving grace and I found a similar query here. Following the suggestion theer I removed and added all the System.* references again and things started to work.

Surprisingly, after adding the DataGrid, both Expression and VS became slow and for some time the changes in one would not even reflect in another. So I had to close both Expression and VS and reopen and continue.

HOL Step 8
While adding the code, I modified the code snippet 8c as below and the DataGrid didn’t crash. I assume that the issue has been fixed in Beta 2.

        // Async event handler: Web service call

        void client_GetExpandedAdsCompleted(object sender, GetExpandedAdsCompletedEventArgs e)

        {

            myAds = e.Result.ToList();

            DataGridCars.ItemsSource = myAds;

 

            if (e.Result.Count() == 0)

            {

                HtmlPage.Window.Alert("No cars matching your qurey where found.");

            }

        }

HOL Step 10
While setting the event handler for the ValueChanged event, I got an error that the method name is invalid. It took me a moment to realize the extra blank space before the “_” in the HOL document and coping and pasting hence was causing the error.

After adding all the code as in this step, I realized that the TextKilometers was changing correctly, but the ListBoxImages wasn’t showing the right set of images. Debugging helped me realize that the MouseLeftButtonUp event from Slider wasn’t firing. Interestingly, this seems to be the feature of Beta 2 slider. See here. There is a discussion about writing custom slider or extending the existing one, but since I wanted to keep things simple for now, I did a dirty little trick as below

        private void SliderKilometers_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)

        {

            if (TextKilometers != null)

            {

                TextKilometers.Text = ((int)e.NewValue).ToString();

            }

         SliderKilometers_Click(sender, null);

        }

This got the ListBoxImages working, but needless to say that it killed the UI. Since the filtering of data is happening on the same thread, the slider becomes unresponsive till the time the filter from the data is done. Another option will be to run this in background thread. That I will try later, once I get everything else working.

HOL Step 11
The Event handler names again have extra spaces so I had to be careful when copying and pasting these names. I guess a better option will be to type names directly or type the event name in VS in XAML view and let VS propose the default name for the event handler.

I thought of further extending the functionality here by getting the selected items into view. So I modified the code as below 

 

        private void DataGridCars_SelectionChanged(object sender, EventArgs e)

        {

            ListBoxImages.SelectedItem = DataGridCars.SelectedItem;

         ListBoxImages.ScrollIntoView(ListBoxImages.SelectedItem);

        }

 

        private void ListBoxImages_SelectionChanged(object sender, EventArgs e)

        {

            DataGridCars.SelectedItem = ListBoxImages.SelectedItem;

         DataGridCars.ScrollIntoView(DataGridCars.SelectedItem, null);

        }

I have set the DataGridColumn to null since I don’t care which columns are displayed for now.
Interestingly the code for DataGrid works, but ListBox didn’t get the selected item into view. I tried setting the StackPanel in ListBox’s ItemPanel template to Vertical and the scroll into view functionality worked perfectly. So this seems to be an issue when the ListBox is set as horizontal. I guess the ListBox doesn’t realize that the content inside it is set horizontally. It may hence be better if ListBox itself supports a Orientation property rather than I having to change it by putting my own StackPanel.

HOL Step 12
When adding the ListBoxDetails control, I had to set the Height to Auto and VerticalAlignment to Stretch to get the required size of the control. I did a few further changes like, made the Rectangle a bit more opaque by using an Alpha value of 80%. So the Fill value was set to - #CCB7B7B7. In the animation, I reduced the Opacity of DataGridCars and ListBoxImages to 50%. So the final animation storyboard looked like

        <Storyboard x:Name="GridDetails_Animate">

            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="GridDetails" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">

                <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0.1"/>

                <SplineDoubleKeyFrame KeyTime="00:00:01" Value="1"/>

            </DoubleAnimationUsingKeyFrames>

            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="GridDetails" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)">

                <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0.1"/>

                <SplineDoubleKeyFrame KeyTime="00:00:01" Value="1"/>

            </DoubleAnimationUsingKeyFrames>

            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="DataGridCars" Storyboard.TargetProperty="(UIElement.Opacity)">

                <SplineDoubleKeyFrame KeyTime="00:00:01" Value="0.5"/>

            </DoubleAnimationUsingKeyFrames>

            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ListBoxImages" Storyboard.TargetProperty="(UIElement.Opacity)">

                <SplineDoubleKeyFrame KeyTime="00:00:01" Value="0.5"/>

            </DoubleAnimationUsingKeyFrames>

        </Storyboard>

And in the ButtonDetailsClose_Click event, I then reset the Opacity values back to 100% as below 

        private void ButtonDetailsClose_Click(object sender, RoutedEventArgs e)

        {

            GridDetails.Visibility = Visibility.Collapsed;

            DataGridCars.Opacity = 1;

            ListBoxImages.Opacity = 1;

        }

This worked fine, though for some reason, the opacity effect on DataGrid’s header row wasn’t that visible. The header row still appeared to be fully opaque.

HOL Step 14
The customization of DataGrid template as such was fine, but there is one difference that I had do. The namespace prefix System_Windows_Controls was used already when I had added DataPicker control and this pointed to clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Extended. The DataGrid isn’t in this namespace and hence I had to create my own namespace prefix as xmlns:ctrls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data". Hence when I modified the DataGrid to use custom template I had to replace System_Windows_Controls with ctrls everywhere. Post this change, things worked, but surprisingly, Expression started to give an error and would no longer load the designer. For the code where the column width is set as “<ctrls:DataGridTemplateColumn Width="150" Header="CarImage">”, I got the following error in Expression
“The TypeConverter for “DataGridLength” does not support converting from a string”
Saw a similar error posted on the forum here and I also went ahead and voted for the same here

HOL Step 15
Finally reached the end of the HOL and the last part of templating the Slider control also had some issues. The templating logic as such worked fine and I could see the modified Slider at run time, but it neither reacts to clicks nor is it initialized to the max value as before. The Width also though set to 200, doesn’t visually look correct (see figure below). Is this related to my not having set any visual states as per the new VSM feature in Beta 2? I need to experiment further. If you have any idea on this, do write back.

SliderTemplate.jpg

With this, the experimentation with Silverlight 2.0 Beta 2 and migrating the Camparis Carfinder application was over.

Comments

Great post.

I get an error in the application when trying to run this code:
public System.Collections.ObjectModel.ObservableCollection EndGetCarMakes(System.IAsyncResult result) {
object[] _args = new object[0];
System.Collections.ObjectModel.ObservableCollection _result = ((System.Collections.ObjectModel.ObservableCollection)(base.EndInvoke("GetCarMakes", _args, result)));
return _result;
}

The error is an unexpected response:(404) Not Found.

Do you have any idea what this is?

Kjell, do you have any additional information? When you get exception, if debugging in VS, can you check and see if there is any inner exception with more details?

Is your database setup properly and you able to access it?

I found out that if I direct my sql query to ExpandedAds it works fine. So I tried to remove everything except for the CarMake table and managed to get the right result for the dropdownlist then. I then added the CarModel table with the constraint and the the result for the dropdownlist failed. Any idea why??

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)

Please key in the two words you see in the box to validate your identity as an authentic user and reduce spam.

Subscribe to this blog's feed

Follow us on

Blogger Profiles

Infosys on Twitter