none
How to avoid the "Dialogs must be user-initiated" exception when trying to print from Lightswitch?

    Question

  • Hello,

    I am trying to print a report from a LS app, and am using the Silverlight PrintDocument class. I tested this out in a Silverlight app, and it worked fine, but when I tried it in my LS app, I got the "Dialogs must be user-initiated" exception.

    I have wrapped the code in a call to Dispatchers.Main.BeginInvoke(), which I thought might solve this, but it didn't.

    Is there any way to get around this in LS? If not, how do people like DevExpress manage to invoke dialogs in their controls?

    Thanks


    FREE custom controls for Lightswitch! A collection of useful controls for Lightswitch developers. Download from the Visual Studio Gallery.

    If you're really bored, you could read about my experiments with .NET and some of Microsoft's newer technologies at http://dotnetwhatnot.pixata.co.uk/

    Monday, January 30, 2012 5:20 PM

Answers

  • It's not a LightSwitch limitation, but a Silverlight security limitation.

    Basically, you have to open a SaveFileDialog or OpenFileDialog directly in response to a event that is caused by a user action (e.g. Click), which are only raised on the main thread. So, you cannot open these dialogs from your user code in a normal method, because user code is executed on the logic thread, which is 1) not the main thread, and 2) not necessarily executed in response to a event caused by a user action.

    You basically have two options to get around the security limitation:

    1. Create a custom control and open the dialog in response to a user event (e.g. a button click). This is how the excel importer and office integration pack extensions work.
    2. Use the FindControl method on a button control and subscribe an click event handler to the button in the control available event. Write the dialog code in the click event handler.

    I'm not sure how the DevExpress extension does it, but my guess is that they use the first approach (they created some piece of UI and have event handlers for the UI that open the dialogs).


    Justin Anderson, LightSwitch Development Team
    • Marked as answer by Mr Yossu Wednesday, February 01, 2012 1:59 PM
    Wednesday, February 01, 2012 4:15 AM
  • Hi Justin, thanks for the reply.

    I thought I would get around the threading issue by the call to Dispatchers.Main.BeginInvoke(), which I thought was supposed to run the code on the main thread.

    It occurred to me afterwards that the problem is probably that the Execute() method of the button is probably not a click event handler, but a command method on the view model, which is bound to the view. This is one of the areas where I think LS is very confusing. I'm used to regular MVVM, where the view model code and view code (if there is any) live in separate files, and are clearly separated. In LS, you end up with the two in the one file, and it's hard to keep your mind on which is which.

    Anyway, I decided to try the click event idea first, as if I were to use a custom control, I would have the problem of having to find out how to get the control to know about the model entities. I have never found a way of doing this, which makes working with the entities very hard in custom control code.

    It turned out to be very straightforward. As the code is in the same file as the view model code, I had access to the entities, so could create my print document easily. I'm still dubious about the technicalities of this, as the click event handler is view code, so shouldn't have straight access to view model properties, but it works.

    In case it helps anyone, here is the code I used. The screen is called Diary, and I have a button to print details of the selected clinic in the diary.

        partial void Diary_InitializeDataWorkspace(List<IDataService> saveChangesTo) {
          var PrintClinicDetailsButton = this.FindControl("PrintClinicDetailsButton");
          PrintClinicDetailsButton.ControlAvailable += new EventHandler<ControlAvailableEventArgs>(PrintClinicDetailsButton_ControlAvailable);
        }
    
        void PrintClinicDetailsButton_ControlAvailable(object sender, ControlAvailableEventArgs e) {
          ((Button)e.Control).Click += new System.Windows.RoutedEventHandler(Diary_Click);
        }
    
        void Diary_Click(object sender, System.Windows.RoutedEventArgs e) {
          Clinic c = ClinicsByDateAndLocation.SelectedItem;
          PrintDiary(c);
        }
    
    


    The actual printing is done with the aid of the rather excellent Simple ReportDocument library, which requires you to add a reference in the Client project to the DLL, and then use code like he shows on that blog post. It works really well. The only downside is that you have to code the report yourself, you don't get a WYSIWYG report designer like you do with the DevEx control, but on the other hand, it's free, and works right inside LS. You don't need to add RIA services to get it to work.

    I'll mark both your reply and mine as answers Justin, as mine should help anyone else see what code to use.

    Thanks again for the reply.


    FREE custom controls for Lightswitch! A collection of useful controls for Lightswitch developers. Download from the Visual Studio Gallery.

    If you're really bored, you could read about my experiments with .NET and some of Microsoft's newer technologies at http://dotnetwhatnot.pixata.co.uk/

    • Marked as answer by Mr Yossu Wednesday, February 01, 2012 1:59 PM
    Wednesday, February 01, 2012 1:59 PM

