Archive of entries posted on November 2008

WPF and Cursor

I was trying to figure out today the WPF’s equivalent to old WinForms functionality that allows a developer to change cursor shape.  For example, if you have a control that may take a second or two to load, you want to change cursor shape to hourglass (WaitCursor), then change it back once control has been loaded.

Turns out, this is just as easy in WPF, just not as intuitive.

Mouse.OverrideCursor = Cursors.Wait;

To change it back to default:

Mouse.OverrideCursor = null;

You will need to import a namespace:

using System.Windows.Input;

GGMUG Presentation on Silverlight Data Access

Today I did a presentation at the GGMUG (Gwinnett, GA Microsoft Users’ Group) meeting on Silverlight’s data access.  Download presentation and project here.

Integrating Reporting Services (SSRS) 2008 into Silverlight applications

Task of the day: create SSRS reports that can be shared between desktop (WPF) and Silverlight applications.

Silverlight does not have SSRS report viewer.  So what should we do?  The cleanest (and easiest) solution is to utilize ASP.NET capabilities.  ASP.NET does have web based controls for viewing SSRS reports.  So, we will create an aspx page in the web application that hosts Silverlight application.  We will set it up as follows:

<%@ Register Assembly="Microsoft.ReportViewer.WebForms, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
    Namespace="Microsoft.Reporting.WebForms" TagPrefix="rsweb" %>
<html xmlns="http://www.w3.org/1999/xhtml" style="height:100%;">
<head runat="server">
    <title>Report</title>
</head>
<body>
    <form id="form1" runat="server" style="height:100%" >
    <div style="height:100%" >
        <rsweb:ReportViewer ID="MainReportViewer" runat="server" 
            ProcessingMode="Remote"
            Width="95%"
            Height="700"
            ShowExportControls="True"
            ShowFindControls="True"
            ShowParameterPrompts="False"
            ShowPromptAreaButton="False"
            ShowRefreshButton="False"
            BackColor="White">
            <ServerReport DisplayName="MainReport" ReportServerUrl="" />
        </rsweb:ReportViewer>
    </div>
    </form>
</body>
</html>

Now the next step is to make sure this web page can parse query parameters and show appropriate report:

protected void Page_Load(object sender, EventArgs e)
        {
            MainReportViewer.ProcessingMode = ProcessingMode.Remote;
            MainReportViewer.ServerReport.ReportPath = "/SSRSReports/" + this.Request.QueryString["ReportName"];
            MainReportViewer.ServerReport.ReportServerUrl = new Uri("http://localhost:8080/ReportServer_SQL2008");
            List<ReportParameter> parameters = new List<ReportParameter>();
            List<string> values = new List<string>();
            values.Add("1");
            values.Add("2");
            values.Add("3");
            ReportParameter oneParamter = new ReportParameter("MyParameter", values.ToArray());
            parameters.Add(oneParamter);
            MainReportViewer.ServerReport.SetParameters(parameters.ToArray());
            MainReportViewer.ShowParameterPrompts = false;
            MainReportViewer.ServerReport.Refresh();
        }

Code above contains hard-coded report parameters.  But, as you can see those can be easily passed in as part of query string as well.

Now, code from Silverlight application:

private void Button_Click(object sender, RoutedEventArgs e)
    {

        Uri sourceUri = new Uri(HtmlPage.Document.DocumentUri, Application.Current.Host.Source.ToString().Substring(0, Application.Current.Host.Source.ToString().IndexOf("ClientBin") – 1) + "/ReportViewerForm.aspx?ReportName=DevelopmentPlanAction");

        if (true == HtmlPage.IsPopupWindowAllowed)
        {
            System.Text.StringBuilder codeToRun = new System.Text.StringBuilder();
            codeToRun.Append("window.open(");
            codeToRun.Append("\"");
            codeToRun.Append(sourceUri.ToString());
            codeToRun.Append("\",");
            codeToRun.Append("\"");
            codeToRun.Append("\",");
            codeToRun.Append("\"");
            codeToRun.Append("width=1000,height=900,scrollbars=yes,menubar=no,toolbar=no,resizable=yes");
            codeToRun.Append("\");");
            try
            {
                HtmlPage.Window.Eval(codeToRun.ToString());
            }
            catch
            {
                MessageBox.Show("You must enable popups to view reports.  Safari browser is not supported.", "Error", MessageBoxButton.OK);
            }
        }
        else
            MessageBox.Show("You must enable popups to view reports.  Safari browser is not supported.", "Error", MessageBoxButton.OK);
    }

This code simply creates a javascript that is using the same web URL, but adds the address to our report viewer web page we just created.  Ideally, we should have use  built-in Silverlight function HtmlPage.PopupWindow, but it does not honor options, always creating non-resizeable browser window.  So, I am resorting to javascript instead that does create a resizeable window, but without menu, etc…  The only sad part is that Siliverlight application does launch another window, but there is no way to workaround this issue.  Overall I am pretty happy with this solution.  What is also pretty cool is that the same SSRS report can be called from WPF application.  In that case we use WinForm SSRS viewer that we host inside WindowsFormsHost  control like so:

<winForms:WindowsFormsHost x:Name="ViwerHost" Grid.Row="1" Background="White">
            <reports:ReportViewer x:Name="ReportViewerControl">
            </reports:ReportViewer>
        </winForms:WindowsFormsHost>

The code to launch the report is pretty much identical to the one for ASP.NET

Cool, hah?

More on designer / developer collaboration

I encountered one more aspect of collaboration, and I would like to elaborate on.  This involves communication with the user.  Here is how the story goes.  A designed lays out the UI.  Proposed UI is discussed  with the user, who subsequently approves the design.  At this point the UI is given to developer to work on.  Well, developer finds that the UI is not workable within the constraints of the development environment.  For example, UI design results in highly inefficient communication between UI layer and business layer or database layer.  Or, the sample UI lists a handful of items on the screen, but database has hundreds, and the full list does not even fit on the screen and looks unreadable.  So, what is the solution you ask?  Involve a developer in review process prior to demonstrating UI to the user.  I think this procedure should minimize the changes to UI after the user’s sign-off.

WPF ListBox and SelectedItem’s BackColor

In our application we use black as background color everywhere and white or light gray as foreground color.  I was working on a ListBox that has very complicated item’s layout.  Essentially there are about 8 different controls contained within each item.  ListBox is simply used as a container to view the data, we really do not need to know which item is highlighted by the user.  Default background color for ListBox’s highlighted item is a system color, called HighlightBrushKey.  In my case (on my machine) it is blue.  Highlighted items looks UGLY with this color schema.  So, I wanted to simply remove the highlight altogether.  To do so, I defined a custom style as so

<UserControl.Resources>

    <Style x:Key="customListBoxItemStyle" TargetType="{x:Type ListBoxItem}">
        <Style.Resources>
            <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"
Color="Transparent"/>
        </Style.Resources>
    </Style>

</UserControl.Resources>

This style is replacing default system color with transparent color, essentially removing the highlights.  To use this style in the ListBox, I did the following:

<ListBox ItemsSource="{Binding Path=ClientProductGroups}" ScrollViewer.HorizontalScrollBarVisibility="Hidden" ItemContainerStyle="{StaticResource customListBoxItemStyle}">

Reusable WPF/Silverlight Animations

As I was working on fixing some animations that got broken due to splitting on one control into multiple controls, I came up with an idea of creating a single animation that could be apply to multiple controls.  In my case, the animation was two-fold.  As control came into view, it should fade into the view as well as "bounce".  Essentially, this animation utilizes scale transform and opacity transform.  Here is the new class I wrote.  The cool thing is that this class can be called with any UI element or control.As a matter of fact, the control does not even need to have transform group defined for it, as the class will create one on the fly to support desired animation.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Windows.Media.Animation;
using System.Windows;
using System.Windows.Media;

namespace App.Animation
{
    public static class InitialControlAnimation
    {
        public static void Play(UIElement controlToAnimate)
        {
            Storyboard story = new Storyboard();

            // opacity animation
            DoubleAnimationUsingKeyFrames opacityAnimation = new DoubleAnimationUsingKeyFrames();
            opacityAnimation.BeginTime = TimeSpan.FromMilliseconds(0);
            opacityAnimation.KeyFrames.Add(new SplineDoubleKeyFrame(0, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(0))));
            opacityAnimation.KeyFrames.Add(new SplineDoubleKeyFrame(0.8, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(200))));
            opacityAnimation.KeyFrames.Add(new SplineDoubleKeyFrame(1, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(300))));
            ((SplineDoubleKeyFrame)opacityAnimation.KeyFrames[2]).KeySpline = new KeySpline(0, 1, 1, 1);
            Storyboard.SetTarget(opacityAnimation, controlToAnimate);
            Storyboard.SetTargetProperty(opacityAnimation, new PropertyPath(UIElement.OpacityProperty));
            story.Children.Add(opacityAnimation);

            //stretch horizontally
            DoubleAnimationUsingKeyFrames scaleXAnimation = new DoubleAnimationUsingKeyFrames();
            scaleXAnimation.BeginTime = TimeSpan.FromMilliseconds(0);
            scaleXAnimation.KeyFrames.Add(new SplineDoubleKeyFrame(0.95, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(0))));
            scaleXAnimation.KeyFrames.Add(new SplineDoubleKeyFrame(1.08, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(200))));
            scaleXAnimation.KeyFrames.Add(new SplineDoubleKeyFrame(1, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(300))));
            Storyboard.SetTarget(scaleXAnimation, controlToAnimate);
            Storyboard.SetTargetProperty(scaleXAnimation, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"));
            story.Children.Add(scaleXAnimation);

            //stretch vertically
            DoubleAnimationUsingKeyFrames scaleYAnimation = new DoubleAnimationUsingKeyFrames();
            scaleYAnimation.BeginTime = TimeSpan.FromMilliseconds(0);
            scaleYAnimation.KeyFrames.Add(new SplineDoubleKeyFrame(0.95, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(0))));
            scaleYAnimation.KeyFrames.Add(new SplineDoubleKeyFrame(1.08, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(200))));
            scaleYAnimation.KeyFrames.Add(new SplineDoubleKeyFrame(1, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(300))));
            Storyboard.SetTarget(scaleYAnimation, controlToAnimate);
            Storyboard.SetTargetProperty(scaleYAnimation, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"));
            story.Children.Add(scaleYAnimation);

            if (!(controlToAnimate.RenderTransform is TransformGroup))
            {
                TransformGroup group = new TransformGroup();
                group.Children.Add(new ScaleTransform(1, 1, controlToAnimate.RenderSize.Height / 2, controlToAnimate.RenderSize.Width / 2));
                controlToAnimate.RenderTransform = group;
            }
            story.Begin();

        }
    }
}

