SaguiItay

My blog has moved!

You should be automatically redirected in 4 seconds. If not, visit:
http://itaysagui.wordpress.com
and update your bookmarks.

Monday, September 10, 2007

Updating GUI from different threads - Part 2

As part of my work at Tzunami Inc., I'm working on migrating our product Deployer - a SharePoint migration tool - from .Net 1.1 to .Net 2.0 and Visual Studio 2005. One of the first things we've encountered was the managed debugging assistants (MDA). I'll talk about the MDAs in a later entry, but for now, let's just say that those are exceptions that are thrown only in debug mode, and that assist you in finding bugs and problematic points in your code that are otherwise hard to locate. The first MDA that we've encountered was the "Cross-thread operation not valid". What this means, is that you're trying to update the GUI from a thread other than the thread that created the GUI control. You can read about how to solve this issue in .Net 1.1 in one of my previous entries. However, .Net 2.0 allows you to resolve this in a much cleaner way. Bellow is the way we decided to handle this:
private void InvokeGuiDelegate(GUIDelegate d)
{
    if (InvokeRequired)
        BeginInvoke(d);
    else
        d();
}
Now we can use the above method everywhere we need to update the GUI, using an annonymous method:
InvokeGuiDelegate( delegate() { numericUDTimeout.Value = value; });

Labels: , ,

Thursday, September 6, 2007

Updating GUI from different threads

Even the smallest thing, when thought upfront, can help you go a long way. Whenever you create an application that is more that two forms thrown together, and that does a little bit more than just saying “Hello world”, you’ll encounter a point where you’ll want to use threads. So if you’re going to reach that point somewhere along the way, why not plan and prepare for that in advance? Working with threads by itself is not the really topic of this post. However, changing the GUI from different threads is. Windows applications in .Net request that changes in Forms be made in the thread that owns the form. Managing that on your own can be a real pain. Thanksfully, the .Net team thought about us, and allowed us a simple way to handle this:
public void UpdateForm()
{
    if (this.InvokeRequired())
    {
        this.Invoke(new MethodInvoker(UpdateForm));
        return;
    }
    // Update your form as usual here...
}
The more attentive readers will raise the point, that this example works only because UpdateForm() returns void and accepts no argument. Sadly this is true. For any other case we’ll have to write a delegate, and send the Invoke() method an instance of that delegate, just as the following example shows:
private delegate bool UpdateFormMethod(int argument);

public bool UpdateForm(int argument)
{
    if (this.InvokeRequired)
    {
        return (bool)this.Invoke(new UpdateFormMethod(UpdateForm),
            new object[] {argument});
    }
    // Update your form as usual here...
    return true;
}

Labels: , ,

Transparent Click-thru windows

It is often usefull to provide the user with information on the screen, without interfering his work. Usually, you would just create a window with a somewhat lower opacity, allowing the user to see what’s beneath your window. However, this window will still receive all the normal windows events, and mostly, the user’s mouse clicks. To prevent this, all you have to do is override the window’s CreateParams accessor, and add to the ExStyle property the WS_EX_TRANSPARENT value. Let’s have a look:
protected override CreateParams CreateParams
{
    get
    {
        CreateParams cp = base.CreateParams;
        cp.ExStyle = 0×00000020;
        return cp;
    }
}

Labels: , ,

Filtering Win32 messages of a form

I've recently came across a nice post on C# Frequently Asked Questions blog. In the post, they've shown a way to filter out, or (pre)process Win32 messages of a form. Let's have a look at the code, then we’ll go through it.
public class MyMessageFilter : IMessageFilter
{
    public bool PreFilterMessage(ref Message m)
    {
        // Intercept the left mouse button down message
        if (m.Msg == 513)
        {
            Console.WriteLine("WM_LBUTTONDOWN is: " + m.Msg);
            return true;
        }
        return false;
    }
}
And in the form itself:
public class mainForm : System.Windows.Forms.Form
{
    private MyMessageFilter msgFliter = new MyMessageFilter();

    public mainForm()
    {
        // Register message filter
        Application.AddMessageFilter(msgFliter);
    }
}
As you can see, we've started by creating a class that implements the IMessageFilter interface. Then we've added an instance of that class to the form, and in the constructor registered it. We can use the Application.RemoveMessageFilter() method to stop filtering messages using the specified filter. All the filter class does is handle the WM_LBUTTONDOWN message. By returning true, we specify that we've handled the message, and there's no need of any further handling of it - meaning that no event will be sent. This technique can be used either as a way to handle specific messages, or just ignore certain messages. Other nice things to try is to catch the message only a certain amount of times, or to "hide" some of the messages when certain requirements are met.

Labels: , ,

Unbound items in a bound ComboBox

