Slow CAB performance

Topics: CAB & Smart Client Software Factory
May 8, 2007 at 6:58 PM
Hello, all.
I've been running into performance issues with using the CAB for my application. I have a module that adds a new record to the database (the record has a lot of fields). So, I have my CAB design where I click on a button in the main shell, and it opens the workItem for add new record. The add new record view source file (without the designer.cs file) has 2098 lines of (C#) code in it. It has a number of text boxes, tab navigator, 4 microsoft report viewers, and a sometimes it loads pictures also. It takes up to 4 seconds to open up. What is it that I can do to speed the performance? or at least, detect which part of this work item is taking the most amount of time? Would ngen or multithreading (using the backgroundworker) help me?

Thanks,
May 8, 2007 at 11:28 PM
Four report viewers? Wow, no wonder it is slow.

Background thread won't help; you're creating Forms elements, and those must be created on the UIThread.

You won't be able to speed this up (that's my guess given the size of the class you're instantiating). Creating windows forms is not a cheap operation, and when you have to create a huge form like the one you have, with several heavyweight controls on them, then there's just no getting around the creational slowdown.

What you can do is provide your users with a progressbar so that they know the application is loading something. We do this with a service. It's not big, so I'll post it here:

public delegate void ProgressDelegate();
    
    public interface IProgressNotificationService
    {
        void Start();
        void Start(string caption);
        void SetShellForm(Control control);
 
        ProgressDelegate ProgressDelegate { get; set;}
        
    }

Then the implementation:

public class ProgressNotificationService : IProgressNotificationService
    {
        private Thread _thread;
        private ProgressDialog _dialog;
        private Control _control;
        private ProgressDelegate _progressDelegate;
 
        #region IProgressNotificationService Members
 
        public void Start()
        {
            StartProgressThread("Loading...");
            try
            {
                ProgressDelegate.Invoke();
            }
            catch
            {
                throw;
            }
            finally
            {
                Stop();
            }
        }
 
        public void Start(string caption)
        {
            StartProgressThread(caption);
            try
            {
                ProgressDelegate.Invoke();
            }
            catch
            {
                throw;
            }
            finally
            {
                Stop();
            }
        }
 
        public void SetShellForm(Control control)
        {
            _control = control;
        }
 
        public ProgressDelegate ProgressDelegate
        {
            get { return _progressDelegate; }
            set { _progressDelegate = value; }
        }
        
        #endregion
 
        private void Stop()
        {
            _dialog.Close();
        }
 
        private void StartProgressThread(string caption)
        {
            if (_control != null)
                _control.Update();
 
            _dialog = new ProgressDialog();
            _dialog.Text = caption;
 
            _thread = new Thread(new ThreadStart(ShowProgressDialog));
            _thread.Priority = ThreadPriority.AboveNormal;
            _thread.Start();
        }
        
        private void ShowProgressDialog()
        {
            _dialog.StartPosition = FormStartPosition.CenterScreen;
            _dialog.ShowDialog();
        }
    }

To use the service, you just have to create designate the delegate method...

private void CreateAndRunAdministrationWorkItem()
{
            TimecardAdministrationWorkItem administrationWorkItem =
                Items.AddNew<TimecardAdministrationWorkItem>(WorkItemNames.TimecardAdministrationWorkItem);
            administrationWorkItem.Run();
}

And then make the proper call to start:

 [CommandHandler(CommandNames.ShowTimecardAdministrationView)]
public void ShowTimecardAdministrationView(object sender, EventArgs e)
{
            TimecardAdministrationWorkItem administrationWorkItem =
                WorkItems.Get<TimecardAdministrationWorkItem>(WorkItemNames.TimecardAdministrationWorkItem);
 
            if (administrationWorkItem == null)
            {
                _progressService.ProgressDelegate = new ProgressDelegate(CreateAndRunAdministrationWorkItem);
                _progressService.Start("Loading Timecard Administration WorkItem");
            }
}


The _dialog class is a just small Windows Forms class with a ProgressBar control on it set to Marquee mode, nothing more (no other code or methods added). When the dialog is show the progress bar scrolls in marquee mode and the user at least knows something is loading that is going to take longer than a split second.

May 10, 2007 at 4:10 AM
I tried above implementation in my CAB app. and got following error message while calling dialog.Close() method.

Additional information: Cross-thread operation not valid: Control ProgressDialog accessed from a thread other than the thread it was created on.

Can anyone correct me , what I am doing wrong.

Thanks
May 10, 2007 at 5:37 AM
nyuser98,
you might want to post more details about your code. Chris's implementation worked perfect for me. Thanks Chris!
May 11, 2007 at 2:19 AM
wittgenstein,

Thanks for the reply, Here is my code.

Chris's implementation
IProgressNotificationService.cs
ProgressNotificationService.cs

ManagerShellApplication.cs
        protected override void AddServices()
	{
		base.AddServices();
		RootWorkItem.Services.AddNew<ProgressNotificationService, IProgressNotificationService>();
	}

InventoryController.cs
	private IProgressNotificationService m_progressService;
 
        [InjectionConstructor]
        public InventoryController
            (
              [ServiceDependency] IProgressNotificationService progressService
            )
        {
		m_progressService = progressService;
        }
                
	[CommandHandler(CommandNames.ShowItems)]
        public void OnShowItems(object sender, EventArgs e)
        {
		m_progressService.ProgressDelegate = new ProgressDelegate(GetItems);
		m_progressService.Start("Loading items...");
        }
        
        public void GetItems()
	{
		Console.WriteLine("before db access....");
		Thread.Sleep(2000);		// Run query, to load items from database
		Console.WriteLine("after db access....");
        }
Jun 2, 2008 at 1:56 PM


nyuser98 wrote:
I tried above implementation in my CAB app. and got following error message while calling dialog.Close() method.

Additional information: Cross-thread operation not valid: Control ProgressDialog accessed from a thread other than the thread it was created on.

Can anyone correct me , what I am doing wrong.

Thanks



Ok this is 1 year ago, but i got the same problem trying to impliment this functionality
Did u find what u were doing wrong?

Cause I might be doing the same thing wrong :)

Some help anyone?