Creating a Custom Add or Edit Dialog (Sheel Shah)

Creating a Custom Add or Edit Dialog (Sheel Shah)

Rate This
  • Comments 26

LightSwitch provides built-in functionality quickly add or edit data in a grid or list.  Unfortunately this modal dialog is not customizable.  In more complex data entry scenarios, the built-in dialogs may not fulfill your customer's needs.  There are a couple of options in this situation.

  1. Provide specific screens for adding/editing records.  For extremely complex data entry or manipulation scenarios, this may be the best option.  However, your application will need to manually manage refreshing data on the list or grid screens.
  2. Replace the built-in modal dialog with your own.  This is ideal for quick edit or add functionality but does require a bit of code to achieve.

This article will describe how to provide a customized dialog for adding or editing records in a grid.

Getting Setup

Create a new LightSwitch application.  Add a Customer table with the following fields:

  • FirstName (String, Required)
  • MiddeInitial (String, Not Required)
  • LastName (String, Required)
  • PhoneNumber (Phone Number, Required)

clip_image002

Create a new Editable Grid screen for Customers.

clip_image002[9]

Define the Modal Window

On the Editable Grid screen,  right click on the top-most item and select "Add Group".

clip_image002[11]

On the group, select the dropdown next to "Rows Layout" and change this to "Modal Window".   Select the "Add"  button underneath the modal window and choose Selected Item.  This will display the currently selected customer in the modal window.   This view can now be customized as necessary.

clip_image003

By default, a modal window group will display a button on the screen.  This button can be used to launch the window.  In our scenario, we don't want this default button display - instead, we'd like to launch the modal window whenever the user clicks the Add or Edit button in a list or grid.   Select the modal window and in the properties grid set the "Show Button" property to false.  We can also change the name of the group to CustomerEditDialog.

clip_image004

Modifying the Grid’s Add/Edit Functionality

We can now override the grid's add and edit buttons to instead launch our modal window.   Open the Command Bar for the data grid, right click on the "Add…" button and select "Override Code".

clip_image002[13]

Add the following code for the Execute and CanExecute methods.  This code will add a new record to the collection and open the modal window. 

Private Sub gridAddAndEditNew_CanExecute(ByRef result As Boolean)

    result = Me.Customers.CanAddNew()

End Sub

 

Private Sub gridAddAndEditNew_Execute()

    Dim newCustomer As Customer = Me.Customers.AddNew()

    Me.Customers.SelectedItem = newCustomer

    Me.OpenModalWindow("CustomerEditDialog")

End Sub

Similarly, right click on the "Edit…" button in the screen editor and select "Override Code"  Add the following code.

Private Sub gridEditSelected_CanExecute(ByRef result As Boolean)

    result = Me.Customers.CanEdit AndAlso Me.Customers.SelectedItem IsNot Nothing

End Sub

 

Private Sub gridEditSelected_Execute()

    Me.OpenModalWindow("CustomerEditDialog")

End Sub

Test the Application

Run the application and click on the Add or Edit buttons in the Editable Customer Grid.  This should launch our custom dialog.

clip_image001

Unfortunately, the dialog we've created is quite limited in functionality.  It does not include the Ok and Cancel buttons of the built-in dialog.  It also does not update its title based on whether it is editing or creating a customer.

Providing Additional Functionality

In the screen designer, right click on the Modal Window and select "Add Button...".     Choose "New Method" and name the method EditDialogOk.  In the property sheet, change the Display Name of the button to just Ok.

clip_image002[15]

clip_image002[17]

Add a similar button called EditDialogCancel.   Again, change the display name of the modal window to just Cancel.

clip_image002[19]

Double click on the Ok button to add some code.   We will add some code to close the modal dialog when either Ok or Cancel is pressed.

Private Sub EditDialogOk_Execute()
    Me.CloseModalWindow("CustomerEditDialog")
End Sub
 
Private Sub EditDialogCancel_Execute()
    Me.CloseModalWindow("CustomerEditDialog")
End Sub

This code simply closes the dialog when the user presses Cancel.   This is not ideal.    When Cancel is pressed, we'd like new records removed and changes to existing records reverted.  This is possible, but requires a fair bit of code.  The following helper class will provide this functionality.

