StrataFrame Forum

Beta 1.6.5.1: ListView enhanced features....

http://forum.strataframe.net/Topic15732.aspx

By Edhy Rijo - 4/17/2008

Hi Trent,

First, thanks for all the enhancements done to the ListView, very impressive Smile

I setup a form to test the ListView and found the following issues:

  • Moving the record pointer in the ToolStrip button does not update/refresh the ListView.
  • Creating a new record or modifying an existing one does not Refresh the ListView with the updated data.

I tested the functionality of the Add/Delete/Edit Objects and they all worked as expected. 

One question, when using the ListView with a child BO, I may need to have buttons for Save/Undo if not using a childform object, would you consider adding a SaveObject/UndoObject as well for this case?

Please take a loot at the video in this link:  http://www.progytech.com/videos/Beta_ListView\Beta_ListView.html

By Trent L. Taylor - 4/17/2008

Moving the record pointer in the ToolStrip button does not update/refresh the ListView.

Not supposed to.  The two are separate.  I might add something so that it auto-navigates from an external source, but for now there is not an issue because it wasn't supposed to move.  The auto-navigation occurs when the item is selected within the list, not the other way around.

Creating a new record or modifying an existing one does not Refresh the ListView with the updated data.

You have to handle the ChildFormResults event and if you want it to requery set the event arg and it will then requery itself:

e.Requery = True
By Trent L. Taylor - 4/17/2008

One question, when using the ListView with a child BO, I may need to have buttons for Save/Undo if not using a childform object, would you consider adding a SaveObject/UndoObject as well for this case?

I will consider it.  This is pretty far out of the norm since your child logic should not generally be separate from the rest of the environment and there may be more than one BO that requires saving, etc.  So this gets more complicated in that sense. 

By Edhy Rijo - 4/22/2008

Trent,

Following the ListView enhancements, in the following code

Private Sub AddNewChildRecord()

     '-- If no business object is attached then there is nothing to do

     If _BusinessObject Is Nothing Then Exit Sub

          '-- Add the new record

          _BusinessObject.Add()

          '-- See if there is a child dialog

          If _ChildForm IsNot Nothing Then

               '-- Call the child form and create the args

               Dim args As New ListViewChildFormResultsEventArgs(_ChildForm.ShowDialog())

               '-- Raise the event

               Me.OnChildFormResults(args)

               '-- See if the list should be requeried

               If args.Requery Then

                    '-- Save off the primary key value

                    Dim reselect As Boolean = True

                    Dim pk As Object = Nothing

                    Try

                         pk = _BusinessObject.CurrentRow(_BusinessObject.PrimaryKeyField)

                    Catch ex As Exception

                         reselect = False

                    End Try

                    '-- Requery the list

                    Me.Requery()

                    '-- Attempt to select the item

                    If reselect Then SelectIndexByPrimaryKey(pk)

               End If

          End If

End Sub

The code that check if the list should be requeried will only run if a ChildForm exist and that may not be case if no ChildForm is used, but there is still the need for the list to be requeried. 

Do you think that the new ListView event ChildFormResults could be raised even if a ChildForm is not being used? or is there a way I can manually raise this event so I can force the code to Requery the view to be run at will?

By Edhy Rijo - 4/25/2008

HI Trent,

Still working with the ListView enhancements, I am having a problem when trying to Edit a record in the ListView this time using a ChildForm w00t I get this error:

BusinessLayerException
  There are no rows in the current data table.  An edit is not allowed.

Source     : MicroFour StrataFrame Business

Stack Trace:
   at MicroFour.StrataFrame.Business.BusinessLayer.Edit(Boolean CheckSecurity)
   at MicroFour.StrataFrame.Business.BusinessLayer.Edit()
   at MicroFour.StrataFrame.UI.Windows.Forms.ListView.EditChildRecord()
   at MicroFour.StrataFrame.UI.Windows.Forms.ListView.HandleObjectClick(Object sender, EventArgs e)
   at System.Windows.Forms.ToolStripItem.RaiseEvent(Object key, EventArgs e)
   at System.Windows.Forms.ToolStripButton.OnClick(EventArgs e)
   at System.Windows.Forms.ToolStripItem.HandleClick(EventArgs e)
   at System.Windows.Forms.ToolStripItem.HandleMouseUp(MouseEventArgs e)
   at System.Windows.Forms.ToolStripItem.FireEventInteractive(EventArgs e, ToolStripItemEventType met)
   at System.Windows.Forms.ToolStripItem.FireEvent(EventArgs e, ToolStripItemEventType met)
   at System.Windows.Forms.ToolStrip.OnMouseUp(MouseEventArgs mea)
   at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
   at System.Windows.Forms.ToolStrip.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativewindow.OnMessage(Message& m)
   at System.Windows.Forms.Control.ControlNativewindow.WndProc(Message& m)
   at System.Windows.Forms.Nativewindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

