locked
Lightswitch Silverlight Client Screen Close Performance/Slow for complex business screens RRS feed

  • General discussion

  • I wanted to share a solution to the issue raised in several forums concerning the speed of the Lightswitch Silverlight Client when closing a screen tab.  The 'workaround' is to make your screen simpler.  That simply didn't work for the requirements I had from my clients.

    After much digging and opening up an issue with Microsoft, I confirmed that the deeper the levels on the screen the longer it takes to close, but to a factor of 2^#Levels!. I dug deeper into the Microsoft Lightswitch code and it certainly appears that the iterative process they are using is terribly inefficient.

    My solution was to 'Pre-Dispose' the ContentItems at screen close time.

    First, I created an extension to iterate from the bottom up and dispose of the content items.

    using System.Windows.Controls;
    using Microsoft.LightSwitch.Presentation;
    using Microsoft.LightSwitch.Client;
    using Microsoft.LightSwitch.Presentation.Framework;
    using Microsoft.LightSwitch.Presentation.Extensions;
    
    namespace LightSwitchApplication
    {
        public static class PreDisposeScreen
        {
            private static void DisposeChildItem(IContentItem contentItem)
            {
                foreach (IContentItem content in contentItem.ChildItems)
                {
                    DisposeChildItem(content);
                }
                contentItem.Dispose();
            }
            public static void PreDispose(this IScreenObject screenObject)
            {
                screenObject.FindControl("ScreenLayout").ControlAvailable += (sender, e) =>
                {
                    Grid _ScreenLayout = (Grid)e.Control;
                    foreach (ContentItemPresenter contentItemPresenter in ((Grid)e.Control).Children)
                    {
                        DisposeChildItem(contentItemPresenter.ContentItem);
                    }
                };
            }
        }
    }
    

    Now, during the close event of the screen, you will need to make sure there are no changes so that in the event that the 'Save or Discard' window pops up, you haven't already disposed of the contents, and if safe, call the predispose:

    partial void MyScreen_Closing(ref bool cancel)
            {
                //only predispose if there are no pending changes
                if (!this.DataWorkspace.ApplicationData.Details.HasChanges)
                    this.PreDispose();
            }

    In the test application I was using I had a grid 8 levels down and it took minutes to close.  Now, it takes .2 seconds!

    Hope this helps all of you with this issue

    Friday, December 27, 2013 6:36 PM

