Welcome to MSDN Blogs Sign in | Join | Help

Fixes to WpfPerf Performance Profiling Tool

Some folks reported issues with x64 & with x86 on non-US PCs with our first post.
On 10/9/2008 we posted an update.  These issues should be fixed now.

You are welcome to download from: http://windowsclient.net/wpf/perf/wpf-perf-tool.aspx

Posted by jgoldb | 3 Comments

ETW Event Tracing in WPF

Event Tracing for Windows (ETW) provides application developers the ability to start and stop event tracing sessions, instrument an application to provide trace events, and consume trace events.

In addition to Windows itself, WPF also emits an array of interesting events that can be used to track the execution of an application at the framework level.

Mike Cook wrote a great document that explain the various ETW events emitted by WPF.

Check out the this documentation here.

You are encouraged to use these events to measure and analyze your WPF application performance.

The WpfPerf tool for example, use these events extensively. In addition, the WPF team is also using ETW in its automated tests to analyze WPF performance and catch regressions.

Attached is also a sample that demonstrates how you can consume one of these events (UceNotifyPresent event) to measure frame-rate (aka FPS , Frame-per-Second) in a WPF application.

The sample animates a rectangle and outputs the FPS to a command window . It is writing to a command window and not to the main app window so that it will not affect the real FPS.
image 
You should expect to see frame-rate tied to the refresh rate of your monitor.
Special thanks to Mike Cook and  TJ Hsiang who helped put this sample together.

Posted by jgoldb | 4 Comments

Attachment(s): FPSMeasurementSample.zip

Updated WPFPerf Performance Profiling Tools for WPF

The WPFPerf tool comprises of a suite of performance profiling tools that allow you to analyze the run-time behavior of your WPF application and point to potential performance bottlenecks.

We now added some new functionality as well as given the tool a major facelift.
Below are highlights for some of the exciting improvements that are included in this update.
More details and download link is available here.
Most of the concepts on how to use the tool remain the same so you can still refer to the existing documentation for the tool here to learn more.  The tool should work on all 3.x version of .Net.

Note: some folks reported issues when installing on none-US machines. Until this is investigated, please install only on US  machine using the default local settings.

A) Much improved “Visual Profiler” tool:
image 

The Visual Profile tool now provides:

1) Search: provides the ability to search element in the application’s Element Tree.
image

2) Hotpath: Allows you to Right Click / Expand Hotpath on an Element Tree, the tree will expand to show the element in the sub-tree that consumes the most amount of CPU in that sub-tree.

3) Elements CPU Usage: As user expands the Element Tree (e.g. by using the Hotpath feature from above), elements that (Exclusively) consumes CPU time are highlighted in Red. The intensity of the Red highlighting corresponds to the percentage of CPU usage.

image

4) Tinting the Target Application : Using the Overlay Window button, you can view the Selected and Hotpath sub-tree elements in their target application.
If the Overlay Window button is depressed, then:

  • Selecting an element in Visual Profiler’s Element Tree will draw a yellow rectangle on that same element in the target application.
  • Hotpath elements are tinted in Red in the target application. The intensity of the Red tinting in the target application corresponds to the percentage of CPU usage.

Here is an example of how target application may look:
image

Note: Enabling this feature will slow the target application performance. In addition, tinting the target application uses Layered Windows. It is recommended that users follow this blog to make to obtain latest QFEs (or use XP Sp3 or Vista Sp1)

5) Live Preview: If the “Live Preview” is enabled, then in a live representation of the selected Element sub-tree is drawn on the bottom left. Otherwise a static image of the sub-tree is displayed.
In both cases, the image will change if another Tree Element is selected.

Note: Enabling this feature will slow the target application performance.

6) Splitter control: Allows user to adjust the various display areas (E.g. the Element Tree & Element Information areas)

7) Graph Duration Slider: Allow user to change the amount of history data collected.

8) Expander controls: Allow user to remove un-wanted clutter from the screen

9) Element Tree View: Using the View menu you can control what information is displayed on the each node in the Element Tree and remove unwanted clutter.
clip_image010

B) Much improved “Perforator” tool:
image 

The Perforator tool now provides:

1. Improved UX : More intuitive and richer UX.

