Make a context-sensitive AcceptButton in C#


[AcceptButton]

You’ve probably seen applications, often on web sites, where the action that a form takes when you press Enter depends on the control that has focus at the time. For example, many login pages have a place for you to enter your user name and password if you have an account, or to enter other information if you want to create an account. If you type your user name and password and press Enter, you go to a login success or failure page. If you enter your name in the new account area, you go to a new account page.

A Windows Form’s AcceptButton property determines which button is triggered when you press Enter, but a form can have only one AcceptButton. This example uses the following code to change the AcceptButton depending on which control has the focus.

private void createGroupBox_Enter(object sender, EventArgs e)
{
    this.AcceptButton = createButton;
}

private void loadTableGroupBox_Enter(object sender, EventArgs e)
{
    this.AcceptButton = makeItemsButton;
}

private void createFindGroupBox_Enter(object sender, EventArgs e)
{
    this.AcceptButton = findButton;
}

When focus enters one of the form’s GroupBox controls, the corresponding Enter event handler sets the form’s AcceptButton property to whichever button is appropriate for that GroupBox. For example, when focus is in the Load Table GroupBox, the Enter key fires the Make Items button. When focus is in the Create/Find GroupBox, the Enter key fires the Find button.

If you look very closely at the picture, you’ll see that the form automatically highlights the AcceptButton by giving it a blue outline. (In Windows 11 with this set of window settings, anyway.) That means an alert user can tell which button is the AcceptButton at any given moment.

I wouldn’t necessarily use this technique for every program, but it can make the user’s life easier if they must perform a certain sequence of events many times as in this example where the user creates a table, populates it, and then searches for items in it.

[Essential Algorithms: A Practical Approach to Computer Algorithms]

Note: This program is taken from an algorithms book I wrote. The naming conventions don’t follow my usual style because publishers prefer the style that Microsoft uses, which I find harder to read. For more information on algorithms, see my book
Essential Algorithms: A Practical Approach to Computer Algorithms


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in controls, user interface | Tagged , , , , , , , , , | Leave a comment

A little light holiday reading

Here are a few book books that make excellent holiday reading! (Actually they make good reading any time. ;-))