All replies

  • It's not a LightSwitch limitation, but a Silverlight security limitation.

    Basically, you have to open a SaveFileDialog or OpenFileDialog directly in response to a event that is caused by a user action (e.g. Click), which are only raised on the main thread. So, you cannot open these dialogs from your user code in a normal method, because user code is executed on the logic thread, which is 1) not the main thread, and 2) not necessarily executed in response to a event caused by a user action.

    You basically have two options to get around the security limitation:

    1. Create a custom control and open the dialog in response to a user event (e.g. a button click). This is how the excel importer and office integration pack extensions work.
    2. Use the FindControl method on a button control and subscribe an click event handler to the button in the control available event. Write the dialog code in the click event handler.

    I'm not sure how the DevExpress extension does it, but my guess is that they use the first approach (they created some piece of UI and have event handlers for the UI that open the dialogs).


    Justin Anderson, LightSwitch Development Team
    • Marked as answer by Mr Yossu Wednesday, February 01, 2012 1:59 PM
    Wednesday, February 01, 2012 4:15 AM
  • Hi Justin, thanks for the reply.

    I thought I would get around the threading issue by the call to Dispatchers.Main.BeginInvoke(), which I thought was supposed to run the code on the main thread.

    It occurred to me afterwards that the problem is probably that the Execute() method of the button is probably not a click event handler, but a command method on the view model, which is bound to the view. This is one of the areas where I think LS is very confusing. I'm used to regular MVVM, where the view model code and view code (if there is any) live in separate files, and are clearly separated. In LS, you end up with the two in the one file, and it's hard to keep your mind on which is which.

    Anyway, I decided to try the click event idea first, as if I were to use a custom control, I would have the problem of having to find out how to get the control to know about the model entities. I have never found a way of doing this, which makes working with the entities very hard in custom control code.

    It turned out to be very straightforward. As the code is in the same file as the view model code, I had access to the entities, so could create my print document easily. I'm still dubious about the technicalities of this, as the click event handler is view code, so shouldn't have straight access to view model properties, but it works.

    In case it helps anyone, here is the code I used. The screen is called Diary, and I have a button to print details of the selected clinic in the diary.

        partial void Diary_InitializeDataWorkspace(List<IDataService> saveChangesTo) {
          var PrintClinicDetailsButton = this.FindControl("PrintClinicDetailsButton");
          PrintClinicDetailsButton.ControlAvailable += new EventHandler<ControlAvailableEventArgs>(PrintClinicDetailsButton_ControlAvailable);
        }
    
        void PrintClinicDetailsButton_ControlAvailable(object sender, ControlAvailableEventArgs e) {
          ((Button)e.Control).Click += new System.Windows.RoutedEventHandler(Diary_Click);
        }
    
        void Diary_Click(object sender, System.Windows.RoutedEventArgs e) {
          Clinic c = ClinicsByDateAndLocation.SelectedItem;
          PrintDiary(c);
        }
    
    


    The actual printing is done with the aid of the rather excellent Simple ReportDocument library, which requires you to add a reference in the Client project to the DLL, and then use code like he shows on that blog post. It works really well. The only downside is that you have to code the report yourself, you don't get a WYSIWYG report designer like you do with the DevEx control, but on the other hand, it's free, and works right inside LS. You don't need to add RIA services to get it to work.

    I'll mark both your reply and mine as answers Justin, as mine should help anyone else see what code to use.

    Thanks again for the reply.


    FREE custom controls for Lightswitch! A collection of useful controls for Lightswitch developers. Download from the Visual Studio Gallery.

    If you're really bored, you could read about my experiments with .NET and some of Microsoft's newer technologies at http://dotnetwhatnot.pixata.co.uk/

    • Marked as answer by Mr Yossu Wednesday, February 01, 2012 1:59 PM
    Wednesday, February 01, 2012 1:59 PM
  • Hi Mr Yossu

    I think than other would like to know if this work both for OOB and Browser app (if I miss this info).

    Thanks


    Spaso Lazarevic
    Wednesday, February 01, 2012 2:13 PM
  • Hi Spaso,

    I know it works for OOB, as that's what I'm using, and I just tried changing my app type to browser to test it. Other than the fact that you get a small dialog pop up saying "Dialog must be user initiated" it works fine. It might be offputting to the user to see this pop up, but they get the print dialog and can print the document.

    Hope this helps.


    FREE custom controls for Lightswitch! A collection of useful controls for Lightswitch developers. Download from the Visual Studio Gallery.

    If you're really bored, you could read about my experiments with .NET and some of Microsoft's newer technologies at http://dotnetwhatnot.pixata.co.uk/

    Wednesday, February 01, 2012 2:23 PM