Public Class DialogHelper
    Private _screen As Microsoft.LightSwitch.Client.IScreenObject
    Private _collection As Microsoft.LightSwitch.Client.IVisualCollection
    Private _dialogName As String
 
    Private _isEditing As Boolean = False
    Private _entity As IEntityObject

Public Sub New (ByVal visualCollection As Microsoft.LightSwitch.Client.IVisualCollection,
                    ByVal dialogName As String)
        _screen = visualCollection.Screen
        _collection = visualCollection
        _dialogName = dialogName
    End Sub
 
    Public Sub InitializeUI()
            'This code may not work in Beta 2.  Fixed in final release.  
    
AddHandler
_screen.FindControl(_dialogName).ControlAvailable,
            Sub(sender As Object, e As ControlAvailableEventArgs)
                Dim childWindow As System.Windows.Controls.ChildWindow = e.Control
         AddHandler childWindow.Closed, 
Sub(s1 As Object, e1 As EventArgs)
                        If _entity IsNot Nothing Then
          DirectCast(_entity.Details, IEditableObject).CancelEdit()
                        End If
                    End Sub
            End Sub
    End Sub
 
    Public Function CanEditSelected() As Boolean
        Return _collection.CanEdit() AndAlso (Not _collection.SelectedItem Is Nothing)
    End Function
 
        Public Function CanAdd() As Boolean
            Return _collection.CanAddNew()
        End Function
        Public Sub AddEntity()
            _isEditing = False
            _collection.AddNew()
            _screen.FindControl(_dialogName).DisplayName = "Add " + 
_collection.Details.GetModel.ElementType.Name
            BaseOpenDialog()
        End Sub
 
        Public Sub EditSelectedEntity()
            _isEditing = True
            _screen.FindControl(_dialogName).DisplayName = "Edit " + 
_collection.Details.GetModel.ElementType.Name
            BaseOpenDialog()
        End Sub
 
        Private Sub BaseOpenDialog()
            _entity = _collection.SelectedItem()
            If _entity IsNot Nothing Then
Dispatchers.Main.Invoke(Sub()
                   DirectCast(_entity.Details, IEditableObject).EndEdit()
   DirectCast(_entity.Details, IEditableObject).BeginEdit()
                End Sub)
                _screen.OpenModalWindow(_dialogName)
            End If
        End Sub
 
        Public Sub DialogOk()
            If _entity IsNot Nothing Then
Dispatchers.Main.Invoke(Sub()
                   DirectCast(_entity.Details, IEditableObject).EndEdit()
              End Sub)
         _screen.CloseModalWindow(_dialogName)
            End If
        End Sub
 
        Public Sub DialogCancel()
            If _entity IsNot Nothing Then
                Dispatchers.Main.Invoke(Sub()
                     DirectCast(_entity.Details, IEditableObject).CancelEdit()
                End Sub)
                If _isEditing = False Then
                    _entity.Delete()
                End If
                _screen.CloseModalWindow(_dialogName)
            End If
        End Sub
End Class

Using the Helper Class

First, we'll need to add the class to our project.  Right click on the application in the solution explorer and select "View Application Code (Client)".   Paste the code from above beneath the Application class.    This will also require importing the Microsoft.LightSwitch.Threading namespace.

Imports Microsoft.LightSwitch.Threading
Namespace LightSwitchApplication
 
    Public Class Application
 
    End Class
 
    Public Class DialogHelper
        'Code from above
    End Class
End Namespace

This helper library requires a reference to an additional library.  Select the application in Solution Explorer and switch to File View.   Right click on the Client application and select "Add Reference".   Select the "System.Windows.Controls" component and press Ok.

On our existing grid screen for Customer, we'll need to initialize and use this helper class.  The code for many of our existing methods is replaced by calls to the helper class.  The final code for our screen is shown below.

Public Class EditableCustomersGrid
    Private customersDialogHelper As DialogHelper

