Click here to Skip to main content
15,885,978 members
Articles / Programming Languages / C#

MDI Case Study Purchasing - Part VII - Document Form II

Rate me:
Please Sign up or sign in to vote.
5.00/5 (6 votes)
27 Nov 2015CPOL7 min read 9K   356   7  

Introduction

In Part VII we will complete the layout for our document form by adding our final control, the DataGridView for our Item collection. We will also give our DataGridView a context menu to handle deleting, and copy/paste functions.

DataGridView

First, in the Data Sources tool panel, let's change the control type for PurchaseOrder->Items to DataGridView by clicking the drop down for the Items property, and selecting DataGridView. Then drag Items on the document Form, and a DataGridView will be created, along with an itemsDataBindingSource, which you can see in the non-dispaly asset area

Screenshot 1

Screenshot 2

So at this point you pretty much have everything you need if you wanted to enter products manually, we could move on to copy/paste, undo/redo, and printing. However, we are not going to be using manual product entry. Instead, we will have a set collection of items that are available to order. In the future we will handle how to get them onto the document, but for now, we will just make a few items in our constructor for sample data. However, let's first finish constructing our data grid to our liking. First, size it so that the left is even with the left of the vendor address panel, the right is even with the right of the shipping address panel, and the bottom is a nice even margin equal with the side margin, and the top is a nice margin below the address panels, like so

Screenshot 3

Next, we don't want the user to be able to add or delete directly, or reorder columns, or resize rows (we will allow resize of columns). So set the following properties

  • AllowUserToAddRows = False
  • AllowUserToDeleteRows = False
  • AllowUserToOrderColumns = False
  • AllowUserToResizeCoumns = True
  • AllowUserToResizeRows = False
  • AutoSizeColumnsMode = Fill

The last property, AutoSizeColumnsMode, determines how the DataGridView intelligently sizes the columns. Choosing Fill will cause the entire width of the DataGridView to be filled with the columns, having them resized based on a property of the Column, which is currently equal for all 4, so they will be equally spaced. Let's also get rid of the RowHeader by setting RowHeadersVisible to False. Now let's edit the Columns, click the "..." button beside columns, and a collection edit box will open, allowing us to edit the properties of our columns. First, lets reorder them using the up/down arrows, so that we have PurchaseQuantity, ProductNumber, Description, Cost. Next, find the FillWeight property for each column, this is the property that governs how the grid auto sizes the columns. Set PurchaseQuantity and Cost to 30, set ProductNumber to 50, and leave Description at 100. The header text "PurchaseQuantity" wil be a bit to lengthy for our column width, so let's change the HeaderText of PurchaseQuantity to just "Quantity". Lasty, for the Cost column, find the DefaultCellStyle property, and click the "..." edit button. This will launch another window. Find the Format property, and again click the "..." edit button to launch the format edit window. Choose "Numeric". Also, in the DefaultCellStyle set the Alignment to "TopRight". Finally, set the BackColor of the DataGridView to Window.

Screenshot 5

Screenshot 6

Now let's add a few items to the collection in our PurchaseOrder constructor. Add these lines at the end of the constructor method

C#
Item item = new Item();
item.ProductNumber = "100001";
item.Description = "Product Number 1";
item.Cost = 1.5;
item.PurchaseQuantity = 100;
_items.Add(item);
item = new Item();
item.ProductNumber = "100002";
item.Description = "Product Number 2";
item.Cost = 2.5;
item.PurchaseQuantity = 200;
_items.Add(item);
item = new Item();
item.ProductNumber = "100003";
item.Description = "Product Number 3";
item.Cost = 3.5;
item.PurchaseQuantity = 300;
_items.Add(item);

Now you can open the application, and create a new document, and see what the form looks like now, with the three items added.

ContextMenu

Next, let's create a context menu for our items data grid. This context menu will have 2 selections for now, but we will expand it in the future. First, from the tools panel, drag a new ContextMenu onto our form, and name it itemGridMenu. Then, using the menu editing tools, create 3 menu items, "Select All", "-" to create a divider line, and "Delete".

Screenshot 7

Now for the ContextMenuStrip property of the DataGridView, choose itemGridMenu. Now you can run the app, and see that when you right click the grid, you get the context menu. However, it needs some cleaning up. First of all, we need to have the menu act a bit intelligently, so that it selects the row that is clicked, and we want to control the ability to use the menu items based on which cells, if any, are clicked. We will accomplish this with two events, the MouseDown event of the DataGridView and the Opening event of the ContextMenuStrip. With the DataGridView selected, click the little lightning bolt icon in the properties panel to access the events, and double click to create a handler for the MouseDown event. Then with the itemGridMenu selected, double click the Opening event to create its handler.

Screenshot 8

With the MouseDown event we will respond to where the user clicks, and adjust the selection accordingly. First, in the properties window, for the Select All and Delete menu items, set their Enabled property to False. This way they will always be disabled, unless we decide to enable them. Now for the MouseDown event. Here we will determine if the mouse click was the right click, and if so we will change the selected row to the one containing the clicked cell. Your handler should look like