2. History Data:

  • History Graphs of Frames-Per-Second, Direct Rectangles Addition Rate, HW and SW IRT count and Video Memory Usage.
  • Graph Duration slider to change the amount of history data.
  • Capture Start/Pause button to allow pause and re-capture of data.
    The History Graph value axis (Y-Axis) is now auto-adjusting.

3. Detect SW rendered Bitmap Effects: Checkbox added to tint the target application elements that use SW rendered legacy Bitmap Effects.

In below example, the top button is decorated with OuterGlowEffect Bitmap Effect. Since this effect is not HW accelerated the Button is tinted in Red.

The bottom Button is decorated with DropShadow Bitmap Effect which is HW accelerated (DropShadow is now HW accelerated in .Net 3.5 Sp1), and therefore is not tinted with Red.
image 
Known limitations:
A) You must force the target elements to redraw itself after you enabled the checkbox for you to see the red tint. E.g. hover over with the mouse, if it is standard button.
B) You still need to launch Perforator before you launch your app.

C) New tool: String Allocation Profiler
image
We added a new tool to WPFPerf called “String Allocation Profiler”
The tool enables users to view all the strings and their sizes which were allocated by the application from the time the application started till the time it closed.

To use this tool:
1. Click on “Action / Lunch Process…” to launch the target application.
Notice that WpfPerf says “Profiling…” on the status barclip_image002[4]
2. Click around your application and when done close it.
Notice that WpfPerf says “Processing Profile Data…” on the status bar
clip_image004
3. When done, WpfPerf displays the application method tree and for each method it shows the Inclusive and Exclusive # of strings allocations and their sizes.
4. You can drill down the application Method Stack Tree to view all the strings your application allocated in a specific method sub-tree.
5. Search: If you know the string names you can also search for them and find the methods that allocated them.
6. Using the “Action/Save String Allocation Log” and “Action / Save String Allocation Log” menu, you can save and open a view allocation logs at a later point.
clip_image006
Notes:
a) The tool display strings allocated directly by the application (in code and XAML) as well as strings allocated by the CLR and WPF framework.
b) The tool have affect on the performance of you application while it monitors string allocations.

D) ETW Event Tracing tool:
Some bugs fixed, and new detailed documentation for the WPF ETW events is now available. See more here.

E) General improvements:
1) Numerous bug fixes
2) Now you can attach and profile XBAPs in addition to Standalone WPF apps
3) Improved “Add Tool” dialog. 
    It now allows user to select more than one tool at a time.
clip_image002[6]
4) UI improvements to the “Select Process” dialog.
    This allows user to select the target application to profile.
clip_image004[5]
5) UI improvements to the “Launch Process” dialog.
clip_image006[5]

F) Know issues and Caveats:

  1. General:
    Try to avoid using more than one tool (e.g. Visual profiler & Perforator) at the same time to profile the same app as it will decrease the application performance.
  2. Perforator/ Detect SW rendered Bitmap Effects:
    When you set “Detect SW rendered Bitmap Effects”, in order for the Red tint to show, you must force the target elements to redraw itself.
  3. Perforator/ Overlay Window:
    Tinting the target application uses Layered Windows. It is recommended that users follow this blog to make to obtain latest QFEs (or use XP Sp3 or Vista Sp1)
  4. Perforator/ Live Preview:
    Live Preview uses Visual brush, so if you also use Perforator to profile the same app, you may see that the HW IRT count increases by 1.
  5. Perforator/Visual profiler
    You can attach to an XBAP (by attaching to PresentationHost.exe), but you may not able to launch an XBAP.
  6. Perforator/Visual profiler
    You must launch Perforator before you launch your app.

Please enjoy the tool and tell us what you think and what needs improvement.

Posted by jgoldb | 11 Comments

Improving Microsoft DataGrid CTP sorting performance - Part 2

In this blog I wrote how you can improve Microsoft DataGrid CTP by providing your own custom sort.  
As one of the comments suggested you can get even better performance by using delegates.
I have changed MySort code below and I now use delegates to avoid the switch comparison that was called on every compare during the QuickSort . I can now see additional ~50% speed gain during sort.

