2009-01-11 Ivan N. Zlatev <contact@i-nz.net>
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / DataGridView.cs
index 5eff4f4b73a4179c4c8e3bfda6fd55b437be7690..7ffbbbc087b052f5ccd22e18e4c86d6267a55758 100644 (file)
@@ -118,9 +118,9 @@ namespace System.Windows.Forms {
                private HScrollBar horizontalScrollBar;
                private VScrollBar verticalScrollBar;
                private Control editingControl;
-               private bool new_row_commited = true;
                private bool is_autogenerating_columns = false;
-               private bool in_binding_context_changed = false;
+               private bool is_binding = false;
+               private bool new_row_editing = false;
                
                // These are used to implement selection behaviour with SHIFT pressed.
                private int selected_row = -1;
@@ -190,6 +190,7 @@ namespace System.Windows.Forms {
                        columnHeadersVisible = true;
                        columns = CreateColumnsInstance();
                        columns.CollectionChanged += OnColumnCollectionChanged;
+                       currentCellAddress = new Point (-1, -1);
                        dataMember = String.Empty;
                        defaultCellStyle = new DataGridViewCellStyle();
                        defaultCellStyle.BackColor = SystemColors.Window;
@@ -265,7 +266,11 @@ namespace System.Windows.Forms {
 
                [DefaultValue (true)]
                public bool AllowUserToAddRows {
-                       get { return allowUserToAddRows; }
+                       get { 
+                               if (allowUserToAddRows && DataManager != null)
+                                       return DataManager.AllowNew;
+                               return allowUserToAddRows;
+                       }
                        set {
                                if (allowUserToAddRows != value) {
                                        allowUserToAddRows = value;
@@ -278,7 +283,11 @@ namespace System.Windows.Forms {
 
                [DefaultValue (true)]
                public bool AllowUserToDeleteRows {
-                       get { return allowUserToDeleteRows; }
+                       get {
+                               if (allowUserToDeleteRows && DataManager != null)
+                                       return DataManager.AllowRemove;
+                               return allowUserToDeleteRows;
+                       }
                        set {
                                if (allowUserToDeleteRows != value) {
                                        allowUserToDeleteRows = value;
@@ -701,12 +710,9 @@ namespace System.Windows.Forms {
                        get { return dataMember; }
                        set {
                                if (dataMember != value) {
-                                       ClearBinding ();
-                                       
                                        dataMember = value;
+                                       ReBind ();
                                        OnDataMemberChanged(EventArgs.Empty);
-                                       
-                                       DoBinding ();
                                }
                        }
                }
@@ -726,12 +732,9 @@ namespace System.Windows.Forms {
                                if (!(value == null || value is IList || value is IListSource || value is IBindingList || value is IBindingListView))
                                        throw new NotSupportedException ("Type cannot be bound.");
                                        
-                               ClearBinding ();
-                               
                                dataSource = value;
+                               ReBind ();
                                OnDataSourceChanged (EventArgs.Empty);
-                               
-                               DoBinding ();
                        }
                }
 
@@ -954,7 +957,7 @@ namespace System.Windows.Forms {
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
                public int NewRowIndex {
                        get {
-                               if (!allowUserToAddRows || ColumnCount == 0) {
+                               if (!AllowUserToAddRows || ColumnCount == 0) {
                                        return -1;
                                }
                                return rows.Count - 1;
@@ -995,7 +998,7 @@ namespace System.Windows.Forms {
                                if (value < 0) {
                                        throw new ArgumentException("RowCount must be >= 0.");
                                }
-                               if (value < 1 && allowUserToAddRows) {
+                               if (value < 1 && AllowUserToAddRows) {
                                        throw new ArgumentException("RowCount must be >= 1 if AllowUserToAddRows is true.");
                                }
                                if (dataSource != null) {
@@ -2253,7 +2256,7 @@ namespace System.Windows.Forms {
                                        return false;
                                }
                        }
-                       
+
                        DataGridViewCell cell = currentCell;
                        Type editType = cell.EditType;
                        
@@ -2267,12 +2270,6 @@ namespace System.Windows.Forms {
                        if (e.Cancel)
                                return false;
 
-                       // If the user begins an edit in the NewRow, add a new row
-                       if (CurrentCell.RowIndex == NewRowIndex) {
-                               new_row_commited = false;
-                               OnUserAddedRow (new DataGridViewRowEventArgs (Rows[NewRowIndex]));
-                       }
-               
                        cell.SetIsInEditMode (true);
                        
                        // The cell has an editing control we need to setup
@@ -2320,21 +2317,20 @@ namespace System.Windows.Forms {
 
                public bool CancelEdit ()
                {
-                       if (currentCell != null && currentCell.IsInEditMode) {
-                               // The user's typing caused a new row to be created, but
-                               // now they are canceling that typing, we have to remove
-                               // the new row we added.
-                               if (!new_row_commited) {
-                                       DataGridViewRow delete_row = EditingRow;
-                                       Rows.RemoveInternal (delete_row);
-                                       editing_row = Rows[currentCell.RowIndex];
-                                       OnUserDeletedRow (new DataGridViewRowEventArgs (delete_row));
-                                       new_row_commited = true;
+                       if (currentCell != null) {
+                               if (currentCell.IsInEditMode) {
+                                       currentCell.SetIsInEditMode (false);
+                                       currentCell.DetachEditingControl ();
                                }
 
-                               currentCell.SetIsInEditMode (false);
-                               currentCell.DetachEditingControl ();
-                               OnCellEndEdit (new DataGridViewCellEventArgs (currentCell.ColumnIndex, currentCell.RowIndex));
+                               if (currentCell.RowIndex == NewRowIndex) {
+                                       if (DataManager != null)
+                                               DataManager.CancelCurrentEdit ();
+
+                                       new_row_editing = false;
+                                       PrepareEditingRow (true, false);
+                                       OnUserDeletedRow (new DataGridViewRowEventArgs (EditingRow));
+                               }
                        }
 
                        return true;
@@ -2359,12 +2355,12 @@ namespace System.Windows.Forms {
                                currentCell.Value = currentCell.ParseFormattedValue (currentCell.EditedFormattedValue, 
                                                                                     currentCell.InheritedStyle, null, null);
                        } catch (Exception e) {
-                               Console.WriteLine (e);
                                DataGridViewDataErrorEventArgs args = new DataGridViewDataErrorEventArgs (e, currentCell.ColumnIndex, currentCell.RowIndex, 
                                                                                                          DataGridViewDataErrorContexts.Commit);
                                OnDataError (false, args);
                                if (args.ThrowException)
                                        throw e;
+                               return false;
                        }
                        return true;
                }
@@ -2393,7 +2389,7 @@ namespace System.Windows.Forms {
 
                        for (int index = first_row_index; index < Rows.Count; index++) {
                                DataGridViewRow row = GetRowInternal (index);
-                               if (rowTop + row.Height < ClientSize.Height) {
+                               if (rowTop + row.Height <= ClientSize.Height) {
                                        result++;
                                        rowTop += row.Height;
                                } else {
@@ -2418,15 +2414,21 @@ namespace System.Windows.Forms {
                                return true;
 
                        if (!CommitEdit (context)) {
+                               if (DataManager != null)
+                                       DataManager.EndCurrentEdit ();
                                if (EditingControl != null)
                                        EditingControl.Focus ();
                                return false;
                        }
 
                        currentCell.SetIsInEditMode (false);
-                       new_row_commited = true;
+                       currentCell.DetachEditingControl ();
                        OnCellEndEdit (new DataGridViewCellEventArgs (currentCell.ColumnIndex, currentCell.RowIndex));
                        Focus ();
+                       if (currentCell.RowIndex == NewRowIndex) {
+                               new_row_editing = false;
+                               PrepareEditingRow (false, false);
+                       }
                        return true;
                }
 
@@ -2952,8 +2954,8 @@ namespace System.Windows.Forms {
                        if (Rows.Count == 0)
                                return;
 
-                       IBindingList bindingList = DataSource as IBindingList;
                        if (dataGridViewColumn.IsDataBound) {
+                               IBindingList bindingList = DataManager.List as IBindingList;
                                if (bindingList != null && bindingList.SupportsSorting) {
                                        bindingList.ApplySort (DataManager.GetItemProperties()[dataGridViewColumn.DataPropertyName], direction);
                                        dataGridViewColumn.HeaderCell.SortGlyphDirection = sortOrder;
@@ -3349,11 +3351,7 @@ namespace System.Windows.Forms {
                protected override void OnBindingContextChanged (EventArgs e)
                {
                        base.OnBindingContextChanged(e);
-                       if (!in_binding_context_changed) {
-                               in_binding_context_changed = true;
-                               ReBind();
-                               in_binding_context_changed = true;
-                       }
+                       ReBind();
                }
 
                protected virtual void OnBorderStyleChanged (EventArgs e)
@@ -4632,13 +4630,19 @@ namespace System.Windows.Forms {
                                        horizontalScrollBar.Minimum = 0;
                                        horizontalScrollBar.Maximum = gridWidth;
                                        horizontalScrollBar.SmallChange = Columns[first_col_index].Width;
-                                       horizontalScrollBar.LargeChange = ClientSize.Width - rowHeadersWidth;
+                                       int largeChange = ClientSize.Width - rowHeadersWidth;
+                                       if (largeChange <= 0)
+                                               largeChange = ClientSize.Width;
+                                       horizontalScrollBar.LargeChange = largeChange;
                                }
                                if (verticalVisible) {
                                        verticalScrollBar.Minimum = 0;
                                        verticalScrollBar.Maximum = gridHeight;
                                        verticalScrollBar.SmallChange = first_row_height + 1;
-                                       verticalScrollBar.LargeChange = ClientSize.Height - columnHeadersHeight;
+                                       int largeChange = ClientSize.Height - columnHeadersHeight;
+                                       if (largeChange <= 0)
+                                               largeChange = ClientSize.Height;
+                                       verticalScrollBar.LargeChange = largeChange;
                                }
                        }
 
@@ -4845,9 +4849,18 @@ namespace System.Windows.Forms {
                        if (selected_columns != null)
                                selected_columns.InternalClear ();
 
-                       if (Rows.Count > 0 && Columns.Count > 0 && currentCell != null && 
-                           currentCell.RowIndex >= e.RowIndex)
-                               MoveCurrentCell (0, Math.Min (e.RowIndex, Rows.Count - 2), true, false, false, true);
+                       if (Rows.Count == 0) {
+                               MoveCurrentCell (-1, -1, true, false, false, true);
+                       } else if (Columns.Count == 0) {
+                               MoveCurrentCell (-1, -1, true, false, false, true);
+                       } else {
+                               int nextRowIndex = e.RowIndex;
+                               if (nextRowIndex >= Rows.Count)
+                                       nextRowIndex = Rows.Count - 1;
+                               MoveCurrentCell (currentCell != null ? currentCell.ColumnIndex : 0, nextRowIndex, 
+                                                true, false, false, true);
+                       }
+
                        Invalidate ();
                        OnRowsRemoved (e);
                }
@@ -4907,11 +4920,17 @@ namespace System.Windows.Forms {
 
                protected virtual void OnUserAddedRow (DataGridViewRowEventArgs e)
                {
-                       editing_row = null;
+                       // Switch the current editing row with a real
+                       int newRowIndex = NewRowIndex;
+                       int currentColumnIndex = currentCell != null ? currentCell.ColumnIndex : 0;
+                       new_row_editing = true;
                        PrepareEditingRow (false, false);
+                       if (DataManager != null)
+                               DataManager.AddNew (); // will raise OnListPositionChanged
+                       else
+                               MoveCurrentCell (currentColumnIndex, NewRowIndex, true, false, false, true);
 
-                       e = new DataGridViewRowEventArgs (editing_row);
-                       
+                       e = new DataGridViewRowEventArgs (Rows[NewRowIndex]);
                        DataGridViewRowEventHandler eh = (DataGridViewRowEventHandler)(Events [UserAddedRowEvent]);
                        if (eh != null) eh (this, e);
                }
@@ -4920,7 +4939,6 @@ namespace System.Windows.Forms {
                {
                        DataGridViewRowEventHandler eh = (DataGridViewRowEventHandler)(Events [UserDeletedRowEvent]);
                        if (eh != null) eh (this, e);
-
                }
 
                protected virtual void OnUserDeletingRow (DataGridViewRowCancelEventArgs e)
@@ -5378,10 +5396,23 @@ namespace System.Windows.Forms {
                        if (cell != null && !cell.Visible)
                                throw new InvalidOperationException ("cell is not visible");
                                
+                       // Always update the current cell address property
+                       // If the row has moved it would be out of date.
+                       if (currentCell != null) {
+                               if (setAnchorCellAddress) {
+                                       anchor_cell.X = currentCell.ColumnIndex;
+                                       anchor_cell.Y = currentCell.RowIndex;
+                               }
+                               currentCellAddress.X = currentCell.ColumnIndex;
+                               currentCellAddress.Y = currentCell.RowIndex;
+                       }
+
                        if (cell != currentCell) {
                                if (currentCell != null) {
                                        if (currentCell.IsInEditMode && !EndEdit ())
                                                return false;
+                                       else if (currentCell.RowIndex == NewRowIndex && new_row_editing)
+                                               CancelEdit ();
                                        OnCellLeave (new DataGridViewCellEventArgs(currentCell.ColumnIndex, currentCell.RowIndex));
                                        OnRowLeave (new DataGridViewCellEventArgs (currentCell.ColumnIndex, currentCell.RowIndex));
                                }
@@ -5391,14 +5422,25 @@ namespace System.Windows.Forms {
                                        anchor_cell = new Point (columnIndex, rowIndex);
                                currentCellAddress = new Point (columnIndex, rowIndex);
 
-                               UpdateBindingPosition (currentCell.RowIndex);
-                               OnRowEnter (new DataGridViewCellEventArgs (cell.ColumnIndex, cell.RowIndex));
-                               OnCellEnter (new DataGridViewCellEventArgs(cell.ColumnIndex, cell.RowIndex));
+                               if (cell != null) {
+                                       UpdateBindingPosition (cell.RowIndex);
+                                       OnRowEnter (new DataGridViewCellEventArgs (cell.ColumnIndex, cell.RowIndex));
+                                       OnCellEnter (new DataGridViewCellEventArgs(cell.ColumnIndex, cell.RowIndex));
+                               }
                                OnCurrentCellChanged (EventArgs.Empty);
-                               if (editMode == DataGridViewEditMode.EditOnEnter)
-                                       BeginEdit (true);
+
+                               if (cell != null) {
+                                       // If the user begins an edit in the NewRow, add a new row
+                                       if (AllowUserToAddRows && cell.RowIndex == NewRowIndex && !is_binding && !new_row_editing) {
+                                               // OnUserAddedRow will add a real row and reset the current cell
+                                               OnUserAddedRow (new DataGridViewRowEventArgs (Rows[NewRowIndex]));
+                                       } else {
+                                               if (editMode == DataGridViewEditMode.EditOnEnter)
+                                                       BeginEdit (true);
+                                       }
+                               }
                        } else {
-                               if (throughMouseClick)
+                               if (cell != null && throughMouseClick)
                                        BeginEdit (true);
                        }
 
@@ -5436,7 +5478,8 @@ namespace System.Windows.Forms {
                }
 
                internal void SetSelectedRowCoreInternal (int rowIndex, bool selected) {
-                       SetSelectedRowCore (rowIndex, selected);
+                       if (rowIndex >= 0 && rowIndex < Rows.Count)
+                               SetSelectedRowCore (rowIndex, selected);
                }       
 
                protected virtual void SetSelectedRowCore (int rowIndex, bool selected) {
@@ -5730,6 +5773,14 @@ namespace System.Windows.Forms {
 
                internal void PrepareEditingRow (bool cell_changed, bool column_changed)
                {
+                       if (new_row_editing) {
+                               if (editing_row != null) {
+                                       Rows.RemoveInternal (editing_row);
+                                       editing_row = null;
+                               }
+                               return;
+                       }
+
                        bool show = false;
                        
                        show = ColumnCount > 0 && AllowUserToAddRows;
@@ -5738,23 +5789,18 @@ namespace System.Windows.Forms {
                                Rows.RemoveInternal (editing_row);
                                editing_row = null;
                        } else if (show) {
-                               if (editing_row != null) {
-                                       if (cell_changed) {
-                                               // The row changed, it's no longer an editing row.
-                                               editing_row = null;
-                                       } else if (column_changed) {
-                                               // The number of columns has changed, we need a new editing row.
-                                               Rows.RemoveInternal (editing_row);
-                                               editing_row = null;
-                                       }
+                               if (editing_row != null && (cell_changed || column_changed)) {
+                                       // The row changed, it's no longer an editing row.
+                                       //    or
+                                       // The number of columns has changed, we need a new editing row.
+                                       Rows.RemoveInternal (editing_row);
+                                       editing_row = null;
                                }
                                if (editing_row == null) {
                                        editing_row = RowTemplateFull;
                                        Rows.AddInternal (editing_row, false);
                                }
                        }
-                               
-                       
                }
                
                internal DataGridViewRow EditingRow {
@@ -5810,8 +5856,10 @@ namespace System.Windows.Forms {
                        columns.ClearAutoGeneratedColumns ();
                        rows.Clear ();
                        PrepareEditingRow (false, true);
-                       if (DataManager != null)
+                       if (DataManager != null) {
                                DataManager.ListChanged -= OnListChanged;
+                               DataManager.PositionChanged -= OnListPositionChanged;
+                       }
                }
                
                private void DoBinding ()
@@ -5838,7 +5886,7 @@ namespace System.Windows.Forms {
                                                DataGridViewColumn col = CreateColumnByType (property.PropertyType);
                                                col.Name = property.DisplayName;
                                                col.DataPropertyName = property.DisplayName;
-                                               col.ReadOnly = property.IsReadOnly;
+                                               col.ReadOnly = !DataManager.AllowEdit || property.IsReadOnly;
                                                col.SetIsDataBound (true);
                                                col.ValueType = property.PropertyType;
                                                col.AutoGenerated = true;
@@ -5848,22 +5896,43 @@ namespace System.Windows.Forms {
                                        is_autogenerating_columns = false;
                                }
 
+                               // DataBind both autogenerated and not columns if there is a matching property
+                               foreach (PropertyDescriptor property in DataManager.GetItemProperties()) {
+                                       foreach (DataGridViewColumn col in Columns) {
+                                               if (col.DataPropertyName == property.Name)
+                                                       col.SetIsDataBound (true);
+                                       }
+                               }
+
                                foreach (object element in DataManager.List)
                                        AddBoundRow (element);
 
                                DataManager.ListChanged += OnListChanged;
+                               DataManager.PositionChanged += OnListPositionChanged;
                                OnDataBindingComplete (new DataGridViewBindingCompleteEventArgs (ListChangedType.Reset));
+                               OnListPositionChanged (this, EventArgs.Empty);
+                       } else {
+                               if (Rows.Count > 0 && Columns.Count > 0)
+                                       MoveCurrentCell (0, 0, true, false, false, false);
                        }
-                       if (Rows.Count > 0 && Columns.Count > 0)
-                               MoveCurrentCell (0, 0, true, false, false, false);
+
                        PerformLayout();
                        Invalidate ();
                }
                
                private void MoveCurrentCell (int x, int y, bool select, bool isControl, bool isShift, bool scroll)
                {
-                       if (!SetCurrentCellAddressCore (x, y, true, false, false))
+                       if (x == -1 || y == -1)
+                               x = y = -1;
+
+                       if (!SetCurrentCellAddressCore (x, y, true, false, false)) {
+                               ClearSelection ();
+                               return;
+                       }
+                       if (x == -1 && y == -1) {
+                               ClearSelection ();
                                return;
+                       }
 
                        bool full_row_selected = Rows.SharedRow(CurrentCellAddress.Y).Selected;
                        bool full_col_selected = Columns[CurrentCellAddress.X].Selected;
@@ -5949,6 +6018,8 @@ namespace System.Windows.Forms {
                
                private int ColumnIndexToDisplayIndex (int index)
                {
+                       if (index == -1)
+                               return index;
                        return Columns[index].DisplayIndex;
                }
                
@@ -5964,7 +6035,7 @@ namespace System.Windows.Forms {
                                        AddBoundRow (DataManager[args.NewIndex]);
                                        break;
                                case ListChangedType.ItemDeleted:
-                                       Rows.RemoveAt (args.NewIndex);
+                                       Rows.RemoveAtInternal (args.NewIndex);
                                        break;
                                case ListChangedType.ItemChanged:
                                        break;
@@ -5976,11 +6047,23 @@ namespace System.Windows.Forms {
                        Invalidate ();
                }
                
+               private void OnListPositionChanged (object sender, EventArgs args)
+               {
+                       if (Rows.Count > 0 && Columns.Count > 0 && DataManager.Position != -1)
+                               MoveCurrentCell (currentCell != null ? currentCell.ColumnIndex : 0, DataManager.Position, 
+                                                true, false, false, true);
+                       else
+                               MoveCurrentCell (-1, -1, true, false, false, true);
+               }
 
                private void ReBind ()
                {
-                       ClearBinding ();
-                       DoBinding ();
+                       if (!is_binding) {
+                               is_binding = true;
+                               ClearBinding ();
+                               DoBinding ();
+                               is_binding = false;
+                       }
                }
                
                private bool MouseOverColumnResize (int col, int mousex)
@@ -6094,12 +6177,14 @@ namespace System.Windows.Forms {
 
                                object val1 = row1.Cells[column].FormattedValue;
                                object val2 = row2.Cells[column].FormattedValue;
+                               object val1NullValue = row1.Cells[column].InheritedStyle.NullValue;
+                               object val2NullValue = row2.Cells[column].InheritedStyle.NullValue;
 
-                               if (val1 == null && val2 == null)
+                               if (val1 == val1NullValue && val2 == val2NullValue)
                                        return 0;
-                               if (val1 == null)
+                               if (val1 == val1NullValue)
                                        return direction;
-                               if (val2 == null)
+                               if (val2 == val2NullValue)
                                        return -1 * direction;
 
                                if (numeric_sort)