All replies

  • Hi Grampa,

    It really works, thanks for sharing :)

    Version in VB.NET

    Imports System.Windows.Controls
    Imports Microsoft.LightSwitch.Presentation
    Imports Microsoft.LightSwitch.Client
    Imports Microsoft.LightSwitch.Presentation.Framework
    Imports Microsoft.LightSwitch.Presentation.Extensions
    
    Public Module PreDisposeScreen
        Sub New()
        End Sub
        Private Sub DisposeChildItem(contentItem As IContentItem)
            If contentItem IsNot Nothing Then
                For Each content As IContentItem In contentItem.ChildItems
                    DisposeChildItem(content)
                Next
                contentItem.Dispose()
            End If
        End Sub
        <System.Runtime.CompilerServices.Extension> _
        Public Sub PreDispose(screenObject As IScreenObject)
            AddHandler screenObject.FindControl("ScreenLayout").ControlAvailable,
                Sub(sender, e)
                    Dim _ScreenLayout As Grid = DirectCast(e.Control, Grid)
                    For Each contentItemPresenter As ContentItemPresenter In DirectCast(e.Control, Grid).Children
                        DisposeChildItem(contentItemPresenter.ContentItem)
                    Next
                End Sub
        End Sub
    End Module

    And...

            Private Sub Lancamento_de_Contas_Closing(ByRef cancel As Boolean)
                If Not Me.DataWorkspace.Dados_Data.Details.HasChanges Then
                    Me.PreDispose()
                End If
            End Sub

    Monday, December 30, 2013 7:16 PM
  • Hello, it seems interesting thread you're doing.

    You can tell a little more how to implement? It is on every screen? Or we could create an extension that loads on all screens?
    Tuesday, December 31, 2013 12:14 AM
  • The way I implemented it was to add the call to the extension method to each screen's _Closing event.

    You need to do the check on the HasChanges bit to ensure you do not dispose the objects in the event the dialog for 'Save, Discard or Cancel' is answered with 'cancel' and the user needs those objects.

    Hopefully MSFT will resolve the issue in a future release, making this code unnecessary.


    GrampaParent

    Tuesday, December 31, 2013 5:57 AM
  • Super Thank you!!
    Tuesday, December 31, 2013 8:54 AM
  • But talk to your post that you have created an extension in this case would talk to create a class and then import it to the screen where you work?

    I get this error :(

    Detalles del error: No se puede convertir un objeto de tipo

    'System.Windows.Controls.TabControl' al tipo 'System.Windows.Controls.Grid

    Can  anyone help me¿?

    Thursday, January 16, 2014 9:46 PM
  • What statement are you getting this error on?

    Is it this one?

    screenObject.FindControl("ScreenLayout").ControlAvailable ...

    ScreenLayout should return a grid , not a TabControl...

    Friday, January 17, 2014 8:30 PM
  • yes, its here when app hangs:

    AddHandler screenObject.FindControl("ScreenLayout").ControlAvailable,
                  Sub(sender, e)
                      Dim _ScreenLayout As Grid = DirectCast(e.Control, Grid)
                      For Each contentItemPresenter As ContentItemPresenter In DirectCast(e.Control, Grid).Children
                          DisposeChildItem(contentItemPresenter.ContentItem)
                      Next
                  End Sub

    Saturday, January 18, 2014 12:35 PM
  • It may be that one of your 'Children' of the ScreenLayout is a custom control something other than a ContentItemPresenter.

    Change the line:

    For Each contentItemPresenter As ContentItemPresenter In DirectCast(e.Control, Grid).Children

    to:                     

    ForEach contentItemPresenter As ContentItemPresenter In DirectCast(e.Control, Grid).Children.OfType(Of ContentItemPresenter)()


    • Edited by BillS_GP Saturday, January 18, 2014 8:43 PM
    Saturday, January 18, 2014 8:42 PM
  • I get this error:

    in this line: 

     

    Dim _ScreenLayout AsGrid = DirectCast(e.Control, Grid)

    No se puede convertir un objeto de tipo 'System.Windows.Controls.TabControl' al tipo 'System.Windows.Controls.Grid'.


    Saturday, January 18, 2014 11:21 PM
  • works perfect in one of my complex Screens - but the question is why Microsoft didn't solve that because they knew this problem since months/years
    Sunday, January 19, 2014 2:18 PM
  • In your case, e.Control returned from the "ScreenLayout" group at the very top of your form is a 'Tabs Layout".  The code is written for the default "Rows Layout" that returns a grid. (Or a Columns Layout)

    You will need to either change it to be a Rows Layout or modify the code to handle both.

    Sunday, January 19, 2014 5:39 PM
  • can you help to finalize instruction?

    Dim _ScreenLayout AsTabControl = DirectCast(e.Control, TabControl)                     ForEach contentItemPresenter AsContentItemPresenterInDirectCast(e.Control, TabControl).Children.OfType(OfContentItemPresenter)()                         DisposeChildItem(contentItemPresenter.ContentItem)

    Next

      Children is not membre of TabControl.

    I paste an image of my Screen:

                             

    Sunday, January 19, 2014 6:56 PM
  • <System.Runtime.CompilerServices.Extension> _
            Public Sub PreDispose(screenObject As IScreenObject)
                AddHandler screenObject.FindControl("ScreenLayout").ControlAvailable,
                  Sub(sender, e)
                      For Each tabItem As TabItem In DirectCast(e.Control, TabControl).Items
                          DisposeChildItem(DirectCast(tabItem.Content, ContentItemPresenter).ContentItem)
                      Next
                  End Sub
            End Sub

    Monday, January 20, 2014 12:52 AM
  • THANK'S!! WORKS PERFECTLY!

    Monday, January 20, 2014 8:28 PM