Search code examples
wpfdatagridwpfdatagrid

Wpf Datagrid Max Rows


I am currently working with data grid where I only want to allow the user to enter UP TO 20 rows of data before making CanUserAddRows to false.

I made a dependency property on my own datagrid (that derives from the original one). I tried using the event "ItemContainerGenerator.ItemsChanged" and check for row count in there and if row count = max rows, then make can user add row = false (I get an exception for that saying i'm not allow to change it during "Add Row".

I was wondering if there is a good way on implementing this ....

Thanks and Regards, Kevin


Solution

  • Here's a subclassed DataGrid with a MaxRows Dependency Property. The things to note about the implementation is if the DataGrid is in edit mode and CanUserAddRows is changed an InvalidOperationException will occur (like you mentioned in the question).

    InvalidOperationException
    'NewItemPlaceholderPosition' is not allowed during a transaction begun by 'AddNew'.

    To workaround this, a method called IsInEditMode is called in OnItemsChanged and if it returns true we subscribe to the event LayoutUpdated to check IsInEditMode again.

    Also, if MaxRows is set to 20, it must allow 20 rows when CanUserAddRows is True, and 19 rows when False (to make place for the NewItemPlaceHolder which isn't present on False).

    It can be used like this

    <local:MaxRowsDataGrid MaxRows="20"
                           CanUserAddRows="True"
                           ...>
    

    MaxRowsDataGrid

    public class MaxRowsDataGrid : DataGrid
    {
        public static readonly DependencyProperty MaxRowsProperty =
            DependencyProperty.Register("MaxRows",
                                        typeof(int),
                                        typeof(MaxRowsDataGrid),
                                        new UIPropertyMetadata(0, MaxRowsPropertyChanged));
        private static void MaxRowsPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
        {
            MaxRowsDataGrid maxRowsDataGrid = source as MaxRowsDataGrid;
            maxRowsDataGrid.SetCanUserAddRowsState();
        }
        public int MaxRows
        {
            get { return (int)GetValue(MaxRowsProperty); }
            set { SetValue(MaxRowsProperty, value); }
        }
    
        private bool m_changingState;
        public MaxRowsDataGrid()
        {
            m_changingState = false;
        }
        protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            base.OnItemsChanged(e);
            if (IsInEditMode() == true)
            {
                EventHandler eventHandler = null;
                eventHandler += new EventHandler(delegate
                {
                    if (IsInEditMode() == false)
                    {
                        SetCanUserAddRowsState();
                        LayoutUpdated -= eventHandler;
                    }
                });
                LayoutUpdated += eventHandler;
            }
            else
            {
                SetCanUserAddRowsState();
            }
        }
        private bool IsInEditMode()
        {
            IEditableCollectionView itemsView = Items;
            if (itemsView.IsAddingNew == false && itemsView.IsEditingItem == false)
            {
                return false;
            }
            return true;
        }        
        // This method will raise OnItemsChanged again 
        // because a NewItemPlaceHolder will be added or removed
        // so to avoid infinite recursion a bool flag is added
        private void SetCanUserAddRowsState()
        {
            if (m_changingState == false)
            {
                m_changingState = true;
                int maxRows = (CanUserAddRows == true) ? MaxRows : MaxRows-1;
                if (Items.Count > maxRows)
                {
                    CanUserAddRows = false;
                }
                else
                {
                    CanUserAddRows = true;
                }
                m_changingState = false;
            }
        }
    }