In my 300,000 rows DataGrid, before I saw sort speed decreased from almost 5 minutes to 2.4 seconds and now the String type columns (e.g. Name, Position) are sorted in 1.4 sec while the Integer type columns (e.g. Id) are now sorted in 400 ms!

   1:  public class MySort : IComparer
   2:  {
   3:        public delegate int TwoArgDelegate(Employee arg1, Employee arg2);
   4:        TwoArgDelegate myCompare;
   5:        public MySort(ListSortDirection direction, DataGridColumn column)
   6:        {
   7:                  int dir = (direction == ListSortDirection.Ascending) ? 1 : -1;
   8:                  //set a delegate to be called by IComparer.Compare
   9:                  switch ((string)column.Header)
  10:                  {
  11:                      case "Id":
  12:                          myCompare = (a, b) => { return a.Id.CompareTo(b.Id) * dir; };
  13:                          break;
  14:                      case "Name":
  15:                          myCompare = (a, b) => { return a.Name.CompareTo(b.Name) * dir; };
  16:                          break;
  17:                      case "Position":
  18:                          myCompare = (a, b) => { return a.Position.CompareTo(b.Position) * dir; };
  19:                          break;
  20:                      case "Tel":
  21:                          myCompare = (a, b) => { return a.Telephone.CompareTo(b.Telephone) * dir; };
  22:                          break;
  23:                      case "Email":
  24:                          myCompare = (a, b) => { return a.Email.CompareTo(b.Email) * dir; };
  25:                          break;
  26:                      case "Enabled":
  27:                          myCompare = (a, b) => { return a.Enabled.CompareTo(b.Enabled) * dir; };
  28:                          break;
  29:                      case "City":
  30:                          myCompare = (a, b) => { return a.City.CompareTo(b.City) * dir; };
  31:                          break;
  32:                      case "Country":
  33:                          myCompare = (a, b) => { return a.Country.CompareTo(b.Country) * dir; };
  34:                          break;
  35:                      default:
  36:                          myCompare = (a, b) => { return 0; };
  37:                          break;
  38:                  }
  39:              }
  40:              int IComparer.Compare(object X, object Y)
  41:              {
  42:                  return myCompare((Employee)X, (Employee)Y);
  43:              }
  44:      }
  45:  }
Posted by jgoldb | 1 Comments

Attachment(s): DataGridSort2.zip

Improving Microsoft DataGrid CTP sorting performance

Summary:

As you may know Microsoft released a Community Tech Preview (CTP) of the DataGrid control. See the posting here.
Once you start using the DataGrid CTP, load many elements to it and you use the default, built-in sort (by simply clicking on the DataGrid column headers) you may notice that sorting can be very slow.
The reason is that the comparer that is built-in to the MS DataGrid knows nothing about your data type and currently use reflection which is very costly.

In this blog I wanted to point you to a simple approach that could significantly improve the sorting performance of your DataGrid.

Approach:

If you load many elements to the MS DataGrid and use the built-in sort which currently use reflection, you may find sorting to be very slow.

Fortunately, for many scenarios you can implement your IComparer and provide it in the CustomSort property. You could achieve significant performance gains by doing so, in my example I was able to achieve 100x speed improvement.

In my 300,000 rows DataGrid, sort speed decreased from almost 5 minutes to 2.4 seconds !
(The provided download only loads 6,000 rows, but you are welcome to change...)

image

Code:
public partial class Window1 : System.Windows.Window
{
    private bool UseCustomSort
    {
        get { return CustomSortCB.IsChecked == true; }
    }
 
    private void WPF_DataGrid_Sorting(object sender, DataGridSortingEventArgs e)
    {
        if (_stopwatch == null)
        {
            _stopwatch = new Stopwatch();
        }
        else
        {
            _stopwatch.Reset();
        }
 
        _stopwatch.Start();
        if (UseCustomSort)
        {
            e.Handled = true;   // prevent the built-in sort from sorting
            PerformCustomSort(e.Column);
            CustomSortingDone();
        }
        else
        {
            Dispatcher.BeginInvoke(DispatcherPriority.Normal, new NoArgDelegate(StandardSortingDone));
        }
    }
 