C#
private void itemsDataGridView_MouseDown(object sender, MouseEventArgs e)
{
    if (e.Button == System.Windows.Forms.MouseButtons.Right)
    {
        int rowIndex = this.itemsDataGridView.HitTest(e.X, e.Y).RowIndex;
        if (rowIndex >= 0 && rowIndex < itemsDataGridView.Rows.Count)
        {
            selectAllToolStripMenuItem.Enabled = true;
            deleteToolStripMenuItem.Enabled = true;
            DataGridViewRow clickedRow = this.itemsDataGridView.Rows[rowIndex];
            if (!this.itemsDataGridView.SelectedRows.Contains(clickedRow))
            {
                foreach (DataGridViewCell c in clickedRow.Cells)
                {
                    if (c.Selected)
                    {
                        foreach (DataGridViewCell sC in this.itemsDataGridView.SelectedCells)
                        {
                           sC.OwningRow.Selected = true;
                        }
                        return;
                    }
                }
                this.itemsDataGridView.ClearSelection();
                clickedRow.Selected = true;
            }
        }
        else
        {
            this.itemsDataGridView.ClearSelection();
            selectAllToolStripMenuItem.Enabled = true;
            deleteToolStripMenuItem.Enabled = false;
        }
    }
}

This handler checks to see if a cell is selected, and if so sets it's entire row to selected. This will allow you to select a range of cells, then right click to have all contained rows selected.

Next, with the Opening event we will decide which, if any, menu items will be usable, and set them to enabled. Select All will basically always be functional, unless the form contains no items. Delete will only be functional if one or more item rows are selected. Here is what your Opening handler should look like

C#
private void itemGridMenu_Opening(object sender, CancelEventArgs e)
{
    selectAllToolStripMenuItem.Enabled = _purchaseOrder.Items.Count > 0;
    deleteToolStripMenuItem.Enabled = itemsDataGridView.SelectedRows.Count > 0;
}

Next, let's handle the Select All menu item. In design view, with the itemGridMenu selected, double click the Select All menu item to create the click handler. We'll make a call to a new method we will create called SelectAll() 

C#
private void selectAllToolStripMenuItem_Click(object sender, EventArgs e)
{
    SelectAll();
}

Then our SelectAll() method should look like

C#
public void SelectAll()
{
     foreach(DataGridViewRow r in itemsDataGridView.Rows) r.Selected = true;
}

Now let's handle the Select All menu item in our main MDIForm. We want this button to only be enabled if a form is active, and the active form has items, because we are going to use this button to select all items, and not any of the other UI elements. So for this to work, we want to control the Enabled property of the Select All menu item in 2 places, in the OnMdiChildActivate method, and in the purchaseOrderForm_Changed method. First, in the OnMdiChildActivate method, add the following line to first disable the Select All button

C#
private override void OnMdiChildActivate(EventArgs e)
{
     .....
     selectAllToolStripMenuItem.Enabled = false;
     .....
}

Then in the if (this.ActiveMdiChild != null) block, add the line

C#
if (this.ActiveMdiChild != null)
{
     .....
     selectAllToolStripMenuItem.Enabled = activeChildForm.PurchaseOrder.Items.Count > 0;
}

Now, with in Design View for MDIForm, double click the Select All menu item to create its handler

C#
private void selectAllToolStripMenuItem_Click(object sender, EventArgs e)
{
     if(this.ActiveMdiChild != null) 
     {
          PurchaseOrderForm activeChildForm = this.ActiveMdiForm as PurchaseOrderForm;
​          activeChildForm.SelectAll();
     }
}

Lastly, let's get the Delete function working. First, let's write a public method in our PurchaseOrder class that will handle the actual deletion of items in it's items collection. This delete function will take an ItemCollection containing the items we wish to delete. We need to remember, we need to call the OnChanging method BEFORE actually deleting, and call the OnChanged method after. The reasoning for this will become more clear as we get to our section on Undo / Redo functionality. Here is what our DeleteItems(ItemCollection deleteItems) method in our PurchaseOrder class should look like

C#
public void DeleteItems(ItemCollection deleteItems)
{
    OnChanging(new PurchaseOrderChangingEventArgs());

    foreach (Item i in deleteItems) _items.Remove(i);

    OnChanged(new PurchaseOrderChangedEventArgs());
}

Next we need to create a public method in our PurchaseOrderForm class that will delete all selected items in the DataGridView. This method will be called by our context menu item, and also from the MDIForm when delete functions are needed. Here is what your DeleteSelectedItems() method in PurchaseOrderForm should look like

C#
public void DeleteSelectedItems()
{
    ItemCollection deleteItems = new ItemCollection();
    foreach (DataGridViewRow r in itemsDataGridView.SelectedRows)
    {
        deleteItems.Add(_purchaseOrder.Items[r.Index]);
    }
    if (deleteItems.Count > 0) _purchaseOrder.DeleteItems(deleteItems);
}

With the itemGridMenu selected in Design View, double click the Delete menu item to create its click handler, and for this handler we simply call the DeleteSelected() method

C#
private void deleteToolStripMenuItem_Click(object sender, EventArgs e)
{
    DeleteSelectedItems();
}

So that's it. Part VII is complete, and our form is finished. In future installments we will add some items to our Form, but for now we have the basics. Next we will implement Cut/Copy/Paste, as well as Undo/Redo functionality.

Points of Interest

  • DataGridView
  • ContextMenu

History

  • 11/27/2015 - Orginal publish date
  • 11/30/2015 - Added MDIForm Select All to article

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
-- There are no messages in this forum --