Advanced Controls in Mono Winforms

In this part of the IronPython Mono Winforms tutorial, we introduce some more advanced controls. Namely the ListBox, the ListView and the TreeView control.

ListBox Control

The ListBox control is used to display a list of items. Users can select one or more items by clicking on them.

listbox.py
#!/usr/bin/ipy

import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")

from System.Windows.Forms import Application, Form, StatusBar
from System.Windows.Forms import ListBox, DockStyle
from System.Drawing import Size


class IForm(Form):

    def __init__(self):
        self.Text = "ListBox"
        
        lb = ListBox()
        lb.Parent = self

        lb.Items.Add("Jessica")
        lb.Items.Add("Rachel")
        lb.Items.Add("Angelina")
        lb.Items.Add("Amy")
        lb.Items.Add("Jennifer")
        lb.Items.Add("Scarlett")

        lb.Dock = DockStyle.Fill
        lb.SelectedIndexChanged += self.OnChanged

        self.sb = StatusBar()
        self.sb.Parent = self

        self.Size = Size(220, 220)
        self.CenterToScreen()
    

    def OnChanged(self, sender, event):
        self.sb.Text = sender.SelectedItem


Application.Run(IForm())

Our example shows a listbox with six names. The selected item is shown in the statusbar.

lb = ListBox()
lb.Parent = self

ListBox control is created.

lb.Items.Add("Jessica")

This is how we add a new item to the ListBox control. The control has the Items property. The property is a reference to the list of items in a listbox. Using this reference, we can add, remove or get count of items of the listbox.

lb.SelectedIndexChanged += self.OnChanged

SelectedIndexChanged event is triggered, when we select an item.

def OnChanged(self, sender, event):
    self.sb.Text = sender.SelectedItem

Inside the OnChange() method, we get selected value of the listbox and set it to the statusbar.

ListBox
Figure: ListBox

ListView

ListView control is used to display collections of items. It is a more sophisticated control than the ListBox control. It can display data in various views. It is mostly used to display data in multicolumn views.

listview.py
#!/usr/bin/ipy

import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")

from System.Windows.Forms import Application, Form, StatusBar
from System.Windows.Forms import ListView, View, ColumnHeader
from System.Windows.Forms import ListViewItem, DockStyle, SortOrder
from System.Drawing import Size


class IForm(Form):

    def __init__(self):
        self.Text = 'ListBox'
       

        actresses = { 'Jessica Alba' : '1981', 'Angelina Jolie' : '1975', 
            'Natalie Portman' : '1981', 'Rachel Weiss' : '1971', 
            'Scarlett Johansson' : 1984 }


        name = ColumnHeader()
        name.Text = 'Name'
        name.Width = -1
        year = ColumnHeader()
        year.Text = 'Year'

        self.SuspendLayout()

        lv = ListView()
        lv.Parent = self
        lv.FullRowSelect = True
        lv.GridLines = True
        lv.AllowColumnReorder = True
        lv.Sorting = SortOrder.Ascending
        lv.Columns.AddRange((name, year))
        lv.ColumnClick += self.OnColumnClick

        for act in actresses.keys():
            item = ListViewItem()
            item.Text = act
            item.SubItems.Add(str(actresses[act]))
            lv.Items.Add(item)
        

        lv.Dock = DockStyle.Fill
        lv.Click += self.OnChanged

        self.sb = StatusBar()
        self.sb.Parent = self
        lv.View = View.Details

        self.ResumeLayout()

        self.Size = Size(350, 300)
        self.CenterToScreen()
    

    def OnChanged(self, sender, event):

        name = sender.SelectedItems[0].SubItems[0].Text
        born = sender.SelectedItems[0].SubItems[1].Text
        self.sb.Text = name + ', ' + born
    

    def OnColumnClick(self, sender, event):

        if sender.Sorting == SortOrder.Ascending:
            sender.Sorting = SortOrder.Descending
        else: 
            sender.Sorting = SortOrder.Ascending


Application.Run(IForm())