    private void StandardSortingDone()
    {
        _stopwatch.Stop();
        statusTextBlock1.Text = "Buit-in Sort: " + _stopwatch.ElapsedMilliseconds.ToString() + " msec";
    }
    private void CustomSortingDone()
    {
        _stopwatch.Stop();
        statusTextBlock2.Text = "Custom Sort: " + _stopwatch.ElapsedMilliseconds.ToString() + " msec";
    }
    private void PerformCustomSort(DataGridColumn column)
    {
 
        ListSortDirection direction = (column.SortDirection != ListSortDirection.Ascending) ? 
                                 ListSortDirection.Ascending : ListSortDirection.Descending;
        column.SortDirection = direction;
        ListCollectionView lcv = (ListCollectionView)CollectionViewSource.GetDefaultView(WPF_DataGrid.ItemsSource);
        MySort mySort = new MySort(direction, column);
        lcv.CustomSort = mySort;  // provide our own sort
    }
 
    public class MySort : IComparer
    {
        public MySort(ListSortDirection direction, DataGridColumn column)
        {
            Direction = direction;
            Column = column;
        }
 
        public ListSortDirection Direction
        {
            get;
            private set;
        }
 
        public DataGridColumn Column
        {
            get;
            private set;
        }
 
        int StringCompare(string s1, string s2)
        {
            if (Direction == ListSortDirection.Ascending)
                return s1.CompareTo(s2);
            return s2.CompareTo(s1);
        }
 
        int IComparer.Compare(object X, object Y)
        {
            int int1, int2;
            string str1, str2;
            switch ((string)Column.Header)
            {
                case "Id":
                    int1 = ((Employee)(X)).Id;
                    int2 = ((Employee)(Y)).Id;
                    if (Direction == ListSortDirection.Ascending)
                        return int1.CompareTo(int2);
                    return int2.CompareTo(int1);
                case "Name":
                    str1 = ((Employee)X).Name;
                    str2 = ((Employee)Y).Name;
                    return StringCompare(str1, str2);
                // ... do same for other columns
     
              }
            return 0;
        }
    }
   private Stopwatch _stopwatch;
}

XAML Code:
<Window x:Class="DataGridSortDemo.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    xmlns:local="clr-namespace:DataGridSortDemo"
    xmlns:dg="http://schemas.microsoft.com/wpf/2008/toolkit"
    xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid"
    Title="Microsoft DataGrid Sort Example" Height="760" Width="850"
    >
    <Window.Resources>
        <local:Employees x:Key="employees" />
    </Window.Resources>
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition MinHeight="25" Height="Auto"/>
      <RowDefinition MinHeight="3in" Height="*" />
      <RowDefinition Height="Auto" MinHeight="10" />
      <RowDefinition Height="Auto" MinHeight="10" />
      <RowDefinition Height="Auto" MinHeight="10" />
      <RowDefinition Height="Auto" MinHeight="10" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>
 
 
    <dg:DataGrid x:Name="WPF_DataGrid" FontSize="12pt"
                 Grid.Row="1" Grid.Column="0"  Margin="10,2,10,2"
                 AutoGenerateColumns="False"
                 Sorting="WPF_DataGrid_Sorting"
                 ItemsSource="{StaticResource employees}"
              >
            
      <dg:DataGrid.Columns>
        <dg:DataGridTextColumn Width="Auto" Header="Id" DataFieldBinding="{Binding Path=Id}"></dg:DataGridTextColumn>
        <dg:DataGridTextColumn Width="Auto" Header="Name" DataFieldBinding="{Binding Path=Name}"></dg:DataGridTextColumn>
        <dg:DataGridTextColumn Width="Auto" Header="Position" DataFieldBinding="{Binding Path=Position}"></dg:DataGridTextColumn>
        <dg:DataGridTextColumn Width="Auto" Header="Tel" DataFieldBinding="{Binding Path=Telephone}"></dg:DataGridTextColumn>
        <dg:DataGridTextColumn Width="Auto" Header="Email" DataFieldBinding="{Binding Path=Email}"></dg:DataGridTextColumn>
        <dg:DataGridTextColumn Width="Auto" Header="Enabled" DataFieldBinding="{Binding Path=Enabled}"></dg:DataGridTextColumn>
        <dg:DataGridTextColumn Width="Auto" Header="City" DataFieldBinding="{Binding Path=City}"></dg:DataGridTextColumn>
        <dg:DataGridTextColumn Width="Auto" Header="Country" DataFieldBinding="{Binding Path=Country}"></dg:DataGridTextColumn>
     </dg:DataGrid.Columns>
    </dg:DataGrid>
 
    <TextBlock Grid.Row="2" Name="statusTextBlock1" Foreground="Red"
               FontSize="12pt" Text="" />
    <TextBlock Grid.Row="3" Grid.Column="0" Name="statusTextBlock2" Foreground="Green"
               FontSize="12pt"  Text=""  />
    <CheckBox  Name="CustomSortCB" FontWeight="Bold"  FontSize="12pt" Grid.Row="0" Grid.Column="0" Margin="10,6,6,6"
               Foreground="Blue">
      Use Custom Sort  [Check / Uncheck box, then Click on the DataGrid header to compare Sort performance]
    </CheckBox>
 
 
  </Grid>
