ViewModelBase - BeginSave and DoSave have Implicit EditLevel = 0 to correctly execute.

ViewModelBase - BeginSave and DoSave have Implicit EditLevel = 0 to correctly execute.

Old forum URL: forums.lhotka.net/forums/t/10630.aspx


jamie.clayton posted on Tuesday, August 23, 2011

G'day,

When binding XAML windows/controls to ViewModelBase, users who edit multiple fileds will cause the edit level to increase by 1 on every edit. If you then try and bind the BeginSave or DoSave method to a button via the TriggerAction it can fail with a message like - "Object is still being edited and can not be saved". I had to debug through to the CSLA codebase to realise why my records were not saving.

I needed to put codebehind the XAML buttons which use the save method, which I didn't believe was the aim of the ViewModelBase BeginSave and DoSave methods.

'   (VB.net example) WPF will remember all the edits prior to saving the data, so we will have to commit all those changes.
            If TypeOf Model Is Csla.Core.IUndoableObject Then
                For index As Integer = 0 To CType(Model, Csla.Core.IUndoableObject).EditLevel - 1
                    Model.ApplyEdit()
                Next
            End If
            
            DoSave()

Recommended change to ViewModelBase c# code in BeginSave and DoSave methods (see //apply changes)

          //apply changes
          var undoable = savable as Csla.Core.ISupportUndo;
          if (undoable != null)
          {
              // ensure the full edit history is applied before saving, so the edit level = 0 prior to saving.
              var undoableobject = (Csla.Core.IUndoableObject)undoable;              
              for (int i = 0; i < undoableobject.EditLevel - 1; i++)
              {
                  undoable.ApplyEdit();
              }             
          }

Your thoughts? 
I noticed that the CSLA 4 video's only seemed to include editing of one BusinessObject field at a time before saving.

JonnyBee replied on Tuesday, August 23, 2011

No, no, no, no

What you are describing here is that BeginEdit is called every time you edit a property. You should fix that issue rather than creating a new fix in DoSave.

Which version of CSLA are you using?

In Csla.Xaml.ViewModelBase, BeginEdit is only called in DoCance()l and the dependency property ModelProperty. When a property is set on your BO in Xaml there should be no calls to BeginEdit, CancelEdit or ApplyEdit.

 

 

jamie.clayton replied on Tuesday, August 23, 2011

Jonny,

Thanks for the heads up. I am using Csla 3.8.2 (Csla.Wpf namespace) + VS 2010 + WPF + .net 4.

I'm just doing direct XAML databinding, but will review the underlying Business object as recommended. The XAML is a wizard that passes the Business object through 4 ViewModels which are bound to different pages in the wizard. Essentially the Wizard control is orchestrating the wizard pages and the code follows the excellent MVVM pattern example from CodeProject, but with a Csla ViewModel approach. http://www.codeproject.com/KB/WPF/InternationalizedWizard.aspx

Will upgrade to csla 3.8.4 and do a full debug test to determine the cause of the issue.

jamie.clayton replied on Tuesday, August 23, 2011

Code Sample that triggered the issue.

''' <summary>
''' Creates a collection of ViewModels that share a common Csla business object (model) between them.
''' </summary>
''' <remarks></remarks>
Public Overrides Sub CreateWizardPages()
 
    '   Load a new payment business object into a shared model for use by multiple pages in a wizard
    SharedModel = Iims.BL.Payment.NewPayment
 
    logger.Trace("Started: Loading Wizard ViewModels into a collection and sharing default payment data with all.")
    logger.Debug("Edit level = {0}."CType(SharedModel, Csla.Core.IUndoableObject).EditLevel)
    Dim vmList As New List(Of IWizardPage)
 
    With vmList
        .Add(New PayRun1PreparationViewModel(SharedModel))
        .Add(New PayRun2SettingsViewModel(SharedModel))
        .Add(New PayRun3ReviewViewModel(SharedModel))
        .Add(New PayRun4OutputViewModel(SharedModel))
    End With
 
 
    Pages = New ReadOnlyCollection(Of IWizardPage)(vmList)
    '   Configure the first page.            
    CurrentPage = Pages(0)
 
    '   Reminder for future wizards that addopt this pattern
    If CType(SharedModel, Csla.Core.IUndoableObject).EditLevel > 0 Then
        logger.Fatal("Edit level = {0}, but should be 0. Please ensure BO.ApplyEdit is completd when populating the ViewModels via a contructor."CType(SharedModel, Csla.Core.IUndoableObject).EditLevel)
    End If
 
    logger.Trace("Finished: Loading Wizard ViewModels.")
 
End Sub
#Region "ViewModel Constructor "
 
        Public Sub New(ByRef value As Iims.BL.Payment)
            MyBase.Model = value
            '   LL: Commit the change to the current view model so the edit level = 0. (thanks Jonny B!)
            MyBase.Model.ApplyEdit
        End Sub
 
#End Region 

Full from Humble Pie.Embarrassed

Copyright (c) Marimer LLC