[The C# Helper Top 100]
The C# Helper Top 100


[Interview Puzzles Dissected]
Interview Puzzles Dissected


[C# 24-Hour Trainer, 2nd Edition]
C# 24-Hour Trainer, 2nd Edition


[Beginning Software Engineering]
Beginning Software Engineering


[Essential Algorithms: A Practical Approach to Computer Algorithms]
Essential Algorithms: A Practical Approach to Computer Algorithms


[C# 5.0 Programmer's Reference]
C# 5.0 Programmer’s Reference


Follow me on Twitter   RSS feed   Donate




Posted in books | Tagged , , , , , , , , , , , | Leave a comment

Initialize a TreeView control to display a directory hierarchy in C#


[TreeView control]

This example adds the following LoadFromDirectory extension method to the TreeView control.

// Initialize the TreeView from a directory,
// its subdirectories, and their files.
public static void LoadFromDirectory(this TreeView trv,
    string directory, int folder_img, int file_img)
{
    DirectoryInfo dir_info = new DirectoryInfo(directory);
    AddDirectoryNodes(trv, dir_info, null, folder_img, file_img);
}

This method creates a DirectoryInfo object to represent the directory that should be loaded into the TreeView. It then calls the following AddDirectoryNode method.

// Add this directory's node and sub-nodes.
public static void AddDirectoryNodes(TreeView trv,
    DirectoryInfo dir_info, TreeNode parent, int folder_img, int file_img)
{
    // Add the directory's node.
    TreeNode dir_node;
    if (parent == null) dir_node = trv.Nodes.Add(dir_info.Name);
    else dir_node = parent.Nodes.Add(dir_info.Name);

    // Add the folder image.
    if (folder_img >= 0) dir_node.ImageIndex = folder_img;

    // Add subdirectories.
    foreach (DirectoryInfo subdir in dir_info.GetDirectories())
        AddDirectoryNodes(trv, subdir, dir_node, folder_img, file_img);

    // Add file nodes.
    foreach (FileInfo file_info in dir_info.GetFiles())
    {
        TreeNode file_node = dir_node.Nodes.Add(file_info.Name);
        if (file_img >= 0) file_node.ImageIndex = file_img;
    }
}

This method creates a TreeView node to represent the directory. If the parent parameter is null, it adds the node to the TreeView control’s Nodes collection. Otherwise it adds the new node below the parent node. (This allows a program to add multiple top-level directories to the TreeView if desired.)

If the folder_img parameter is at least 0, then the method sets the node’s ImageIndex property to that value so the node displays an image. In this example, folders display a little folder image.

Next, the method loops through the directory’s subdirectories, recursively calling AddDirectoryNodes for each. This makes the directory’s subdirectories come before the directory’s files.

Finally, the method loops through the directory’s files, adding nodes below the directory’s node. If the file_img parameter is at least 0, the method sets the new nodes’ ImageIndex properties so they display the file image. In this example, that’s a little picture of a document.

Two enhancements I can think of would be to display appropriate images for different file types and to provide a context menu so, for example, the user can open or delete a file. If I have time, I may work on those. If you build them first, post a comment below.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in controls, directories, extension methods, files, user interface | Tagged , , , , , , , , , , , , , | Leave a comment

Cast arrays from one reference type to another in C#

[cast arrays]

Every C# programmer knows that you can use a cast operator to convert one data type into another. For example, if the Employee class inherits from the Person class, then the following code creates an Employee object and then makes a Person variable that refers to the same object.

Employee employee = new Employee();
Person person = (Person)employee;

An Employee is a kind of Person, so it makes sense that the program should be able to treat an Employee object as if it were a Person object.

Fewer programmers know that you can also cast arrays. For example, the following code casts an array of Employee objects into an array of Person objects.

// Initialize the Employee array.
Employee[] employees =
{
    new Employee(...),
    ...
};

// Cast into an array of Person.
Person[] persons = (Person[])employees;

This creates an array of Person objects. Note that the objects refer to the original objects; they are not new Person objects. It’s as if you had cast each of the objects in the original employees array into Person objects individually.

This example uses the following Person and Employee classes.

class Person
{
    public string FirstName, LastName;
    public Person(string first_name, string last_name)
    {
        FirstName = first_name;
        LastName = last_name;
    }
    public string GetName()
    {
        return FirstName + " " + LastName;
    }
}

class Employee : Person
{
    public int EmployeeId;
    public Employee(string first_name, string last_name, int id)
        : base(first_name, last_name)
    {
        EmployeeId = id;
    }
    public string GetName()
    {
        return base.GetName() + " [" + EmployeeId.ToString() + "]";
    }
}

These classes are relatively straightforward.

The example then uses the following code to demonstrate array casting.

private void Form1_Load(object sender, EventArgs e)
{
    // Make an array of Employees.
    Employee[] employees =
    {
        new Employee("Terry", "Pratchett", 1001),
        new Employee("Christopher", "Moore", 1003),
        new Employee("Jasper", "Fforde", 1002),
        new Employee("Tom", "Holt", 1004),
    };
    foreach (Employee employee in employees)
        lstInitial.Items.Add(employee.GetName());

    // Cast the array into an array of Person.
    Person[] persons = (Person[])employees;
    foreach (Person person in persons)
        lstPersons.Items.Add(person.GetName());

    // Modify the Person objects.
    foreach (Person person in persons)
        person.FirstName = "* " + person.FirstName;

    // Display the modified Person[] array.
    foreach (Person person in persons)
        lstModifiedPersons.Items.Add(person.GetName());

    // Display the modified Employee[] array.
    foreach (Employee employee in employees)
        lstModifiedEmployees.Items.Add(employee.GetName());
}

The code first creates an array of Employee objects. It then loops through those objects calling GetName to add their names to a ListBox.

Next, the code casts the Employee array into an array of Person. It loops through that array calling GetName to add the Person names to a ListBox. (Quick Quiz: Why does the example invent a GetName method instead of overriding the classes’ ToString methods? Answer at the end of this post.)

The program then loops through the Person array and adds an asterisk to each Person object’s first name.

Finally, the program loops through the arrays again to show the results in the Person and Employee arrays. Even though the code changed the objects’ names in the Person array, the changes are seen in the Employee array because both arrays contain references to the same objects.

(Quick Quiz Answer: The Object class’s ToString method is declared virtual. That means if a descendant class overrides it, then that object uses the new version even if it is referenced by an ancestor class. In this example, all of the objects are Employee objects so they would use the Employee class’s version of the method even if you refer to an object by using a Person reference. For more information on virtual methods, see Override a parent class method in C#.)


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in arrays, syntax, variables | Tagged , , , , , , , , , , , , , | Leave a comment

Use a timer to make a form TopMost or BottomMost in C#

[BottomMost]

The example Make a form TopMost or BottomMost in C# uses one method for making a form bottommost. It overrides the form’s WndProc method and looks for the message WM_WINDOWPOSCHANGING. When it finds that message, the program uses the SetWindowPos API function to tell the form to move to the bottom of the window z-order.

As I mentioned in that post, there’s one situation that the program doesn’t handle: if you click on the form’s title bar, and then click on it again, the form moves to the top.

This example uses a different approach. It uses a timer to move the form to the bottom of the z-order periodically.

When you click one of the form’s buttons, the following code executes.

private void radOption_Click(object sender, EventArgs e)
{
    this.TopMost = radTopmost.Checked;
    tmrMoveToBottom.Enabled = radBottommost.Checked;
}

If the Topmost radio button is checked, the code sets the form’s TopMost property to true. The code enables the tmrMoveToBottom timer if the Bottommost radio button is checked.

The following code shows the timer’s Tick event handler.

// Move the form to the bottom.
private void tmrMoveToBottom_Tick(object sender, EventArgs e)
{
    MoveToBottom();
}

This code simply calls the following MoveToBottom method to move the form to the bottom of the stacking order.

private void MoveToBottom()
{
    UInt32 flags = SWP_NOSIZE | SWP_NOMOVE;
    if (!SetWindowPos(this.Handle, HWND_BOTTOM, 0, 0, 0, 0, flags))
        Console.WriteLine("Error in SetWindowPos");
}

This method uses the SetWindowPos API function to set the form’s position as described in the previous post.

This approach is simpler than the one used by the previous post, although it is a bit heavy-handed. At design time I set the timer’s Interval property to 500 so it fires twice per second. You can make it fire more or less frequently if you like. Firing more often makes the form pop down to the bottom more quickly, but it also uses more system resources.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in API, controls, forms, user interface | Tagged , , , , , , , , , , , , , , , , , | 2 Comments

Make a form TopMost or BottomMost in C#

[BottomMost]

Keeping a form on top of other windows is simple. Just set the form’s TopMost property to true.

Keeping a form below other windows is harder. To do that, the program must override its WndProc method, look for messages that might move the form to the top, and use SetWindowPos to move the form to the bottom when they occur.

This program starts with the following API declarations.

using System.Runtime.InteropServices;
...
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetWindowPos(
    IntPtr hWnd, IntPtr hWndInsertAfter,
    int x, int y, int cx, int cy, uint uFlags);

// Constants for detecting messages.
private const int WM_WINDOWPOSCHANGING = 0x0046;

// Constants for positioning the window.
private IntPtr HWND_BOTTOM = (IntPtr)1;
private const UInt32 SWP_NOMOVE = 0x0002;
private const UInt32 SWP_NOSIZE = 0x0001;

This code declares the SetWindowPos API function used to move the form to the bottom of the window stacking order. It defines the messages that the program must look for and the values it passes to SetWindowPos to move the window to the bottom.

The following code shows the form’s overridden WndProc method.

protected override void WndProc(ref Message m)
{
    // See if we should be bottommost.
    if (radBottommost.Checked)
    {
        if (m.Msg == WM_WINDOWPOSCHANGING)
        {
            // We're being activated. Move to the bottom.
            MoveToBottom();
            m.Result = (IntPtr)0;
        }
    }

    // Handle the message normally.
    base.WndProc(ref m);
}

This method executes when the form receives a message from Windows. If the Bottommost radio button is checked, the code examines the message to see what message the window is receiving. The WM_WINDOWPOSCHANGING message indicates that the window’s size, location, or position in the stacking order is changing. In that case, the code calls the following MoveToBottom method.

private void MoveToBottom()
{
    UInt32 flags = SWP_NOSIZE | SWP_NOMOVE;
    if (!SetWindowPos(this.Handle, HWND_BOTTOM, 0, 0, 0, 0, flags))
        Console.WriteLine("Error in SetWindowPos");
}

This code calls SetWindowPos, passing it the HWND_BOTTOM value to make it move the form to the bottom of the window stacking order. It also passes the flags SWP_NOSIZE and SWP_NOMOVE to indicate that SetWindowPos should not resize or move the form.

When the user clicks any of the radio buttons, the the following code executes.

private void radOption_Click(object sender, EventArgs e)
{
    this.TopMost = radTopmost.Checked;
    if (radBottommost.Checked) MoveToBottom();
}

If the Topmost button is checked, the code simply sets the form’s TopMost property.

If the Bottommost radio button is checked, the code calls MoveToBottom to immediately move the form to the bottom.

This code works … mostly. Suppose you make the form BottomMost. If you click and drag its title bar, it remains on the bottom of the stacking order. If you drag one of the form’s borders to resize it, it also remains on the bottom of the stacking order.

If you click on the form’s title bar, it also remains on the bottom. However, if you click on the title bar again, the form pops to the top. I think the problem is that the form is already activated so clicking it again doesn’t activate it and that’s where the WM_WINDOWPOSCHANGING message originates.

I have scoured the internet and tried all sorts of solutions, and none of them seem to handle this case. Perhaps it’s a change in Windows 10.

For now, I’m going to call this a feature. The program remains on the bottom most of the time, but there’s still a way to bring it to the top if you really must. To put it back on the bottom, either add a button to the program to do that, or drag the form slightly.

If you find a solution to this problem, please post a comment below.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in API, controls, forms | Tagged , , , , , , , , , , , , , , , , , | 1 Comment

Make a StatusLabel display its text even if it doesn’t fit in C#

[StatusLabel]

If the text displayed in a StatusLabel is too long to fit in the containing StatusStrip control, then by default the StatusLabel doesn’t display anything. I don’t know what the rationale behind that it is, but it makes the StatusLabel somewhat pointless.

I’ve seen people claim that setting the StatusLabel control’s Spring property to true will make it display properly. That doesn’t seem to work for me, at least in some versions of Visual Studio. It could depend on the version.

Another solution that seems to work consistently is to set the containing StatusStrip control’s LayoutStyle property to Flow instead of the default value Table. That makes the StatusLabel display as much of its text as will fit.

private void Form1_Load(object sender, EventArgs e)
{
    lblStatus.Text = "This text is much too long to fit in the " +
        "form when it is its normal size.";
    statusStrip1.LayoutStyle = ToolStripLayoutStyle.Flow;
}

That’s all there is to it.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in controls, user interface | Tagged , , , , , , , , , , , , | Leave a comment

Generate letters with given frequencies in C#

[frequencies]

This example generates random letters with the frequencies they have in normal English text. The following code shows how the example gets started.

// The letter frequencies. See:
// http://en.wikipedia.org/wiki/Letter_frequency
private float[] Frequencies =
{
    8.167f, 1.492f, 2.782f, 4.253f, 12.702f, 
    2.228f, 2.015f, 6.094f, 6.966f, 0.153f, 
    0.772f, 4.025f, 2.406f, 6.749f, 7.507f, 
    1.929f, 0.095f, 5.987f, 6.327f, 9.056f, 
    2.758f, 0.978f, 2.360f, 0.150f, 1.974f, 
    0.074f
};

// Random number generator.
private Random Rand = new Random();

// The ASCII value of A.
private int int_A = (int)'A';

// Make sure the frequencies add up to 100.
private void Form1_Load(object sender, EventArgs e)
{
    // Give any difference to E.
    float total = Frequencies.Sum();
    float diff = 100f - total;
    Frequencies[(int)'E' - int_A] += diff;
}

The Frequencies array holds the letters’ relative percentage frequencies as listed at Wikipedia. This example treats these numbers as percentages so, for example, the letter A should appear roughly 8.167% of the time. If these numbers are some other value such as fractions between 0 and 1, you can adjust the program accordingly. If they are counts, for example the number of occurrences in a particular piece of text, then you can add them up to convert them into percentages.

The code creates a Random object and defines the value of the letter A as an integer for later convenience.

The form’s Load event handler adds up all of the values in the Frequencies. In this example, the frequencies don’t add up to 100% because they didn’t on the Wikipedia page where I got them. To make the total add up to 100%, the code adds them up, subtracts the total from 100, and adds the difference to the letter E, increasing or decreasing the frequency of E slightly so the total is 100%.

When you click the Generate button, the following code executes.

// Generate random letters with the indicated frequencies.
private void btnGenerate_Click(object sender, EventArgs e)
{
    // Keep track of the number of each letter generated.
    int[] counts = new int[26];

    // Generate the letters.
    int num_letters = int.Parse(txtNumLetters.Text);
    string result = "";
    for (int i = 0; i < num_letters; i++)
    {
        // Generate a number between 0 and 100.
        double num = 100.0 * Rand.NextDouble();

        // See which letter this represents.
        for (int letter_num = 0; ; letter_num++)
        {
            // Subtract this letter's frequency from num.
            num -= Frequencies[letter_num];

            // If num <= 0, then this is the letter.
            if ((num <= 0) || (letter_num == 25))
            {
                char ch = (char)(int_A + letter_num);
                result += ch.ToString() +' ';
                counts[letter_num]++;
                break;
            }
        }
    }

    txtLetters.Text = result;
    txtLetters.Select(0, 0);

    // Display the frequencies.
    lstFrequencies.Items.Clear();
    for (int i = 0; i < counts.Length; i++)
    {
        char ch = (char)(int_A + i);
        float frequency = (float)counts[i] / num_letters * 100;
        string str = string.Format("{0}\t{1,6}\t{2,6}\t{3,6}",
            ch.ToString(),
            frequency.ToString("0.000"),
            Frequencies[i].ToString("0.000"),
            (frequency - Frequencies[i]).ToString("0.000"));
        lstFrequencies.Items.Add(str);
    }
}

For each of the letters it should generate, the program picks a random number between 0 (inclusive) and 100 (exclusive). It then loops over the values in the Frequencies array, subtracting them from the random value. When the random value reaches 0, the program uses the letter corresponding to the most recently subtracted frequency. Assuming Random generates numbers with reasonably uniform distribution (and it’s fairly good at that), this means each letter is picked with roughly the same frequency as that given in the Frequencies array.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in algorithms, mathematics | Tagged , , , , , , , , , , | Leave a comment

Make a text file a project resource in C#

[project resource]

This is handy for including a large amount of text as a project resource instead of putting the text directly in the code or including a file in the program’s installation package.

Open the Project menu and select Properties. Open the Add Resource dropdown and select Add Existing File. Select the text file and click Open.

Now the code can use it at run time as shown in the following code.

// Load the file resource.
private void Form1_Load(object sender, EventArgs e)
{
    txtQuotes.Text = Properties.Resources.Twain;
    txtQuotes.Select(0, 0);
}

In this example, the text file is called Twain.txt so the program can get its text from Properties.Resources.Twain.

That’s all there is to it. A simple trick, but sometimes useful.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in files | Tagged , , , , , , , , | Leave a comment

Make a class define and raise events in C#

[define and raise events]

This example uses a BankAccount class to manage a bank account (and to show how to define and raise events). If the program tries to remove more money from the account than its current balance, the BankAccount object raises an Overdrawn event. The event includes two parameters: an object named sender that holds a reference to the BankAccount object that is raising the event, and an OverdrawnArgs object.

The OverdrawnArgs object includes two useful properties. The DebitAmount property indicates the amount that the program is trying to remove from the account. The Allow property is a Boolean that the program can set to true to tell the BankAccount object to process the debit even though that will give the account a negative balance.

A program needs to follow five main steps to define and raise events.

Define the EventArgs Class

The object that the class passes to the event handler should inherit from EventArgs. The following code shows the OverdrawnArgs class used by this example.

// Used to hold information when the account is overdrawn.
class OverdrawnArgs : EventArgs
{
    // The amount being subtracted.
    public decimal DebitAmount;

    // Default is to not allow the account
    // to have a negative balance.
    public bool Allow = false;
}

This class provides the debit amount that is causing the account to be overdrawn. The Boolean Allow field lets the main program tell the object whether it should allow the debit even though that will give the account a negative balance.

Note that the OverdrawnArgs class doesn’t include the account’s current balance. The main program can get that from the BankAccount object itself. It could not get the debit amount in that way, so that is included in the OverdrawnArgs class.

(For bonus points you could make DebitAmount be a read-only property. You could place it and the BankAccount in an assembly and use the internal keyword to let the BankAccount class set the value but not let the main program set it. Or you could make the constructor set the value. Usually programmers just leave this kind of property read-write and then ignore any changes made by the program.)

Define the Event

The BankAccount class needs to define the event. The event specifies the delegate type of the event handler. You can define the delegate type explicitly as in the following code.

// Raised if the program tries to debit more
// than the account's balance.
public delegate void OverdrawnEventHandler(
    object sender, OverdrawnArgs args);
public event OverdrawnEventHandler Overdrawn;

What this code means is the Overdrawn event should be handled by an event handler that takes two parameters: an object named sender representing the object that raised the event and an OverdrawnArgs object.

Alternatively you can use the predefined EventHandler delegate type as shown in the following code.

public event EventHandler<OverdrawnArgs> Overdrawn;

This statement is roughly equivalent to the previous two statements that first defined a separate delegate type.

This version is easier (and was the whole point behind Microsoft inventing the predefined EventHandler type), so that’s the way this example does it.

Raise the Event

Having defined the event, the class should raise it whenever appropriate. The following code shows how the BankAccount class raises the event.

// The account's current balance.
public decimal Balance { get; set; }

// Subtract from the account.
public void Debit(decimal amount)
{
    if (amount < 0) throw new
        ArgumentOutOfRangeException(
            "Debit amount must be positive.");

    // See if the account holds this much money.
    if (Balance >= amount)
    {
        // There is enough money. Subtract the amount.
        Balance -= amount;
    }
    else
    {
        // There isn't enough money.
        // Raise the Overdrawn event.
        if (Overdrawn != null)
        {
            // Make the OverdrawnArgs object.
            OverdrawnArgs args = new OverdrawnArgs();
            args.DebitAmount = amount;

            // Raise the event.
            Overdrawn(this, args);

            // If the program wants to allow the account
            // to have a negative balance, remove the money.
            if (args.Allow) Balance -= amount;
        }
    }
}

The Balance field holds the account’s current balance.

The Debit method is where the class raises the event. First, the method checks whether the amount being debited is less than zero and throws an exception if it is.

Next, the method checks the account’s balance. If the balance is big enough to cover the debit, the method subtracts it from the balance.

If the balance is insufficient, the method raises the Overdrawn event. The statement if (Overdrawn != null) checks whether any code has subscribed to the event. It’s a strange syntax, but if there are no event handlers registered for the event, then Overdrawn is null so the method doesn’t need to raise the event. In fact, if it tries to raise the event, it receives an “Object reference not set to an instance of an object” exception.

If an event handler is subscribed to the event, the method creates an OverdrawnArgs object and sets its DebitAmount. It then uses the syntax Overdrawn(this, args) to raise the event, passing the current object and the OverdrawnArgs object to the subscribed event handlers.

When the event handlers return, the method checks the OverdrawnArgs object’s Allow value to see if it should debit the account anyway. If Allow is true, the method subtracts the debit amount from the current balance.

Subscribe to the Event

If the class you are making is a component or control, you can use the Properties window’s event display to create event handlers for it. Otherwise you need to subscribe to event handlers in the code.

The following code shows how the main program initializes its BankAccount object and subscribes to that object’s Overdrawn event.

// The BankAccount object this program manages.
private BankAccount TheBankAccount;

// Initialize the BankAccount object.
private void Form1_Load(object sender, EventArgs e)
{
    TheBankAccount = new BankAccount();
    TheBankAccount.Balance = 100m;
    txtBalance.Text = TheBankAccount.Balance.ToString("C");

    // Subscribe to the Overdrawn event.
    TheBankAccount.Overdrawn += Account_Overdrawn;
}

The form’s Load event handler creates the BankAccount object, gives it a $100.00 balance, and displays the current balance.

It then registers the Account_Overdrawn method described next to handle the BankAccount object’s Overdrawn event.

Handle the Event

When the BankAccount object raises its Overdrawn event, the following Account_Overdrawn event handler executes.

// Handle the account's Overdrawn event.
private void Account_Overdrawn(object sender, OverdrawnArgs e)
{
    // Get the account.
    BankAccount account = sender as BankAccount;

    // Ask the user whether to allow this.
    if (MessageBox.Show("Insufficient funds.\n\n    Current balance: " +
        account.Balance.ToString("C") + "\n    Debit amount: " +
        e.DebitAmount.ToString("C") + "\n\n" +
        "Do you want to allow this transaction anyway?",
        "Allow?", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
            == DialogResult.Yes)
    {
        // Allow the transaction anyway.
        e.Allow = true;
    }
}

This method converts the sender parameter into a BankAccount object. (This is the first step in many event handlers. It’s too bad the predefined EventHandler delegate type doesn’t take two type parameters so you could make the sender parameter have type BankAccount instead of object. Then you could skip this step. Sadly that’s not the way EventHandler is defined.)

Next, the event handler displays a message box giving information about the transaction and asking the user if it should allow the transaction. If the user clicks Yes, the code sets the OverdrawnArgs object’s Allow field to true so the BankAccount object that raised the event knows that it should allow the transaction.

This may seem like a lot of steps, but it’s not too bad once you get used to it.


Download Example   Follow me on Twitter   RSS feed   Donate




Posted in classes, events | Tagged , , , , , , , , , , , , | 4 Comments