Private Sub
EditableCustomersGridOld_InitializeDataWorkspace(
saveChangesTo As System.Collections.Generic.List(Of Microsoft.LightSwitch.IDataService))
            customersDialogHelper = New DialogHelper(Me.Customers, "CustomerEditDialog")
    End Sub
 
        Private Sub EditableCustomersGridOld_Created()
            customersDialogHelper.InitializeUI()
        End Sub
 
        Private Sub gridAddAndEditNew_CanExecute(ByRef result As Boolean)
            result = customersDialogHelper.CanAdd()
        End Sub
 
        Private Sub gridAddAndEditNew_Execute()
            customersDialogHelper.AddEntity()
        End Sub
 
        Private Sub gridEditSelected_CanExecute(ByRef result As Boolean)
            result = customersDialogHelper.CanEditSelected()
        End Sub
 
        Private Sub gridEditSelected_Execute()
            customersDialogHelper.EditSelectedEntity()
        End Sub
 
        Private Sub EditDialogOk_Execute()
            customersDialogHelper.DialogOk()
        End Sub
 
        Private Sub EditDialogCancel_Execute()
            customersDialogHelper.DialogCancel()
        End Sub
 End Class

Run the Application

Run the application again.  Click on the "Add" button for the grid.  This will open our modal dialog with the title appropriately updated.  If we click on the "Cancel" button, the newly added record will be removed.  Similarly,  if a record is being edited and "Cancel" is clicked, the changes will be reverted.

clip_image001[6]

While a little involved, this technique can be applied to any of your other screens.  If you have more than one list on a screen, multiple custom dialogs can be added.   The DialogHelper class utilizes some of the more advanced API options available within LightSwitch.  We’re also planning on wrapping this sample into a screen template that will automatically generate the modal window and code that is needed.

- Sheel

Leave a Comment
  • Please add 7 and 3 and type the answer here:
  • Post
  • Dear Sheel,

    thanks for the solution. I am very new to Lightswitch and I have a question to your solution:

    You have a Private Sub EditableCustomersGridOld_InitializeDataWorkspace, why "Old" and what is it for, and where or when will the customersDialogHelper ever be created?

    many regards

    OOps

  • Really very useful, thank you.

  • Sheel,  

    Your  DialogHelper is a great solution for modal window implementation. I have used it in a couple projects.  Have you experienced any issue in VS 2012? I had a project that worked correctly in VS 2010, but after migrating to VS 2012, When I load a screen with the DialogHelper implementation  I receive the following error:

       An error occurred while running the screen.

       Error details: Object reference not set to an instance of an object.

    Debugging, it happens right after exiting from the screens IntializeDataWorkspace Sub,  My code is:

           Private MWPersonExpenseDialogHelper As DialogHelper

           Private MWPersonMileageDialogHelper As DialogHelper

           'InitializeDataWorkspace -sets up Modal Windows for both PersonExpensesFiltered and Person_Mileage

           Private Sub ListPersons_PersonExpenses_InitializeDataWorkspace(saveChangesTo As List(Of Microsoft.LightSwitch.IDataService))

               'Setup dialogs for both modal windows

               MWPersonExpenseDialogHelper = New DialogHelper(Me.PersonExpensesFiltered, "MWPersonExpense")

               MWPersonMileageDialogHelper = New DialogHelper(Me.Person_Mileage, "MWPersonMileage")

               'Initialize PEPagingValue

               'Me.PEPagingValue = Me.Details.Properties.PersonExpensesFiltered.PageSize

           End Sub

           'Private Sub ListPersons_PersonExpenses_Created()

           '    'Initialize Modal Windows

              MWPersonExpenseDialogHelper.InitializeUI()

              MWPersonMileageDialogHelper.InitializeUI()

           'End Sub

    I am trying to utilize two modal windows on this screen, but i have tried reducing it to one, same error.

    I can't seem to determine what Object LS is complaining about. Either or both of the MWPerson... objects are instantiated after the return, but as soon as I step out of the End Sub from InitializeDataWorkspace. I get the error mentioned above.

    Hoping you can shed some light on the issue.

    Thanks in advance.

    LThibx

  • Hi,

    If you add an entity, click OK, then edit it but click cancel then the entity disapears. This is certainly because the new entity was not saved before editing it but this is a common scenario. Any suggestions on how to prevent it from happening?

    Tks.

    François

  • I get the same Object Reference Not Set as  LThibx.  Has anyone figured out why?

  • As someone who is new to LightSwitch, I found this too difficult to follow. Adding a new record and clicking OK is easy -- the difficulty arises when trying to cancel the add when the user clicks the close button, or presses the escape or return key.

    The solution I found is to add a new customer ONLY if the user clicks OK. To do this, add data items to the screen which duplicate the fields for a new customer. In the screen designer, create new data items tempFirstName, tempMiddleInitial, tempLastName and tempPhoneNumber with the appropriate types. Then, instead of dragging the SelectedItem (of type customer) into the modal window layout container, drag these four separate data items instead. You can then use the following code.

    Private Sub gridAddAndEditNew_Execute()

              Me.OpenModalWindow("CustomerAddDialog")

    End Sub

    Private Sub OKButton_Execute()      

               NewCustomer = New Customer()

               NewCustomer.FirstName = TempFirstName

               NewCustomer.MiddleInitial = TempMiddleInitial

               NewCustomer.LastName = TempLastName

               NewCustomer.PhoneNumber = TempPhoneNumber

               Me.CloseModalWindow("CustomerAddDialog")

    End Sub

    Private Sub CancelButton_Execute()

               Me.CloseModalWindow("CustomerAddDialog")

    End Sub

    If you have only a handful of fields this works pretty well. If there are a lot of fields, it could be pretty tedious.

  • While looking for a solution for a Custom Edit/Add dialog I came across a cleaner method for canceling changes; see lightswitchhelpwebsite.com/.../LightSwitch-ndash-Modal-Add-and-Edit-Windows.aspx.

    I further modified the code to only discard changes for the current instance. In the for loop test for the matching record using the entities primary key values.

    partial void CancelChanges_Execute()

           {

               foreach (COLLECTIONS_COM_DFLT_ACTION typeDisposition in this.DataWorkspace.GenAdmin.Details.GetChanges().ModifiedEntities.OfType<COLLECTIONS_COM_DFLT_ACTION>())

               {

                   if (typeDisposition.COL_TYPE == TypeDisposition.SelectedItem.COL_TYPE && typeDisposition.COL_DISPOSITION == TypeDisposition.SelectedItem.COL_DISPOSITION && typeDisposition.RECORD_SOURCE == TypeDisposition.SelectedItem.RECORD_SOURCE)

                   {

                       typeDisposition.Details.DiscardChanges();

                   }

               }

               foreach (COLLECTIONS_COM_DFLT_ACTION typeDisposition in this.DataWorkspace.GenAdmin.Details.GetChanges().AddedEntities.OfType<COLLECTIONS_COM_DFLT_ACTION>())

               {

                   if (typeDisposition.COL_TYPE == TypeDisposition.SelectedItem.COL_TYPE && typeDisposition.COL_DISPOSITION == TypeDisposition.SelectedItem.COL_DISPOSITION && typeDisposition.RECORD_SOURCE == TypeDisposition.SelectedItem.RECORD_SOURCE)

                   {

                       typeDisposition.Details.DiscardChanges();

                   }

               }

               this.CloseModalWindow("TypeDispoEditDialog");

           }

  • I used code on cansel button:

    Me.TablePaymentList.SelectedItem.Delete()

               Me.CloseModalWindow("AddEditList")

    works ok

  • Great Job

    thanks a lot

  • I use VS2012 and implemented this Function in a Project.

    When I run the EditableGrid with this Function I get the Error "NullReferenceException was unhandled by user code" in the follwoing Sub:

    Private Sub gridAddAndEditNew_CanExecute(ByRef result As Boolean)

               result = C3DDialogHelper.CanAdd()

    End Sub

    I get the same Error also in the following Sub:

    Private Sub gridEditSelected_CanExecute(ByRef result As Boolean)

               result = C3DDialogHelper.CanEditSelected()

    End Sub

    Have anyone a tip what I need to do to fix this?

    Thanks

    Marcel

  • Is there a way to pre-populate the new model with values from the currently selected row in the grid? The scenario is that when client creates a new record they want the model to pre-populate some values from the currently selected row in the grid to save them from having to reenter them.

    Thanks

Page 2 of 2 (26 items) 12