</Window>
Caveats:

You can only use the CustomSort if your DataGrid is bound to an IList. You cannot use the CustomSort if you bound to an instance of IBindingList such as DataView. In this case the class that derives from ICollectionView is a BindingListCollectionView which does not have a custom sort property. The view in this case delegates the Sort logic to the underlying DataView which does not allow sort customization.

Posted by jgoldb | 7 Comments

Attachment(s): DataGridSort.zip

What’s new in WPF 3.5 SP1: Splash Screen to improve perceived startup perf

Summary:

To improve the perception of a more responsive startup experience many WPF applications added a Splash Screen to their startup.  The Splash Screen, which does not load WPF code shows as soon as possible and is displayed until the application main window is rendered.
Up until 3.5 Sp1 we pointed developers to sample code available on this blog.
In  the just released .Net 3.5 SP1 we added basic Splash Screen support and new APIs and we recommend that you take advantage of it. (See download locations below)

A Visual Studio 2008 Sp1 Item Template that makes adding a Splash Screen much easier is available on WPF Futures site on www.codeplex.com/wpf.

How to Use:

     Approach A:

The easiest way to add is by using VS 2008  SP1.
·  Create a new Standalone VB or C# WPF project in VS 2008 Sp1.  Verify “Target Framework” project property is “.Net Framework 3.5”

·   Add an image (your splash screen image) to the project (e.g. by drag and drop) 

·   Set the image BuildAction to SplashScreen

·   Do F5 ( Build+Run), splash screen should be shown.

·   To disable the Splash Screen image functionality you can either:

   o    Remove the “SplashScreen.png” image from the project, or

   o Set the splash image BuildAction to ‘None’.

image

Upon compile, the build (PresentationBuildTasks.dll) will generates below code in App.g.cs:

   1: SplashScreen splashScreen = new SplashScreen("Splashscreen1.png");
   2: splashScreen.Show(true);
   3: MyApp.App app = new MyApp.App();
   4: app.InitializeComponent();
   5: app.Run();

Note:  Above code will be generated in VS 2008 Sp1 only if the “Target Framework” project property is “.Net Framework 3.5”. Otherwise no code will be generated.

Approach B:

Install the Splash Screen Visual Studio 2008 Sp1 Item Template from the WPF Futures site.

·   Create a new Standalone VB or C# WPF project in VS 2008 Sp1.  Verify “Target Framework” project property is “.Net Framework 3.5”

·   In VS,  Right-Click on your Project and then select “Add / New Item… / Splash Screen (WPF)”.

·   Notice the new image BuildAction  is set to SplashScreen for you.

·   Do F5 (Build+Run), the default splash screen image that was installed with the template will be shown (you likely want to replace with your own Splash Screen image)

image

Approach C:

Another approach is to directly use the new public Splash Screen APIs. With this approach you can set the ‘BuildAction=Resource’.
For example you can call the following code:

   1: SplashScreen appSplash = new SplashScreen("Splashscreen1.png");
   2: appSplash.Show(false);
   3: //….do some work…
   4: appSplash.Close( TimeSpan.FromSeconds(0.3)); // Splash to fadeout in 300ms

The WPF Splash Screen APIs are defined as below:

   1: namespace System.Windows
   2: {
   3:     public class SplashScreen
   4:     {
   5:         public SplashScreen(string resourceName);
   6:         public SplashScreen(Assembly resourceAssembly, string resourceName);
   7:         public void Show(bool autoClose);
   8:         public void Close(TimeSpan fadeOutDuration);    
   9:     }
  10: }

public ApplicationSplashScreen(string resourceName)

Behavior

Constructor.  resourceName points to the embedded splash image

Exceptions

 none

