Bug Report B134492
Visible to All Users

New item row loses focus when PropertyChanged + ListChanged (with Reset ) events are fired

created 16 years ago

I'm having a problem with a simple EditableRootParent (Invoice) which has an EditableChildList (InvoiceDetails) which is bound to a form with a couple of text boxes for the root and a DataGridView or devExoress for the children.
The purpose is to calculate the total for the Invoice, when adding or modifying InvoiceDetails in the grid.
The problem is that when adding a new line in the XtraGrid, the new item row loses its focus.
When you put the code OnPropertyChanged("Total"); from the method ReclacTotal from the class Invoice in comment the grid from DevExpress behaves as expected, but then the Total will not be updated on the form.
class Invoice
    private void RecalcTotal()
    {
      decimal total = 0;
      foreach (InvoiceDetail tmp in this.InvoiceDetails)
      {
        total += tmp.SubTotal;
      }
      //We need to update the Total property
      //Total = total; ==> this is the normal way to do this ,
      //but for debugging purposes I do it in the following way:
      LoadProperty<decimal>(TotalProperty, total);
      OnPropertyChanged("Total"); //DevExpress Putting this in cmment and the new item row from the DevExpress grid keeps it focus.
    }
Steps to Reproduce:
In the example you can find at the top a DataGridView and at the bottom a devexpress grid.
When you execute the following steps with the devexpress grid:

  1. Create a new line by filling in "Product2" in the column ProductName
  2. Click Tab or -> (arrow right) to go to the next cell
  3. Fill in 1 in the column Quantity
  4. Click Tab or -> (arrow right) to go to the next cell
  5. Fill in 10 in the column UnitPrice
  6. when now clicking Tab or -> (arrow right) the row is been committed and loses his focus. The first row gets the focus.
    Expected Results:
    When you execute the following steps with the DataGrid from Microsoft:
  7. Create a new line by filling in "Product2" in the column ProductName
  8. Click Tab or -> (arrow right) to go to the next cell
  9. Fill in 1 in the column Quantity
  10. Click Tab or -> (arrow right) to go to the next cell
  11. Fill in 10 in the columnUnitPrice
  12. when now clicking Tab or -> (arrow right) the cell SubTotal becomes the active cell.
Show previous comments (11)

    I’ve tested your solution and this is working. Thanks for it. Thanks for the attachment.
    I’ve looked also at the source code to find the reason why?
    So when are modifying the column Quantity or UnitPrice of an InvoiceDetail, the property Total of the Invoice will be recalculated and changed automatically in the business layer, and this property Total will raise a PropertyChanged event so that the UI can adapt.
    So I was wondering why there is a different behavior when adding a row or modifying a row in the grid.
    While modifying a row, the grid has a behavior as I suspect.
    While adding a row, the added row will be committed automatically and the first line in the grid gets the focus. So the user can’t undo his changes anymore and the newly added row loses his focus.
    This is due to the fact of the following code in DevExpress.Data.Helpers
    public override void OnRestoreEnd() {
         base.OnRestoreEnd();
         if((this.groupCount == 0 || this.foundCurrentRow == DataController.InvalidRow) && Controller.GroupedColumnCount > 0 && Controller.GroupRowCount > 0) {
              if(Controller.CurrentControllerRow != CurrencyDataController.FilterRow)
                   Controller.CurrentControllerRow = Controller.GroupInfo[0].Handle;
         } else {
              if(this.foundCurrentRow == DataController.InvalidRow) this.foundCurrentRow = 0;
              if(Controller.KeepFocusedRowOnUpdate)
                   Controller.CurrentControllerRow = this.foundCurrentRow;
         }
    }
    So in the case we are adding a row the variable foundCurrentRow will have DataController.InvalidRow as value.
    So while adding a row, this method sets the foundCurrentRow to 0 (the first row in the grid).
    In the case of modifying a row the variable foundCurrentRow will not be set to 0.
    So if you leave out this part of code
    if(this.foundCurrentRow == DataController.InvalidRow) this.foundCurrentRow = 0;
    the grid will behave correctly.
    Can you look with the developper team?
    In the solution you’ve send you are short circuiting the code and the method OnRestoreEnd will not be called.
    This short circuit you can find in the method RaiseOnBindingListChanged in DevExpress.Data.CurrencyDataController
    protected internal override void RaiseOnBindingListChanged(ListChangedEventArgs e) {
         if(ValidationClient.BoundControl != null && ValidationClient.BoundControl.InvokeRequired) {
              if(!_DisableThreadingProblemsDetection) {
                   ValidationClient.BoundControl.BeginInvoke(new MethodInvoker(ThrowCrossThreadException));
                   ThrowCrossThreadException();
              }
              ValidationClient.BoundControl.BeginInvoke(new ListChangedDelegate(OnBindingListChanged), new object[] { e });
              System.Threading.Thread.Sleep(1);
              return;
         }
         RaiseOnBeforeListChanged(e);
         try {
              if(e.ListChangedType == ListChangedType.Reset && IsCurrencyResetEventLocked && IsNewItemRowEditing) return;
              OnBindingListChanged(e);
              if(this.finishingNewItemRowEdit) return;
              CurrentControllerRow = CurrentControllerRow;
         }
         finally {
              RaiseOnListChanged(e);
         }
    }
    In the solution you are given the variable IsCurrencyResetEventLocked will be set to true by calling LockCurrencyResetEvent, which will be called by the following code
        private void myGridView1_ShownEditor(object sender, EventArgs e)
        {
            MyGridView view = (MyGridView)sender;
            ((MyViewDataController)view.DataController).LockResetEvent();
        }
    So would it be possible to look at the solution I describe with the developper team. Because in my opion the grid should have the same behavior in add or modify modus, because in both cases you’ll have ListChangedType.Reset.

    DevExpress Support Team 16 years ago

      Hello,
      It's very risky to change that code. All those flags and checks were added to support the XtraGrid's functionality in various binding scenarios and situations. The developer who wrote that code is currently on vacation. Your issue report will remain open for two weeks, until the developer comes back and analyzes the problem. Sorry, but this time we can't promise that your proposal will be included in our code. You may wish to modify the source code of our components on your system, rebuild the assemblies, and test them. You can also use the solution with inherited classes, suggested above.
      Thanks,
      Nick

      DevExpress Support Team 16 years ago

        Hello,
        It's been decided not to change the OnRestoreEnd method.
        Thanks,
        Nick

        Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

        Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.