In our example, we have a list view with two columns. In the first column, we display the name of the actress. In the second one their date of birth. The data is store in a List collection. By selecting a row, the data in a row is displayed in the statusbar. Also, by clicking on the column header, the data is sorted.

actresses = { 'Jessica Alba' : '1981', 'Angelina Jolie' : '1975', 
    'Natalie Portman' : '1981', 'Rachel Weiss' : '1971', 
    'Scarlett Johansson' : 1984 }

We store our data in the actresses dictionary.

name = ColumnHeader()
name.Text = 'Name'
name.Width = -1

For each column in a list view, we create a ColumnHeader. By setting the Width to -1, the width of the column is equal to the longest item in the column.

lv = ListView()
lv.Parent = self

ListView control is created.

lv.FullRowSelect = True
lv.GridLines = True
lv.AllowColumnReorder = True
lv.Sorting = SortOrder.Ascending

Here we set four properties of the control. This code lines enable full row selection, show grid lines, allow column reordering by dragging the columns and sort the data in ascending order.

lv.Columns.AddRange((name, year))

Here we add two ColumnHeaders to the ListView control.

for act in actresses.keys():
    item = ListViewItem()
    item.Text = act
    item.SubItems.Add(str(actresses[act]))
    lv.Items.Add(item)

This cycle populates the list view control. Each row is added to the list view as a ListViewItem class.

lv.View = View.Details

The ListView control can have different views. Different views display data differently.

name = sender.SelectedItems[0].SubItems[0].Text
born = sender.SelectedItems[0].SubItems[1].Text
self.sb.Text = name + ', ' + born

Inside the OnChanged() method, we get the data from the selected row and show it on the statusbar.

if sender.Sorting == SortOrder.Ascending:
    sender.Sorting = SortOrder.Descending
else: 
    sender.Sorting = SortOrder.Ascending

Here we toggle the sorting order of the column.

ListView
Figure: ListView

TreeView

TreeView control displays hierarchical collection of items. Each item in this control is represented by a TreeNode object.

treeview.py
#!/usr/bin/ipy

import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")

from System.Windows.Forms import Application, Form, StatusBar
from System.Windows.Forms import TreeView, TreeNode, DockStyle
from System.Drawing import Size


class IForm(Form):

    def __init__(self):
        self.Text = 'TreeView'
        
        tv = TreeView()

        root = TreeNode()
        root.Text = 'Languages'

        child1 = TreeNode()
        child1.Text = 'Python'

        child2 = TreeNode()
        child2.Text = 'Ruby'

        child3 = TreeNode()
        child3.Text = 'Java'

        root.Nodes.AddRange((child1, child2, child3))

        tv.Parent = self
        tv.Nodes.Add(root)
        tv.Dock = DockStyle.Fill
        tv.AfterSelect += self.AfterSelect

        self.sb = StatusBar()
        self.sb.Parent = self

        self.Size = Size(220, 220)
        self.CenterToScreen()
    

    def AfterSelect(self, sender, event):    
        self.sb.Text = event.Node.Text
    

Application.Run(IForm())

This is a very simple demonstration of the TreeView control. We have one root item and three children.

tv = TreeView()

We create the TreeView control.

root = TreeNode()
root.Text = 'Languages'
...
tv.Nodes.Add(root)

Here we create a root node.

child1 = TreeNode()
child1.Text = 'Python'

Child node is created in a similar way.

root.Nodes.AddRange((child1, child2, child3))

Child nodes are plugged into the Nodes property of the root node.

TreeView
Figure: TreeView

Directories

The following code example will examine the TreeView control more in-depth.

directories.py
#!/usr/bin/ipy

import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")

from System.Windows.Forms import Application, Form, StatusBar
from System.Windows.Forms import Button, TreeView, TreeNode
from System.Windows.Forms import DockStyle, AnchorStyles
from System.Drawing import Size, Point
from System.IO import Directory, DirectoryInfo


HOME_DIR = '/home/vronskij'

