Wednesday, March 28, 2012

WinForms and C#: How to sort a ListView by column

Problem

How to sort a "WinForms.ListView" content when one of the columns are clicked

Solution

I don't remember where I found this great Sorter-class but just copy-paste it to your project as a new ListViewSorter class
public class ListViewSorter : System.Collections.IComparer
{
        public int Compare(object x, object y)
        {
            int result = 0;

            if (!(x is ListViewItem))
            {
                return result;
            }
            if (!(y is ListViewItem))
            {
                return result;
            }

            // Determine whether the type being compared is a date type.
            try
            {
                System.DateTime firstDate = DateTime.Parse(((ListViewItem)x).SubItems[ByColumn].Text);
                System.DateTime secondDate = DateTime.Parse(((ListViewItem)y).SubItems[ByColumn].Text);
                result = DateTime.Compare(firstDate, secondDate);
            }
            catch
            {        
                // Compare the two items as a string. 
                result = String.Compare(((ListViewItem)x).SubItems[ByColumn].Text, ((ListViewItem)y).SubItems[ByColumn].Text);
            }

            // Determine whether the sort order is descending. 
            if (((ListViewItem)x).ListView.Sorting == SortOrder.Descending)
            {
                // Invert the value returned by compare. 
                result *= -1;
            }

            LastSort = ByColumn;
            return result;
        }

        public int ByColumn
        {
            get { return Column; }
            set { Column = value; }
        }
        int Column = 0;

        public int LastSort
        {
            get { return LastColumn; }
            set { LastColumn = value; }
        }
        int LastColumn = 0;
}

After your class is ready you must declare a sorter object in the form:
private ListViewSorter listSorter = new ListViewSorter();

and create a new event for ListView ColumnClick to handle sorting
private void listMyList_ColumnClick(object sender, ColumnClickEventArgs e)
{
        listMyList.ListViewItemSorter = listSorter;
        if (listSorter.LastSort == e.Column)
        {
            // Change sort order for current column
            if (listMyList.Sorting == SortOrder.Ascending)
            {
                listMyList.Sorting = SortOrder.Descending;
            }
            else
            {
                listMyList.Sorting = SortOrder.Ascending;
            }
        }
        else
        {
            listMyList.Sorting = SortOrder.Descending;
        }
        listSorter.ByColumn = e.Column;
        listMyList.Sort();
}

Thursday, March 22, 2012

ASP.NET: How to pass value back from user control to the page with events

Problem
This is simple tutorial how to pass a value back from user control to the page with event. In this example textbox value is passed back to the label on default page

Solution
After creating a new web site with default page create a simple user control called InputBox. Inputbox should have one TextBox and two buttons

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="InputBox.ascx.cs" Inherits="Testi.InputBox" %>
<table>
    <tr>
        <td colspan="2">
            <asp:Label ID="lblUserInput" runat="server" Text="User input:"></asp:Label>
        </td>
    </tr>
    <tr>
        <td colspan="2">
            <asp:TextBox ID="txtUserInput" runat="server"></asp:TextBox>
        </td>
    </tr>
    <tr>
        <td>
            <asp:Button ID="btnOk" runat="server" Text="OK" onclick="btnOk_Click" />
        </td>
        <td>
            <asp:Button ID="btnCancel" runat="server" Text="Cancel" 
                onclick="btnCancel_Click" />
        </td>
    </tr>
</table>

Open user control code-behind and create a new EventArgs class

public class InputEventArgs : EventArgs
{
    public string UserInput{ get; set; }
}
Then add two events to the user control which uses InputEventArgs

    public event EventHandler<InputEventArgs> OKClicked;
    public event EventHandler<InputEventArgs> CancelClicked;

Add following code to the OK and Cancel -button event handlers to fill InputEventArgs:

        protected void btnOk_Click(object sender, EventArgs e)
        {
            // Create a new event argument when OK is clicked
            if (OKClicked != null)
            {
                EventHandler<InputEventArgs> tmp = OKClicked;
                if (tmp != null)
                {
                    // Take value from textbox 
                    tmp(this, new InputEventArgs() { UserInput = txtUserInput.Text });
                }
            }
        }

        protected void btnCancel_Click(object sender, EventArgs e)
        {
            // Create a new event argument when Cancel is clicked
            if (CancelClicked != null)
            {
                EventHandler<InputEventArgs> tmp = CancelClicked;
                if (tmp != null)
                {
                    tmp(this, new InputEventArgs() { UserInput = null });
                }
            }
        }
Now you can open your default.aspx page and register user control

<%@ Register TagPrefix="Test" TagName="InputBox" Src="InputBox.ascx" %>

Add the user control and result label inside form

        <table>
            <tr>
                <td>
                    <b>For example you can place this user control inside modal confirmation...</b>
                </td>
            </tr>
            <tr>
                <td>
                    <Test:InputBox ID="inputBox" runat="server" OnOKClicked="InputBox_OKClicked" 

OnCancelClicked="InputBox_CancelClicked" />
                </td>
            </tr>
            <tr>
                <td>
                    <b>Display input value from user control:</b>
                </td>
            </tr>
            <tr>
                <td>
                    <asp:Label ID="lblInputBoxResult" runat="server" Text=""></asp:Label>
                </td>
            </tr>
        </table>


Then open default page code-behind and create two event handlers to handle clicks from user control

        protected void InputBox_OKClicked(object sender, InputEventArgs e)
        {
            lblInputBoxResult.Text = e.UserInput;
        }

        protected void InputBox_CancelClicked(object sender, InputEventArgs e)
        {
            lblInputBoxResult.Text = "User clicked cancel";
        }


Now you can run and test if value from user control is passed to the event handler at default page...