CSLA .NET for Silverlight webcast on 11/13

Magenic is sponsoring a webcast about CSLA .NET for Silverlight on November 13. I will be doing this presentation.  I am very excited about this opportunity.  I think very highly of Silverlight/WPF technologies.  I have also being using CSLA for almost 2 years now on nearly daily basis.  As you may have noticed from other posts, I got an opportunity to work with Rocky Lhotka on CSLA for Silverlight for about three months.  I am hopeful that I can share my excitement and get people interested in CSLA for Silverlight.  I honestly believe that Silverlight can revolutionize business application development for the web, and CSLA can play a very big role in this process as the very first (to my knowledge) business framework for Silverlight.

WCF, basicHTTPBinding and compression

I am working on a distributes WPF application.  This application has rich client UI, while getting the data from a database server.  One complication is that this software will be installed at a several companies across the US.  All the installation have to communicate with central application server that will be hosted inside a data center.  As I was working on  a class that provides a list of customers (only about 3,500 rows), I decided to measure the traffic and performance.  I was rather unpleasantly surprised with the message size.  It was 7.5MB in Fiddler 2.  By the way, if you want to measure local traffic in Fiddler while using basicHTTPBinding with WCF, set your endpoint to 127.0.0.1. (yes, you need that trailing period) in order to trick Fiddler into thinking that this is external communication.  When I tried to switch to custom binding using binary messaging, the size only shrunk by 1 MB!  I was expecting much more than that…  Here is how I accomplished that:

<customBinding>
                <binding name="NetHttpBinding" receiveTimeout="00:10:00"
                        sendTimeout="00:10:00"
                        openTimeout="00:10:00">
                    <reliableSession />
                    <binaryMessageEncoding  maxReadPoolSize="2147483647" maxWritePoolSize="2147483647" maxSessionSize="2147483647">
                        <readerQuotas
                            maxBytesPerRead="2147483647"
                            maxArrayLength="2147483647"
                            maxStringContentLength="2147483647"
                            maxDepth="1024"/>
                    </binaryMessageEncoding>
                    <httpTransport maxReceivedMessageSize="2147483647" maxBufferPoolSize="2147483647" maxBufferSize="2147483647" />
                </binding>
            </customBinding>

So, what next?

I thought of compression next.  Guess what, WCF does not have a built in compression mechanisms.

So, I found a few posts of similar implementation (all years old) and fix bugs and extended them to do what I need.  I got the message size down to 400 KB (or so), which is more that I would like, but 90% compression is not something I am going to complain about.  I did not want to switch binding because of firewall issues.

Here is the mechanism that was used.  We extended the WCF behavior by defining a class that implements BehaviorExtensionElement.  Once you do that, you can reference it inside your configuration file as so:

<extensions>
            <behaviorExtensions>
                <add name="compression" type="Compression.CompressionBehaviorSection, Compression, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b99ba6caceb1e8ad"/>
            </behaviorExtensions>
        </extensions>

And

<behaviors>
            <serviceBehaviors>
                <behavior name="WcfPortalBehavior">
                    <serviceMetadata httpGetEnabled="true"/>
                    <serviceDebug includeExceptionDetailInFaults="true"/>
                    <compression/>
                </behavior>
            </serviceBehaviors>
        </behaviors>The key class in implementation has to implement the following interfaces: IEndpointBehavior (client side)

and IServiceBehavior (server side).  I actually combined both in the same class.  You would also need to implement message inspector interfaces that WCF will call if they are registered to perform actions on the message.  These interfaces are: IDispatchMessageInspector (server side) and IClientMessageInspector(client side)

Linq to SQL

I just read this blog post on ADO.NET team blog.  I have been wondering about the future on Linq to SQL since the release of Entity Framework.  There is signification overlap in capabilities of both technologies, and one must wonder about the overhead Microsoft is willing to incur in maintaining both.  My guess is, they will not do this forever.  So, if you are starting a new application, would you use Linq to SQL today.  My answer is no.