I don't have any problem when adding new records, and even after adding new records I am able to Edit current records in the listview.

For the ChildForm, I created this form with a SF MaintenanceForm class and added the child BO as usual.

Please see the attached images.

By Trent L. Taylor - 4/25/2008

You probably have a filter applied or something along those lines.  Or, you are one or two forms deep and the business object on the ListView is not translated.  In the case of the latter, manually set the BusinessObject property on the ListView in code after the BOs have been translated.  Thes best place to do this is in the OnLoad method.  The latter will only happen if you are already in a ChildForm that has been translated and are calling another child form (grand child). 

''' <summary>
''' Provide additional load logic
''' </summary>
''' <param name="e"></param>
''' <remarks></remarks>
Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
    MyBase.OnLoad(e)

    '-- Force the ListView to update its reference
    MyListView.BusinessObject = Me.MyBusinessObject
   
End Sub

By Edhy Rijo - 4/25/2008

Trent,

The problem was that I was not filling the Child BO and was fooled by the data in the Child ListView.

In this regard I have a couple of question I need to clarify:

  • When loading the BO with a fill method, which event is the best one to use:
    • Form's Load or
    • BO ParentFormLoading
  • I am using the following method to fill the child BO

    Me.Child_PolicyBO1.FillByParent(Me.InsuredCustomerBO1)

    • Is this the correct method to use to fill a child BO?
  • When filling the ListView I have this code:

    Private Sub lvPolicy_ListPopulating(ByVal e As MicroFour.StrataFrame.UI.ListPopulatingEventArgs) Handles lvPolicy.ListPopulating

         ' Show only child records

         e.Parameters(0).Value = Me.InsuredCustomerBO1.PK_InsuredCustomer

    End Sub

    • Is this the correct way since the data in the ListView represent a child BO?

So far everything is working find and I just LOVE the enhancements done to the ListView, but wants to make sure I am using the proper methods and events to keep moving forward with this project.

Thanks!

By Greg McGuffey - 4/25/2008

Based on my SF/.NET experience thus far...



When loading the BO with a fill method, which event is the best one to use


ParentFormLoading is usually the best. You can use the InitializationPriority to set the order the various BO/lists are loaded. These happen before the form's Load event, so if you need code that depends on loaded lists/BOs, you can then put it in the form Load and you're good to go. I also find that this keeps the code more organized, when dealing with forms with lots of BOs. You then have a small focused method for each BO, rather than a complicated one in the form Load handler.



Is this the correct method to use to fill a child BO?


Well, correct is a strong word. Correct is very dependent on your situation. However, typically, you can use FillByParent or the FillByParentPrimaryKey method. If you use the FillByParent, you can also set the parent BO instance on the child and skip passing the parent BO, which might be easier. I tend use the FillByParentPrimaryKey, but I'm not going to say this is the way it should be done. I tend to have a database point of view (thus think in terms of PKs), rather than objects. I wouldn't be surprised if this changes as I grow more as .NET programmer.



Is this the correct way since the data in the ListView represent a child BO?


This depends on what you are doing on the form. If the child data is only represented on the form via the ListView, then this seems fine (and it appears that you are using the FillByParentPrimaryKey, which is fine too). If you have the child BO on the form also, then you might want to use the CopyDataFrom() method instead for the list view. By using this, you will only make one trip to the db (to fill the child BO), then just copy that data into the listview.



Hope that gets you thinking some more and helps! BigGrin
By Edhy Rijo - 4/25/2008

Greg McGuffey (04/25/2008)
Hope that gets you thinking some more and helps! BigGrin

Thanks Greg, much appreciated.Smile

By Edhy Rijo - 4/25/2008

Trent,

When the ListView shows the ChilForm Dim args As New ListViewChildFormResultsEventArgs(_ChildForm.ShowDialog()) the form is shown as modal as it should, but it is outside the MDI parent form, is there a way to tell the ChildForm to be part of the MDI form?

By Greg McGuffey - 4/25/2008

Thanks Greg, much appreciated.Smile


Any time!
By Trent L. Taylor - 4/27/2008

the form is shown as modal as it should, but it is outside the MDI parent form, is there a way to tell the ChildForm to be part of the MDI form?

Technically, yes...however, this defies the nature of a modal dialog.  You actually can do this, but you will have some other issues and side-effects.  You can provide the owner of the dialog when you call the ShowDialog method on a form.  If not provided, then it will appear in the screen.  Initially we started down this path with our medical application and we backed off and let "nature" take its course and we have been far better off.

To allow this to happen through the ListView and ChildDialog form would require that we add another event for you to provide the owner handle.  I will add this to the list and consider it, but it is one of those things that may not make it if there are more liabilities than benefits.

By Edhy Rijo - 4/27/2008

Trent L. Taylor (04/27/2008)
[quote]Initially we started down this path with our medical application and we backed off and let "nature" take its course and we have been far better off.

To allow this to happen through the ListView and ChildDialog form would require that we add another event for you to provide the owner handle.  I will add this to the list and consider it, but it is one of those things that may not make it if there are more liabilities than benefits.

Thanks for the confirmation.  I will use it as it is now, so there will be not need to add another event unless you want to open up the possiblities.

By Edhy Rijo - 4/29/2008

Hi Trent,

Following my working with the ListView, I noticed that when using the ListView in a One2Many form and there is not record in the parent BO the Add button of the the ListView is still Enabled which will caused an error when clicking this button and the ChildForm will try to update the FK key with no valid record in the parent.

I have been looking at the source and the list events to see where I could put code to disable the cmdAdd button in the list if there is no parent record.  Can you please give me hand here?

By Trent L. Taylor - 4/29/2008

Generally I do not allow access to the Toolstrip item if there is not a parent record available.  The ListView will not always be calling a "child" table.  Even though a ChildFormDialog will be shown, it may be the parent record.  This is something that I generally do not fight as I have a single method that I funnel all form enables and disables through...as well as testing on certain security elements.  I generally call this UpdateFormStates()....but you could call it whatever you want.  Then I call this when in the parent BOs Navigated, CurrentTableRefilled, and IsDirtyChanges events.  In this method I then set things that would be handled by a zero count, etc:

Private Sub UpdateFormStates()
    '-- Establish Locals
    Dim isAddAllowed As Boolean = MyParentBO.Count > 0

    '-- Set controls that require a parent record
    MyAddButton.Enabled = isAddAllowed
End Sub

It is possible to add functionality to do this within the ListView, but it would require that a reference to the parent BO be provided and another property or two.  I will consider this when I make some changes before the next update, but for now this is what I would do.  In fact, this is what I do Smile

By Edhy Rijo - 4/29/2008

Trent L. Taylor (04/29/2008)
It is possible to add functionality to do this within the ListView, but it would require that a reference to the parent BO be provided and another property or two.  I will consider this when I make some changes before the next update, but for now this is what I would do.  In fact, this is what I do Smile

Hi Trent,

Thanks for the suggestions, I implemented them and still the Add button is always enabled not matter what. 

Following the ListView source code I noticed that in the method UpdateObjectStates() is executed at the end of the PopulateListView() method and this method will set the Add button Enabled=True based on the HasPermission() method which will always return True if no Security is used yet (my case).

I would try adding my code in the AfterRequery() Event, but this event is raised before the UpdateObjectStates() method. 

You can try this and verify that the Add button will always be Enabled.

''' <summary>

''' The main method to populate the list view through the population settings

''' </summary>

''' <remarks></remarks>

Private Sub PopulateListView(ByVal ParamArray Parameters As Object())

     '-- Establish locals

     Dim loBO As BusinessLayer

     Dim lnCnt As Integer

     If Me._PopulationDataSourceSettings IsNot Nothing Then

          '-- Stop the processing of messages

          Me.SuspendLayout()

          Me.BeginUpdate()

          '-- Clear the list

          Items.Clear()

          '-- Get the datatable

          loBO = GetFilledBusinessObject(Parameters)

          '-- Cycle through the rows of the datatable and start populating the list view

          For lnCnt = 0 To loBO.Count - 1

               '-- Move to the row we need

               loBO.MoveAbsolute(lnCnt)

               '-- add the item to the list

               Me.Items.Add(CreateListViewItem(loBO))

          Next

          '-- Resume the drawing and processing

          Me.EndUpdate()

          Me.ResumeLayout()

     End If

     RaiseAfterRequeryEvent()

     '-- Update the states of the objects

     UpdateObjectStates()

End Sub

By Greg McGuffey - 4/29/2008

Edhy,



You need to call the UpdateObjectStates() method whenever the monitored state changes. I.e. in this case, as Trent mentioned, you need to call this when the state of the parent BO could change, like when it is loaded, navigated, added, deleted, undone (maybe not all of these, but hopefully you get the idea). You don't need this here necessarily, unless loading the list could also change a monitored state (e.g. you have some object that requires an item selected within the list). Hope that makes sense.
By Greg McGuffey - 4/29/2008

Oops, I've been sick and didn't read your post carefully enough. I thought that looked a lot like SF code Blush



The basic concept still holds though. You're calling it when the parent BO could change. If you need to also handle control states after the list is loaded, just call it after you call Requery():

Me.lvwExample.Requery()

Me.UpdateControlStates()


This should then get called after the UpdateObjectStates() is called and disable anything that is needed, based on the logic in your UpdateControlStates() method (or whatever you're calling it).
By Edhy Rijo - 4/29/2008

Greg McGuffey (04/29/2008)
The basic concept still holds though. You're calling it when the parent BO could change. If you need to also handle control states after the list is loaded, just call it after you call Requery()

Hi Greg,

Thanks for jump in.  Here is my code:

Private Sub InsuredCustomerBO1_Navigated(ByVal e As MicroFour.StrataFrame.Business.NavigatedEventArgs) Handles InsuredCustomerBO1.Navigated

     If Me.InsuredCustomerBO1.Count > 0 Then

          Me.Child_PolicyBO1.FillByParent()

          Me.lvPolicy.Requery()

     End If

     Me.UpdateFormStates()

End Sub

Private Sub InsuredCustomerBO1_CurrentDataTableRefilled() Handles InsuredCustomerBO1.CurrentDataTableRefilled

     Me.UpdateFormStates()

End Sub

Private Sub InsuredCustomerBO1_IsDirtyChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles InsuredCustomerBO1.IsDirtyChanged

     Me.UpdateFormStates()

End Sub

Private Sub UpdateFormStates()

     '-- Establish Locals

     Dim isAddAllowed As Boolean = Me.InsuredCustomerBO1.Count > 0

     '-- Set controls that require a parent record

     Me.cmdAdd_Policy.Enabled = isAddAllowed

End Sub

As you can see I am calling Me.UpdateFormStates in all places suggested by Trent, but the result is the same, the cmdAdd button is always Enabled when there is not record in the Parent BO.

By Trent L. Taylor - 4/29/2008

Greg is still right....there is no reason to even allow the ListView to Requery itself if there is no parent record.  More than likely you already have something in the Navigated event of the parent BO that calls a Refresh on the child list...if there are no parent records, the you can just clear the list and ensure that your form method is called:

if MyParentBo.Count = 0 Then
    MyListView.Items.Clear()
End If

UpdateFormStates()

By Edhy Rijo - 4/29/2008

Trent L. Taylor (04/29/2008)
Greg is still right....there is no reason to even allow the ListView to Requery itself if there is no parent record.  More than likely you already have something in the Navigated event of the parent BO that calls a Refresh on the child list...if there are no parent records, the you can just clear the list and ensure that your form method is called:

Trent, if you look at my previous message, you will notice that I am only calling the ListView.Refresh() method in the ParentBO.Navigated() event.  The ListView is being populated by MicroFour.StrataFrame.Business.BusinessCloneDataType.ClearAndFillFromDefaultView, see the code below.

Private Sub lvPolicy_ListPopulating(ByVal e As MicroFour.StrataFrame.UI.ListPopulatingEventArgs) Handles lvPolicy.ListPopulating

     '-- Use the already populated business object on the form to load the list.

     e.Parameters(0).Value = Me.Child_PolicyBO1

     '-- Tell the business object that will be used in the listview (creates a new instance)

     '-- to clear itself and load from the forms default view on the Customers business object.

     e.Parameters(1).Value = MicroFour.StrataFrame.Business.BusinessCloneDataType.ClearAndFillFromDefaultView

End Sub

This is a bit frustrating BigGrin  As you can see, I am not calling the ListView.Refresh() in any other place other than the ParentBO.Navigated() event and I still believe that the problem is in the ListView code which will be called after all other events has occurred and it is setting the Add button Enabled=True.

I hope you can see this and if possible test it.

By Trent L. Taylor - 4/30/2008

Try this assembly.  It also includes the changes I made to the DateTimePicker.  I will do another build later today and post it on the forum for the 1.6.6 beta that will include both of these changes.
By Trent L. Taylor - 4/30/2008

Edhy,

This won't work until I make some other changes....this is why I was reluctant to mess with it.  Also, in order for this to work, you will have to define the parent relationship and set the ParentBusinessObject properties...but at this point it still won't work.  I will have to add handlers to the parent BO events.

By Edhy Rijo - 4/30/2008

Trent L. Taylor (04/30/2008)
Try this assembly.  It also includes the changes I made to the DateTimePicker.  I will do another build later today and post it on the forum for the 1.6.6 beta that will include both of these changes.

Hi Trent,

Sorry, but this assembly did not fix the problem with the ListView Add button.Angry  It did fix the DateTimePicker problem.

By Trent L. Taylor - 4/30/2008

Actually, I just tested it and it DOES fix the problem.  Your program is not setup right.  You need to create a ParentRelationship on the OrdersBO that is CustomersBO.  Then on the form (after you recompile) set the ParentBusinessObject to the CustomersBO1 on the form....then the final, most important step, and this could be why you have had other issues, set the BusinessObject property on the ListView to the OrdersBO1 on the form.  If you do all of this, as it was intended to work, it will work like you want it to.
By Edhy Rijo - 4/30/2008

Trent L. Taylor (04/30/2008)
Actually, I just tested it and it DOES fix the problem.

Hi again,

Yes IT DOES WORK, sorry but my quick sample project was too quick BigGrin and not setup properly, but after testin in my actual project it did work and then after adding all the missing setp in the quick project it also worked.

If you don't mind, could you tell me what was changed in the source code?

By Trent L. Taylor - 4/30/2008

Sure, I just changed the UpdateObjectStates method to this:

''' <summary>
''' Updates the objects states based on the selected state of the list items and security
''' </summary>
''' <remarks></remarks>
Private Sub UpdateObjectStates()
    '-- Establish Locals
    Dim isSelected As Boolean = Me.SelectedItems.Count > 0
    Dim isAddAllowed As Boolean = HasPermission(_AddObjectSecurityKey)
    '-- Determine if additional logic should be performed to determine the add status
    If (_BusinessObject IsNot Nothing) AndAlso _
       (_BusinessObject.ParentBusinessObject IsNot Nothing) AndAlso _
       isAddAllowed Then
        isAddAllowed = _BusinessObject.ParentBusinessObject.Count > 0
    End If
    If _AddObject IsNot Nothing Then SetObjectEnabledState(_AddObject, isAddAllowed)
    If _EditObject IsNot Nothing Then SetObjectEnabledState(_EditObject, isSelected AndAlso HasPermission(_EditObjectSecurityKey))
    If _DeleteObject IsNot Nothing Then SetObjectEnabledState(_DeleteObject, isSelected AndAlso HasPermission(_DeleteObjectSecurityKey))
End Sub
By Edhy Rijo - 4/30/2008

Thanks Trent.
By Edhy Rijo - 5/6/2008

Trent,

The DeleteChildRecord() method of the ListView is marking the records as deleted which then will require a Save() to actually delete the record.

'-- If we are here, then delete the record

_BusinessObject.DeleteCurrentRow(True)

In my form which uses 3 ListViews in the same fashion I have the need to permanently delete the record when the DeleteChildRecord() is being executed.  I tried adding code in the BO_AfterDelete() event to save this child BO but I am getting an error:

Private Sub Child_PolicyBO1_AfterDelete(ByVal e As MicroFour.StrataFrame.Business.AfterDeleteEventArgs) Handles Child_PolicyBO1.AfterDelete

     If e.RowsAffected > 0 Then

          Me.Child_PolicyBO1.Save()

     End If

End Sub

This is the error:

BusinessLayerException
  An error occurred while saving the data to the server.
IndexOutOfRangeException
  Index 0 is either negative or above rows count.

Source     : MicroFour StrataFrame Business

Stack Trace:
   at System.Data.DataView.GetRow(Int32 index)
   at System.Data.DataView.get_Item(Int32 recordIndex)
   at MicroFour.StrataFrame.Business.BusinessLayer.get_CurrentRow()
   at MicroFour.StrataFrame.Business.BusinessLayer.FilterChildRecords(BusinessLayer ChildBusinessObject)
   at MicroFour.StrataFrame.Business.BusinessLayer.Save(Boolean Transactional, String TransactionKey)
   at MicroFour.StrataFrame.Business.BusinessLayer.Save()
   at IBS_UI.frmInsuredCustomer.Child_PolicyBO1_AfterDelete(AfterDeleteEventArgs e) in E:\Visual Studio 2008 Projects\StrataFrame\Insurance Broker System (SF)\UI\Main Forms\frmInsuredCustomer.vb:line 226
   at MicroFour.StrataFrame.Business.BusinessLayer.AfterDeleteEventHandler.Invoke(AfterDeleteEventArgs e)
   at MicroFour.StrataFrame.Business.BusinessLayer.raise_AfterDelete(AfterDeleteEventArgs e)
   at MicroFour.StrataFrame.Business.BusinessLayer.OnAfterDelete(AfterDeleteEventArgs e)
   at MicroFour.StrataFrame.Business.BusinessLayer.DeleteCurrentRow(Boolean CheckSecurity, Boolean OnlyMarkAsDeleted)
   at MicroFour.StrataFrame.Business.BusinessLayer.DeleteCurrentRow(Boolean OnlyMarkAsDeleted)
   at MicroFour.StrataFrame.UI.Windows.Forms.ListView.DeleteChildRecord()
   at MicroFour.StrataFrame.UI.Windows.Forms.ListView.HandleObjectClick(Object sender, EventArgs e)
   at System.Windows.Forms.ToolStripItem.RaiseEvent(Object key, EventArgs e)
   at System.Windows.Forms.ToolStripButton.OnClick(EventArgs e)
   at System.Windows.Forms.ToolStripItem.HandleClick(EventArgs e)
   at System.Windows.Forms.ToolStripItem.HandleMouseUp(MouseEventArgs e)
   at System.Windows.Forms.ToolStripItem.FireEventInteractive(EventArgs e, ToolStripItemEventType met)
   at System.Windows.Forms.ToolStripItem.FireEvent(EventArgs e, ToolStripItemEventType met)
   at System.Windows.Forms.ToolStrip.OnMouseUp(MouseEventArgs mea)
   at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
   at System.Windows.Forms.ToolStrip.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativewindow.OnMessage(Message& m)
   at System.Windows.Forms.Control.ControlNativewindow.WndProc(Message& m)
   at System.Windows.Forms.Nativewindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

Where should I call the BO.Save() to permanent delete the record?

P.S.

I think that a new property in the ListView to flag the DeleteChildRecord() as OnlyMarkAsDeleted or not, may be an easy way to solve this.

By Trent L. Taylor - 5/6/2008

Your error has less to do with the delete than it does binding it would appear.  Something is trying to reference the CurrentRow property of the BO, thus the error.

To prove it, and to do this yourself, just set the HandleDeleteAction to false and manually delete the record...it should already be on the correct record, etc.

MyBo.DeleteCurrentRow(False)
By Edhy Rijo - 5/6/2008

I did the test as follow and it worked just fine, of course passing the False to the DeleteCurrentRow() method does not requires me to Save() the record :

Private Sub cmdDelete_Policy_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdDelete_Policy.Click

     '-- If no business object is attached then there is nothing to do

     If Me.Child_PolicyBO1 Is Nothing Then Exit Sub

     '-- Ensure the the correct record is selected in case it had been moved by the developer

     Me.Child_PolicyBO1.NavigateToPrimaryKey(Me.lvPolicy.SelectedItems(0).Tag)

     '-- If we are here, then delete the record    

     Me.Child_PolicyBO1.DeleteCurrentRow(False)

      '-- Requery this list

     Me.lvPolicy.Requery()

End Sub

Like I said before I think that a new property in the ListView to flag the DeleteChildRecord() as OnlyMarkAsDeleted or not, will take care of this situation and will not require us to add more code to the form whenever the ListView is used and the Delete action needs to be permanent.

By Trent L. Taylor - 5/6/2008

Edhy,

Let me just point out something when we address a problem...we first try to get to the source of an issue before we add a new feature.   Just because you get an error to go away doesn't mean that the problem is fixed.  Adding a property is not big deal and I have no issue doing this at some point...so just because I don't immediately say, "I will go add this right now!" doesn't mean that it has no merit...it just means that it doesn't get to the crux of the issue.

If you look at your stack trace, something was trying to bind or pull from the CurrentRow...which could be related to the code in the ListView possibly and we may need to setup a test.  Thus I suggested the manual code change to see if something else arose.  I will see if I can reproduce the error, but until I prove the error, I will not add a property.  It is more important for me to understand why there was an error so that something else is not introduced versus just providing a fix that may prevent the error but we don't know why.

By Edhy Rijo - 5/6/2008

Trent,

Just because you get an error to go away doesn't mean that the problem is fixed.

I do understand what you are saying and respect your policies.  But so we are in the same page here let me clarify the problem a little bit:

  • The ListView enhancement is in beta and I am using this control a lot.
  • When testing the Delete functionality I noticed that the record was only marked as deleted, and could not find a way to change this behavior without disabling the HandleDeleteAction and adding a custom code for all ListView used in this form.
  • Since the mark as deleted will required a call to the BO.Save() to permanently delete the record, then I tried to do that and got the error reported.
  • Based on the test you asked me to do, by calling the BO.DeleteCurrentRow(False) will work fine, then I make the suggestion to add the property to control how the DeleteCurrentRow will behave.

I am trying to move forward with this project and have not problem to take the time to investigate any problem, in fact I spend a lot of time in the forums before posting to try to get answers to my stituations, but I do really need to be ready to show this project to the customer anytime soon.  So for now I will simply add a common DeleteRecord method for my ListViews.Hehe

By Edhy Rijo - 5/21/2008

Hi Trent,

I have another situation with the ListView, I guess BigGrin

I am using the HandleAddAction = True to let the ListView calling the ChildForm when adding.  I added the code below to cancel the Adding if the Save() of the parent BO is not Success, but even though I am setting the e.Cancel = True, the ChildForm is being shown.  Am I using the correct logic here?

Private Sub Child_PolicyBO1_BeforeAddNew(ByVal e As MicroFour.StrataFrame.Business.BeforeAddNewEventArgs) Handles Child_PolicyBO1.BeforeAddNew

     If Me.InsuredCustomerBO1.IsDirty Then

          If Me.Save() <> MicroFour.StrataFrame.Data.SaveUndoResult.Success Then

               e.Cancel = True

          End If

     End If

End Sub

P.S.

FYI, since the ListView enhancements are still in beta, I keep posting in this thread to keep everything related to this in one place, if you prefer I can post this kind of stuff in a separate thread.

By Trent L. Taylor - 5/21/2008

That cancel flag doesn't logically tie into the BOs BeforeAddNew...so the cancel will not have any affect in this case.  That Cancel only has to do with adding a new row to the BO...and FYI...saving within that event could come back and slap you at some point...this is generally never a good idea.

However, it might not be a bad idea to have a BeforeShowChildDialog event on the ListView so that you could cancel it if you like.

By Edhy Rijo - 5/21/2008

Trent L. Taylor (05/21/2008)
...and FYI...saving within that event could come back and slap you at some point...this is generally never a good idea.

Point taken, then where to put the save code then?  In the ChildForm I am making a reference to the ParentBO PK field value which will be -1 if not saved and brake the code.  Any suggestion?

So However, it might not be a bad idea to have a BeforeShowChildDialog event on the ListView so that you could cancel it if you like.

That sounds good. Hehe

By Trent L. Taylor - 5/21/2008

Point taken, then where to put the save code then?  In the ChildForm I am making a reference to the ParentBO PK field value which will be -1 if not saved and brake the code.  Any suggestion?

This is the whole point of a parent relationship.  You SHOULD be able to create a child record on a parent that doesn't yet have a PK yet.  SF will automatically persist the parent's PK to the child once the parent gets a PK.  So you should be saving these records at the same time.  It isn't fair to require the end-user to save a parent record before a child gets created.  All of this should be able to be done at the same time.

In one maintenance form that I recently finished, I have 11 child and grandchild records that could be created on a new record without ever first requiring a parent record to first be saved.  So that is the first thing I would rework versus forcing the save of the parent.

By Edhy Rijo - 5/21/2008

Trent L. Taylor (05/21/2008)
In one maintenance form that I recently finished, I have 11 child and grandchild records that could be created on a new record without ever first requiring a parent record to first be saved.  So that is the first thing I would rework versus forcing the save of the parent.
  • So basically all child BO should be Saved when clicking the Save button in the Parent Form? 
  • I guess that if I select Undo from the toolbar that all child data will be undoed?

Guess I am still thinking as a VFP developer BigGrin

By Trent L. Taylor - 5/21/2008

So basically all child BO should be Saved when clicking the Save button in the Parent Form?

Yes.  All Save and Undo logic should occur from a single point (not the child form Smile ) but the most common place would be the Parent form or container.

I guess that if I select Undo from the toolbar that all child data will be undoed?

Yup...you got it Smile

By Edhy Rijo - 5/22/2008

Hi Trent,

After your post, I when to check the SF Samples and after reviewing the ChildDialogFormSample.sln I think I now have a better understanding of this concept of drilling down child forms and having control of all the savings from a single point. 

I must admit that it still scares me a bit Cool drilling down several child forms without saving and potentially undo some data that the end user may not wanted to undo, but I guess is just a matter of understanding the design purpose of the ChildFormDialog control.

I'll keep you posted Hehe

By Trent L. Taylor - 5/22/2008

Really, we didn't even do that in VFP (bad word, I know BigGrin).  We used table buffering and didn't commit the changes until down stream.  Granted, it was nowhere as clean as using the BOs and disconnected data, but the concept was really the same.
By Edhy Rijo - 5/22/2008

Trent,

Refactoring my child forms, i have a situation with my current code in the ChildForm Save button:

Private Sub cmdSave_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdSave.Click

     '-- Use the form' Save method to force the Broken Rules to be check and show the Broken Rule InfoBox if needed.

     If Me.Save() <> MicroFour.StrataFrame.Data.SaveUndoResult.Success Then

          Exit Sub

     ElseIf Me.Save() = MicroFour.StrataFrame.Data.SaveUndoResult.Success Then

          Me.DialogResult = Windows.Forms.DialogResult.OK

     Else

          Me.PolicyBO1.Undo(MicroFour.StrataFrame.Business.BusinessUndoType.CurrentRowOnly)

          Me.DialogResult = Windows.Forms.DialogResult.Cancel

     End If

End Sub

As you can see, I am using the Form's Save() to force the Broken Rules check and show the InfoBox.  But intent to refactor it to simply check for broken rules and return a DialogResult.OK as it should be.

In the CustomerNodeEditor form this code is being used for the same reason:

Private Sub cmdSave_Click(ByVal sender As Object, ByVal e As EventArgs) Handles cmdSave.Click

     '-- Check the rules on the business object. We are not going to save

     '-- until we get all the way back to the parent form. A save could

     '-- be executed here if necessary, however, since this is a child

     '-- form, the parent form usually controls the saves. The true

     '-- will show the InfoBox error window if the rules are broken.

     If CustomerNotes.CheckRulesOnRow(True) Then

          Me.DialogResult = System.Windows.Forms.DialogResult.OK

     End If

End Sub

The CheckRulesOnRow(True) is not working in a sense that is not showing the required fields nor showing the InfoBox to inform the user that there is a problem with that record, but it is not entering the IF condition which mean that it is partially working.

How can I deal with this situation since in all my ChildForms I need to check for broken rules defined in the BO class?

By Trent L. Taylor - 5/23/2008

Just call the CheckRules method, not the CheckRulesOnRow.  For example:

If MyBo.CheckRules(True) Then
    DialogResult = OK
End If

All of the error providers will then work, however, the InfoBox will not appear and this is by design.  That logic is placed in the Save() of the BaseForm and creates a collection list...however, you can add this yourself very easily in your BaseBO.  Just handle the BusinessRulesChecked event of the BaseBO, and you will probably want to have a property that determines if the InfoBox will be shown when there are broken rules (this too would be on your BaseBo).  The only other property that you may consider adding or testing for is the parent form (or which ever form you may want the InfoBox to show within).  But you would then call the InfoBox like this:

If e.CountOfBrokenRulesAdditionalRows > 0 Then
    InfoBox.ErrorBox(...)
Else
    InfoBox.ErrorBox(...)
End If

You can look at the BaseForm in the Business assembly of the source to see what we do in that dialog.

By Edhy Rijo - 6/24/2008

Hi Trent,

In the latest update 1.6.6 a new event BeforeChildFormExecuted was added to the ListView control.  I noticed that in the ListViewBeforeChildExecuteEventArgs there is no Cancel property to cancel any action handled by the ListView control.

Would it be necessary to add the e.Cancel property, or is there any other way to abort any of the 3 actions allow in the ListView (Add/Edit/Delete)?

By Trent L. Taylor - 6/24/2008

Oh, I suppose we could add a cancel parm.  It was created so that there could be a centralized localization for pre-child form initialization code.  Technically the action is already in progress (Delete would actually never hit this event).  So either an Add or Edit will already be in progress.  Adding a cancel shouldn't be too big of a deal, so I will add this to the list.
By Guillermo Vilas - 8/20/2008

I think the ListViewBeforeChildExecuteEventArgs event would be a good place to add extra params to send to the Child Form.
By Edhy Rijo - 9/23/2008

Hi Trent,

Trent L. Taylor (06/24/2008)
Oh, I suppose we could add a cancel parm.  It was created so that there could be a centralized localization for pre-child form initialization code.  Technically the action is already in progress (Delete would actually never hit this event).  So either an Add or Edit will already be in progress.  Adding a cancel shouldn't be too big of a deal, so I will add this to the list.

I really have the need to be able to cancel out showing the childform dialog from the listview BeforeChildFormExecuted, I have not find a way to do this in a form without having to have all the listview code in the form to manage it. 

Have you consider adding an e.Cancel parameter?

By Trent L. Taylor - 9/24/2008

Done, it will be in the next update.  We will post a 1.6.7 beta build in the next week or two, but this will be included.  Here is the update:

The ListView BeforeChildFormExecuted event now has a CancelAction parm on the event argument which allows several options.  None, which would continue as normal, cancel the ChildFormDialog execution only, or cancel the dialog execution and undo any data changes related to the currently executed action (i.e. if adding a new record the new record would not be added).  This gives the best of all worlds in regards to the potential needs to cancelling an automated ChildFormDialog execution.
By Edhy Rijo - 9/24/2008

Excellent!  this much more than what I asked.

Thanks Tongue