You are once again invited to read, reply and vote on my new article at CodeProject. The article demonstrates how to add unbound items to a bound ComboBox. The code uses a wrapper that implements the IList interface, and supply the ComboBox both the extra items, that are kept at the beginning of the drop-down list, and the normal items, taken from the DataSource. The article can be found here: http://www.codeproject.com/useritems/UnboundItemsComboBox.asp

Labels: , , ,

Fading form while moving

The different between a nice application and an exellent one, is many time only in the small details. How much did the developers really put into it? How much they thought about every detail? The following code is nothing more than an eye-candy, but we all know that it’s those simple things, that take seconds to write that make the difference. Let’s write 2 methods to increase and decrease the opacity of a window:

private void DecreaseOpacity()
{
    while (this.Opacity > 0.2)
    {
        this.Opacity -= 0.02;
        double lastOpacity = this.Opacity;
        Thread.Sleep(10);
        if (this.Opacity != lastOpacity)
            break;
    }
}
private void IncreaseOpacity()
{
    while (1 > this.Opacity)
    {
        this.Opacity += 0.04;
        Thread.Sleep(10);
    }
}

Now, we want the form do fade out when the user moves it, and fade back in when he releases the form. For this, we’ll catch the WM_ENTERSIZEMOVE and WM_EXITSIZEMOVE messages of the window, and call the above methods in a different thread:

protected override void WndProc(ref Message m)
{
    switch(m.Msg)
    {
        case (int)0×231: //WM_ENTERSIZEMOVE
            Thread t = new Thread(new ThreadStart(DecreaseOpacity));
            t.Start();
            break;
        case (int)0×232: //WM_EXITSIZEMOVE
            Thread t2 = new Thread(new ThreadStart(IncreaseOpacity));
            t2.Start();
            break;
    }
    base.WndProc(ref m);
}

Labels: , ,

Wednesday, September 5, 2007

Prevent changing the state of a CheckBox

Preventing an item in a CheckedListBox or a ListView with CheckBoxed is a really basic requirement when dealing with GUI. The simple and elegant code is for some reason, quite under-sampled. Assuming that you know which item you want to prevent the user from changing, all you have to do is register on the ItemCheck event off either the CheckedListBox or the ListView, and write the following event handler:
private void checkedListBox1_ItemCheck(object sender, System.Windows.Forms.ItemCheckEventArgs e)
{
    if (e.Index == 0)
        e.NewValue = e.CurrentValue;
}
One could add grayed-out coloring to the item, to visually notify the user that the item is disable, and of course, obviously you could keep a list of all indexes that are to be disabled this way. If you take a look at my previous post, and expand it to a CheckedListBox, you can just as easily keep the Enabled state of the item in the obect itself, and use that in the ItemCheck event.

Labels: , ,

Tuesday, September 4, 2007

Using ComboBox with objects

In most of the cases where you need to use a ComboBox, you need it only so the user will be able to choose between a couple of string values. In the majority of the cases, you’ll have a list of objects that you’ll want the user to choose from, and you’ll have to do something similar to this:
Hashtable idToItem = new Hashtable();
foreach (Item item in listOfItems)
{
    int index = comboBox.Add(item.DisplayName);
    idToItem[index] = item;
}
We can now use the SelectedIndex field of the ComboBox, in conjuction with the Hashtable to retrieve the selected item. While the above code works perfectly, it does consume more memory, and takes more time than needed. And while you might say that both the time and memory wasted are negligible, try to use 10,000 items to a ComboBox, that go enjoy a cup of coffee while you wait for things to move around… There exist a simple and elegant way of doing the same thing. The DisplayMember field of the ComboBox is mostly known in relation to binding the control to a data source. We are going to use it for something similar - our data sources will be the items themselves:
comboBox.DisplayMember = "DisplayName";
foreach (Item item in listOfItems)
{
    comboBox.Add(item);
}
Now we can just use the SelectedItem field of the ComboBox, casting it to the correct type.

Labels: , ,

Thursday, August 2, 2007

ListViewGroup - only one item per group

As I was working on a new GUI part of the software I'm working on, I needed to allow the user to choose only a single item in each category. I overloaded the ListView control, and override the OnItemCheck method. Here's the result:
public class SingleItemPerGroupListView : ListView
{
    public SingleItemPerGroupListView()
    { }

    protected override void OnItemCheck(ItemCheckEventArgs ice)
    {
        // Always behave regulary when user uncheck an item
        if (ice.NewValue == CheckState.Unchecked)
        {
            base.OnItemCheck(ice);
            return;
        }

        // Get the checked item
        ListViewItem lvi = this.Items[ice.Index];

        // If no item has no group, handle regulary
        if (lvi.Group == null)
        {
            base.OnItemCheck(ice);
            return;
        }

        // If another item is checked, uncheck item
        foreach (ListViewItem otherLvi in lvi.Group.Items)
        {
            if (otherLvi == lvi)
                continue;

            if (otherLvi.Checked)
            {
                otherLvi.Checked = false;
            }
        }
    }
}

Labels: , ,