public ApplicationSplashScreen(Assembly resourceAssembly, string resourceName)

Behavior

Constructor. resourceAssembly specify the assembly in which resources lives resourceName points to the embedded splash image

Exceptions

 none

public void Show(bool autoClose);

Behavior

Constructor. Shows the splash image.

If autoClose is true, the code uses the dispatcher to queue an item at Idle priority, enabling the Splash screen to start fading out using the default fadeOutDuration (300 msec) after the application first renders. 

If autoClose is false, the app is responsible to call Close(fadeOutDuration), in this case the splash screen fades out using the provided fadeOutDuration.

Exceptions

 IOException if resource provided in constructor not found in app assembly

public void Close(TimeSpan fadeOutDuration)

Behavior

Closes the splash window. 

fadeOutDuration is the time for splash screen to fade out. If autoClose is true, WPF will close the window and use a 300 msec as the default fadeOutDuration.

Exceptions

none

Caveats:

It is important to understand that the Splash Screen has the following limitations:
·         It does not actually improve application cold startup time.
·         Cannot display sequence of splash images or animation.
·         Does not support for animated GIFs
·         Does not support earlier versions of VS (earlier than VS2008 Sp1). You should still be able to use the APIs (e.g. Approach C)

·         Does not support XBAPs (XBAPs already have their own improved coldstart progress page in  .Net 3.5 Sp1)
·         Does not have  XAML support for setting splash screen image
·         Expression Blend does not provide UI to  set the SplashScreen build action

.Net 3.5 SP1 Download locations:

Content

Links

Visual Studio 2008 Express Editions with Service Pack 1 (Bootstrappers)

http://go.microsoft.com/fwlink/?LinkId=123679

Visual Studio 2008 Express Editions with Service Pack 1 (iso)

http://go.microsoft.com/fwlink/?LinkId=123680

Visual Studio 2008 Service Pack 1 (Bootstrapper)

http://go.microsoft.com/fwlink/?LinkId=122094

Visual Studio 2008 Service Pack 1 (iso)

http://go.microsoft.com/fwlink/?LinkId=122095

Visual Studio  Team System 2008 Team Foundation Server Service Pack 1

http://go.microsoft.com/fwlink/?LinkId=124829

.NET Framework 3.5 Service Pack 1

http://go.microsoft.com/fwlink/?LinkId=124150

 

Posted by jgoldb | 11 Comments

Improve WPF application startup using ClickOnce On-Demand Download

In this blog (see item 11.) we discussed the fact that ClickOnce can have a significant effect on application start app time.

For standalone applications (both WPF and Winform apps) if you use the default settings in Visual Studio, ClickOnce will access the site-of-origin server (e.g. company network or internet) to check for a newer version before the application start. As expected, this will slow down the overall application startup. You can consider modifying this default setting to check for a new version after the application starts.

In the XBAP case this is not configurable. ClickOnce will always check the deployment site (site-of-origin) for updates before the XBAP starts.

In certain scenarios, you can consider using the ClickOnce Deployment APIs to download a smaller part of your application (for example, code required only for the first screen of the application) and download the rest of your app modules at a later stage, in the background, as they are needed. If your app is large, this could significantly improve your application startup performance the first time it runs.

It is also very possible that your end-user will not use certain portions of the application, so if you can identify what they are and avoid downloading them it could provide a big win.

Below I have a sample XBAP that shows how you can use the ClickOnce Deployment APIs to achieve this. Note that this approach works both for Standalone and XBAPs applications.

Although the overall size of my sample is 13MB in size, the application downloads and displays relatively quickly since at first we only download the main app module which is only 9KB in size.

The large image resources (~13MB) are contained in a separate module (SimpleOnDemandLibraray.dll) and are not downloaded at startup. SimpleOnDemandLibraray.dll is downloaded in the background from the XBAP site-of-origin only when user clicks on the button.

After launching XBAP this is what is shown:

OnDemand1 

Only when users click on the button the ~13MB module (SimpleOnDemandLibraray.dll) which contains the image resources downloads in the background.
Your app could display a fancier progress UX then mine…:)
OnDemand2
Sometime later, when download completes the image shows as below:OnDemand3

To enable this you need specifcy that SimpleOnDemandLibraray.dll is an optional component.
You can do this in Visual Studio in the Properties/Publish/ Application Files property page like here: