locked
Entry/Edit screens RRS feed

  • Question

  • This may not be a question with just a single answer but I should at least ask.

    I have created many Entry/Edit screens per the Beth Massi video and they all work perfectly except for one and I have been unable to determine why.

    The general procedure is exactly the same in all cases. I first issue a Search screen that lists all the entries in a table along with an Add button in the command bar for adding new records. If the user clicks on the linked field in one of the existing records an update screen is issued. If the user clicks the Add button, the same screen is issued but in add mode instead of update mode. I have perhaps a dozen tables using this exact method of entry/update and all function perfectly except for one.

    With one particular table when the user clicks on the link for an existing record the update screen is indeed issued but it is issued in such a state that the system thinks it has already been altered and if the user then tries to simply exit the screen a message is issued about saving or discarding the changes, even though no changes have been made. This does not happen on any other table but I see no difference in the way any of them have been designed or the screens been coded.

    This problem does not appear to carry with it any observable negative consequences other than causing the user to click on the Discard Changes button when they leave the screen even if they have made no changes.

    Can someone at least give me a hint as to where to start looking?

    Thanks

    Ed



    • Edited by Jyuma1 Friday, May 10, 2013 2:08 PM
    Friday, May 10, 2013 2:05 PM

Answers

  • There's nothing complicated in that code. An online converter should have been able to handle it.

    But here's my translation:

    Imports System.Windows
    Imports Microsoft.LightSwitch.Threading.Dispatchers
    
    Namespace LightSwitchApplication
    
        Public Class MovieList
            Private isExplicitClose As Boolean = False
    
            Private Sub MovieList_Closing(ByRef cancel As Boolean)
                If (isExplicitClose = False) _
                    AndAlso (Me.DataWorkspace.MovieCentralData.Details.HasChanges = False) _
                Then
                    'there are no changes - cancel the built-in save and see 
                    'if the user really wants to close the dialog...
                    cancel = True
                    Current.BeginInvoke(AddressOf ShowAreYouSureDialog)
                Else
                    'there are changes - you could show an alternate dialog (similar to above)
                    ' or rely on the built-in prompt... 
                    'in this case, we've chosen the latter...
                End If
            End Sub
    
            Private Sub ShowAreYouSureDialog()
                If (Me.ShowMessageBox("Close screen" _
                    , "Do you really want to close the screen?" _
                    , MessageBoxOption.YesNo) = MessageBoxResult.Yes) _
                Then
                    'since the screen is already closed, 
                    'you can safely go ahead and close the screen...
                    Me.isExplicitClose = True
                    Me.Close(False)
                    Me.isExplicitClose = False
                End If
            End Sub
    
        End Class
    
    End Namespace

    I've also verified that this "trick" does in fact work.


    Yann Duran
         - Co-Author of Pro Visual Studio LightSwitch 2011
         - Author of the  LightSwitch Central Blog

    FREE Download: Luminous Tools for LightSwitch
    (a Visual Studio productivity extension for LightSwitch)
     
    Click Mark as Answer, if someone's reply answers your question
    Click  Vote as Helpful, if someone's reply is helpful
     
    By doing this you'll help everyone find answers faster.


    Thursday, May 16, 2013 12:41 PM
    Moderator

All replies

  • No takers?  I probably did a poor job of wording my question.

    Here's a shorter version...

    What conditions cause the message to be displayed that asks "Save"  "Discard"  or "Cancel"  before you can exit the screen?  

    Friday, May 10, 2013 8:21 PM
  • Somebody out there has gotta know the answer.

    I now have two screens doing this and it's real annoying to the client.  Why would calling up an existing entity cause Lightswitch to behave as if a change had been made when no change had occurred?

    Saturday, May 11, 2013 12:17 PM
  • I found the problem but I'm not certain I know what to do about it.

    The cause was 2 computed fields I set in the entity designer.  Normally that would not cause the problem but I was taking the result of the computed field and moving it to a database field right in the _computed method.  That is what caused the problem (and in hindsight, rightfully so).

    Saturday, May 11, 2013 1:33 PM
  • This is what happens when ones uses a hook provided by LightSwitch for something other than what it intended. :-P

    You should be assigning the value of the computed property to the "real" property in the screen's Saving method.

    Sorry, I've been offline for a bout a week with server problems.


    Yann Duran
         - Co-Author of Pro Visual Studio LightSwitch 2011
         - Author of the  LightSwitch Central Blog

    FREE Download: Luminous Tools for LightSwitch
    (a Visual Studio productivity extension for LightSwitch)
     
    Click Mark as Answer, if someone's reply answers your question
    Click  Vote as Helpful, if someone's reply is helpful
     
    By doing this you'll help everyone find answers faster.

    Saturday, May 11, 2013 1:51 PM
    Moderator
  • Yup... I figured you were AFK.  Fortunately I had already figured that the final move needed to be in the screen's Saving method.

    Life is good again. :)

    Thanks

    Ed

    Saturday, May 11, 2013 8:14 PM
  • I finally got all my updates working properly when suddenly I realized that I may have made a major error in my design.  I have 4 different sets of screens for updating the 4 levels deep that my project goes... and while it is true that as long as I execute each screen in sequence from lowest to highest all my totals work, it is also true that the totals for level 1 won't be right unless I execute the screen for level1.  The same is true for levels 2 through 4.  If I don't execute the screens in sequence my totals will not be accurate.  

    I suppose I could make it so the user must go through the screens in sequence from Highest to lowest in order to even get to the lowest level for entry and then force a refresh on the way back out but what if the user skips a screen on the way out or otherwise exits them out of sequence?

    Screen 1... contains totals of activity on screen 2

    Screen 2... contains totals of activity on screen 3

    Screen 3... contains totals of activity on screen 4.

    I have no problem creating the screen navigation so the user must go through the screens in the proper order on the way in, but I can't stop the user from exiting the screens out of sequence.  If they drill all the way down to the lowest level (screen 4) but then exit screen 2 then the totals from screen 4 and screen 3 will not be accurate in screen 2.   

    Is there a way to handle this problem or have I painted myself into a corner?      

    Sunday, May 12, 2013 2:21 AM
  • The way that I handle this is by using modal window controls. Instead of creating individual screens for associated data, I add a ModalWindow control to the main screen. Then, instead of code to open a separate screen, I use code to display the ModalWindow control.

    Would that technique help you out?


    Yann Duran
         - Co-Author of Pro Visual Studio LightSwitch 2011
         - Author of the  LightSwitch Central Blog

    FREE Download: Luminous Tools for LightSwitch
    (a Visual Studio productivity extension for LightSwitch)
     
    Click Mark as Answer, if someone's reply answers your question
    Click  Vote as Helpful, if someone's reply is helpful
     
    By doing this you'll help everyone find answers faster.

    Sunday, May 12, 2013 4:01 AM
    Moderator
  • I'm sure it would if I knew how to implement it.  :)

    I've used ModalWindow pickers but never a ModalWindow Control.  There is a ton of logic in each one of the 4 main screens I've designed.  Does you method require that "all" the logic resides in just one screen?  Sounds like a major rewrite.

    Is it possible to programmatically check if one screen is open and not allow a screen to close before another is closed? i.e.  If the user attempts to close screen 2 but screen 3 is still open, then issue a message telling the user to close screen 3 first?  If I can do that I can salvage what I've already developed and then use your method in future projects.

    Ed

    Sunday, May 12, 2013 11:35 AM
  • I've found a way to check what screens are open but I can't figure out how to use it to prevent one screen from being closed before another.  That pesky X is on the tab of each open screen and I have no way of preventing a user from clicking it.

    Any ideas? (I'm screwed)  

     
    Sunday, May 12, 2013 1:21 PM
  • You can cancel a screen closing in the Closing method:

    Private Sub ScreenName_Closing(ByRef cancel As Boolean)
        Dim screens = Me.Application.ActiveScreens _
            .Where(Function(x) _
                (TypeOf x.Screen Is NameOfScreenToCheck) _
            ).ToList
    
        cancel = (screens.Count > 0)
    End Sub



    Yann Duran
         - Co-Author of Pro Visual Studio LightSwitch 2011
         - Author of the  LightSwitch Central Blog

    FREE Download: Luminous Tools for LightSwitch
    (a Visual Studio productivity extension for LightSwitch)
     
    Click Mark as Answer, if someone's reply answers your question
    Click  Vote as Helpful, if someone's reply is helpful
     
    By doing this you'll help everyone find answers faster.

    Monday, May 13, 2013 7:24 AM
    Moderator
  • Thanks Yann

    Can I also send a message to the user explaining why the Screen Closing was canceled?  

    Monday, May 13, 2013 11:51 AM
  • Of course. You can add a call to ShowMessageBox, as you would anywhere else in screen code:

    Private Sub ScreenName_Closing(ByRef cancel As Boolean)
        Dim screens = Me.Application.ActiveScreens _
            .Where(Function(x) _
                (TypeOf x.Screen Is NameOfScreenToCheck) _
            ).ToList
    
        cancel = (screens.Count > 0)
    
        If (cancel = True) Then 
            Me.ShowMessageBox("Your explanation text")
        End If
    End Sub


    Yann Duran
         - Co-Author of Pro Visual Studio LightSwitch 2011
         - Author of the  LightSwitch Central Blog

    FREE Download: Luminous Tools for LightSwitch
    (a Visual Studio productivity extension for LightSwitch)
     
    Click Mark as Answer, if someone's reply answers your question
    Click  Vote as Helpful, if someone's reply is helpful
     
    By doing this you'll help everyone find answers faster.

    Monday, May 13, 2013 3:10 PM
    Moderator
  • Thank you.  I'll try it and let you know.
    Tuesday, May 14, 2013 12:14 AM
  • @Yann

    I tried your code and it appears to work perfectly but only if the data on the screen in question is dirty. If it isn't then Lighswitch (for reasons known only to the Gods) simply closes my MessageBox after about 3 or 4 seconds and then proceeds to close the screen even though the code that is used to trigger the MessageBox is also designed to cancel the close.

    The Me.ShowMessageBox appears to be the ideal solution to my problem but only if I can get the message to wait for a response from the user.  I suppose I could force all the screens to think they needed to be saved and that way I would at least get the message “Do you want to wait”?

    What am I missing???

    Tuesday, May 14, 2013 3:07 AM
  • Ah, you've just reminded me of something that I'd forgotten about. The "3 to 4 seconds" was the key to triggering my memory.

    I don't remember the "reason" for doing it, but I do remember now that this was done on purpose by the LightSwitch team. If I remember correctly, it had something to do with improving perceived performance. Perhaps the behaviour in this particular scenario this is a side-effect of that decision? Maybe someone from the team can comment here?

    I know I personally didn't think it was a good idea. But it's an example of LightSwitch trying to be "helpful", but in cases such as yours, it makes the behaviour had to justify, performance enhancement or not. Nothing should ignore a modal dialog box like that.

    Sorry about leading you astray. In my opinion it should work the way you & I are expecting. While I love how easy LightSwitch can make things most of the time, I don't like it when LightSwitch changes expected behaviour like this.


    Yann Duran
         - Co-Author of Pro Visual Studio LightSwitch 2011
         - Author of the  LightSwitch Central Blog

    FREE Download: Luminous Tools for LightSwitch
    (a Visual Studio productivity extension for LightSwitch)
     
    Click Mark as Answer, if someone's reply answers your question
    Click  Vote as Helpful, if someone's reply is helpful
     
    By doing this you'll help everyone find answers faster.

    Tuesday, May 14, 2013 9:20 AM
    Moderator
  •  

    The overall logic does work and result in exactly the behavior I need but only if the user is fast enough to click the OK button on the MessageBox before the 3 second time limit expires. Reminds me of the old TV game show called "Beat the clock".

    I find it a bit hard to swallow that a team capable of producing a gem like Lightswitch couldn't come up with a better solution to a perceived problem than to simply ignore the fundamental purpose of a function that ordinarily requests/requires an input action by the user. However, the question remains what to do about it.

    Inasmuch as the overall intention is to cancel the "close" of a screen if some other screen is open... and in my case the message is only an ancillary function... then is it possible to nest two tests for the active screens, issuing the message on the first test (but not the cancel) and then the cancel on the second without the message?

        
    Tuesday, May 14, 2013 10:36 AM
  • No, from what I remember, once you click Close, a background process is initiated that waits 3-4 seconds (presumably to allow time for things to be tidied up in user code) then silently kills the screen. Or something like that.

    You'll have to play with what works & what doesn't. All I can do in this instance is give you my (vague) recollection of why what you see is happening.


    Yann Duran
         - Co-Author of Pro Visual Studio LightSwitch 2011
         - Author of the  LightSwitch Central Blog

    FREE Download: Luminous Tools for LightSwitch
    (a Visual Studio productivity extension for LightSwitch)
     
    Click Mark as Answer, if someone's reply answers your question
    Click  Vote as Helpful, if someone's reply is helpful
     
    By doing this you'll help everyone find answers faster.

    Tuesday, May 14, 2013 1:31 PM
    Moderator
  • Your vague recollection is worth more than whatever I can come up with.  :)

    However, in this case I must question the teacher... gulp.

    If the user is fast enough (obviously under 3 seconds) and clicks the OK button on the MessageBox, the "Close" is indeed canceled and the screen remains open.  Would that not indicate that the backround process can be canceled?

      

    • Proposed as answer by EssbiSolutions Wednesday, May 15, 2013 5:56 AM
    • Unproposed as answer by EssbiSolutions Wednesday, May 15, 2013 5:56 AM
    Tuesday, May 14, 2013 1:41 PM
  • Hehe, no problem, question away. :-)

    Yes, you're right. I hereby amend my previous statement

    "then silently kills the screen"

    to

    "then silently kills the screen, unless the user somehow cancels the closing within the 3-4 seconds".

    Again from slightly enhanced vague recollection, I think the purpose was to prevent any long-running "clean-up" processes from delaying the closing of the screen. It was therefore deemed that 3-4 seconds (I'm pretty sure it was 4 seconds actually) was the maximum reasonable time for any clean-up processes when a screen was closing. If anything took longer than that, the screen was silently killed.

    So that fits in with what you've experienced. If the cancel is triggered by a user clicking on a button in your message box, the screen close is cancelled. If the message box isn't closed with the time limit, the screen gets killed.

    Again, I don't agree with this behaviour, but I do understand why it was put in place.


    Yann Duran
         - Co-Author of Pro Visual Studio LightSwitch 2011
         - Author of the  LightSwitch Central Blog

    FREE Download: Luminous Tools for LightSwitch
    (a Visual Studio productivity extension for LightSwitch)
     
    Click Mark as Answer, if someone's reply answers your question
    Click  Vote as Helpful, if someone's reply is helpful
     
    By doing this you'll help everyone find answers faster.

    Wednesday, May 15, 2013 4:24 AM
    Moderator
  • Hi Jyuma1,

    Not sure if this helps but I had a similar problem when calculating totals in a cash book. Pages would appear to have changes made to them when nothing was done? I had a bunch of totals that needed to be calculated when a payment or receipt was entered in the cash book, or when I changed the query start date and end date on the screen. The totals would be calculated depending on the entries retrieved in the tables. I tried doing the calculations with the screen events but couldn't find a good solution. I eventually figured that anything that needed to be calculated should be using the 'Is Computed' method in the entity.

    Even though my screen had a 'start date' and 'end date' that was bound to the query, my calculations were completely independent from what the screen produced.

    I did this by emulating a linq query to what was happening on the screen. For instance, my tables were bound to a start date and an end date using a query in the screens query designer, but I also had an identical query in the Is Compute method on the entity. Below is the query.

    Dim query = From cbp As Payment In Payments
                Where cbp.DateOfPayment >= Me.QueryStartDate AndAlso
    cbp.DateOfPayment <= Me.QueryEndDate
                Select cbp

               result = query.Sum(Function(fTotalPayment) fTotalPayment.TotalPayment)

    This was the entity query in the Is Computed method but the screen query was basically the same. I found that doing my calculations this way seemed to keep the screen a lot happier, and the only thing I needed to worry about was saving changes to the date pickers on the screen when they were changed using the DataWorkSpace.ApplicationData.SaveChanges method. Hope this helps?? Cheers, Tim  

    Wednesday, May 15, 2013 6:23 AM
  • Strangely, the code to cancel the "close" works as expected as long as you don't include the ShowMessageBox anywhere in the closing method.  If I omit the ShowMessageBox and just include the code for checking if another screen is open and if so, cancel the close... that works fine.

    I found a thread where this behavior is discussed at length and a fellow named Andrew offered the following code as a way around the problem.  Apparently if you use "Invoke" to issue a message, it somehow gets around the 3 second rule.  Unfortunately Andrew posted the code in C# and my attempts to convert the code to VB have failed.  Can someone please convert the following code to VB for me? 

    public partial class FoosListDetail

    {

        private bool isExplicitClose = false;

        partial void FoosListDetail_Closing(ref bool cancel)

        {

            if (!isExplicitClose && !this.DataWorkspace.ApplicationData.Details.HasChanges)

            {

                // There are no changes - cancel the built-in save and see if the user really wants to

                // close the dialog...

                cancel = true;

                Microsoft.LightSwitch.Threading.Dispatchers.Current.BeginInvoke(ShowAreYouSureDialog);

            }

            else

            {

                // There are changes - you could show an alternate dialog (similar to above) or rely

                // on the built-in prompt... In this case, we've chosen the latter...

            }

        }

        private void ShowAreYouSureDialog()

        {

            System.Diagnostics.Debug.Assert(!this.DataWorkspace.ApplicationData.Details.HasChanges);

            if (this.ShowMessageBox("Close screen", "Do you really want to close the screen?", MessageBoxOption.YesNo) == System.Windows.MessageBoxResult.Yes)

            {

                // Since the screen is already closed, you can safely go ahead and close the screen...

                this.isExplicitClose = true;

                this.Close(false);

                this.isExplicitClose = false;

            }

        }

    }

    Thank you.

    Ed

    Thursday, May 16, 2013 1:08 AM
  • There's nothing complicated in that code. An online converter should have been able to handle it.

    But here's my translation:

    Imports System.Windows
    Imports Microsoft.LightSwitch.Threading.Dispatchers
    
    Namespace LightSwitchApplication
    
        Public Class MovieList
            Private isExplicitClose As Boolean = False
    
            Private Sub MovieList_Closing(ByRef cancel As Boolean)
                If (isExplicitClose = False) _
                    AndAlso (Me.DataWorkspace.MovieCentralData.Details.HasChanges = False) _
                Then
                    'there are no changes - cancel the built-in save and see 
                    'if the user really wants to close the dialog...
                    cancel = True
                    Current.BeginInvoke(AddressOf ShowAreYouSureDialog)
                Else
                    'there are changes - you could show an alternate dialog (similar to above)
                    ' or rely on the built-in prompt... 
                    'in this case, we've chosen the latter...
                End If
            End Sub
    
            Private Sub ShowAreYouSureDialog()
                If (Me.ShowMessageBox("Close screen" _
                    , "Do you really want to close the screen?" _
                    , MessageBoxOption.YesNo) = MessageBoxResult.Yes) _
                Then
                    'since the screen is already closed, 
                    'you can safely go ahead and close the screen...
                    Me.isExplicitClose = True
                    Me.Close(False)
                    Me.isExplicitClose = False
                End If
            End Sub
    
        End Class
    
    End Namespace

    I've also verified that this "trick" does in fact work.


    Yann Duran
         - Co-Author of Pro Visual Studio LightSwitch 2011
         - Author of the  LightSwitch Central Blog

    FREE Download: Luminous Tools for LightSwitch
    (a Visual Studio productivity extension for LightSwitch)
     
    Click Mark as Answer, if someone's reply answers your question
    Click  Vote as Helpful, if someone's reply is helpful
     
    By doing this you'll help everyone find answers faster.


    Thursday, May 16, 2013 12:41 PM
    Moderator
  • @Yann

    Sorry it took me so long to respond.  Ever shifting priorities took me off the project for a few days.

    The code Andrew provided that you so kindly converted to VB for me worked great.  I did need to make a few modifications in order to issue the "cancel" of one screen based on there being another screen active but that was a relatively trivial modification.

    The code is working almost perfectly but it seems every time I fix one problem a new one sprouts wings.

    I'll create a new thread for the new problem but in case you're wondering what it is... if I click the X on a tab that is not in focus I get a program exception.  Oh good grief.

    Thanks

    Ed

        

    Monday, May 20, 2013 2:26 PM
  • @EssbiSolutions

    Thank you for your help.

    The problem has been resolved using a modified version of some code that was provided by Andrew but I will likely use your suggestions in future code. 

    Ed

    Monday, May 20, 2013 2:31 PM