class IForm(Form):

    def __init__(self):
        self.Text = 'Directories'
        self.Size = Size(400, 400)
        
        self.tv = TreeView()

        self.SuspendLayout()

        self.tv.Parent = self
        self.tv.Location = Point(10,10)
        self.tv.Size = Size(self.ClientSize.Width - 20, self.Height - 200)
        self.tv.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right 


        self.tv.FullRowSelect = False
        self.tv.ShowLines = True
        self.tv.ShowPlusMinus = True
        self.tv.Scrollable = True  
        self.tv.AfterSelect += self.AfterSelect

        expand = Button()
        expand.Parent = self
        expand.Location = Point(20, self.tv.Bottom + 20)
        expand.Text = 'Expand'
        expand.Anchor = AnchorStyles.Left | AnchorStyles.Top
        expand.Click += self.OnExpand

        expandAll = Button()
        expandAll.Parent = self
        expandAll.Location = Point(20, expand.Bottom + 5)
        expandAll.Text = 'Expand All'
        expandAll.Anchor = AnchorStyles.Left | AnchorStyles.Top
        expandAll.Click += self.OnExpandAll

        collapse = Button()
        collapse.Parent = self
        collapse.Location = Point(expandAll.Right + 5, expand.Top)
        collapse.Text = 'Collapse'
        collapse.Anchor = AnchorStyles.Left | AnchorStyles.Top
        collapse.Click += self.OnCollapse

        collapseAll = Button()
        collapseAll.Parent = self
        collapseAll.Location = Point(collapse.Left, collapse.Bottom + 5)
        collapseAll.Text = 'Collapse All'
        collapseAll.Anchor = AnchorStyles.Left | AnchorStyles.Top
        collapseAll.Click += self.OnCollapseAll

        self.sb = StatusBar()
        self.sb.Parent = self

        self.ShowDirectories(self.tv.Nodes, HOME_DIR)

        self.ResumeLayout()

        self.CenterToScreen()
    
    def AfterSelect(self, sender, event):
  
        self.sb.Text = event.Node.Text
        
    def ShowDirectories(self, trvNode, path):
    
        dirInfo = DirectoryInfo(path)
        if (dirInfo != None):
      
          subDirs = dirInfo.GetDirectories()
          tr = TreeNode(dirInfo.Name)

          if (subDirs.Length > 0):
          
              for dr in subDirs: 
                  if not dr.Name.StartsWith("."):
                      self.ShowDirectories(tr.Nodes, dr.FullName)
                        
          trvNode.Add(tr)    

      
    def OnExpand(self, sender, event):
        self.tv.SelectedNode.Expand()
  

    def OnExpandAll(self, sender, event):
        self.tv.ExpandAll()
  

    def OnCollapse(self, sender, event):
        self.tv.SelectedNode.Collapse()
  

    def OnCollapseAll(self, sender, event):
        self.tv.CollapseAll()


Application.Run(IForm())

Our code example shows the directories of the specified home directory in a TreeView control. The application starts with some delay, because it reads the directory structure of the home directory first. We have also four buttons on the form. The buttons expand and collapse nodes programatically.

self.tv.Scrollable = True 

We make the treeview control scrollable, because the control shows lots of directories.

self.ShowDirectories(self.tv.Nodes, HOME_DIR)

The ShowDirectories() method fills the nodes of the treview control with directories available in the specified home directory.

if (subDirs.Length > 0):
    ...

We check if there are any subdirectories.

for dr in subDirs: 
    if not dr.Name.StartsWith("."):
        self.ShowDirectories(tr.Nodes, dr.FullName)

We loop through all directories. For this, we use the recursion algorithm. We also skip the hidden directories. They begin with a dot on Unix systems.

trvNode.Add(tr)     

This code line actually adds the directory to the treeview control.

def OnExpand(self, sender, event):
    self.tv.SelectedNode.Expand()

All four buttons have events plugged to a method. Here is a method for the Expand button. It calls the Expand() method of the currently selected node.

Directories
Figure: Directories

In this part of the IronPython Mono Winforms tutorial, we covered several advanced controls available in Winforms programming library.