BindingContext – List controls that share a datasource

This one had me stumped for a while! If you have multiple list controls (combo’s etc) on the same form set to the same datasource, the default behaviour is for the controls to share the form’s BindingContext ,thus synchronising the current position. This means when you change list position in say ‘Combo A’, the selected index is also changed in ‘Combo B’.

To avoid this behaviour requires just one line of code applied to at least one of your controls.
Phew!


comboBox1.BindingContext = new BindingContext();

Custom Messagebox

9 times out of 10 the standard Windows Forms messagebox will do, but for those occasions it won’t, it’s pretty straight forward to design your own. I have an enum ‘MyMessageBoxButtons’ set up to accept ‘ok’ and ‘okCancel’, I have also set the OK button to fill the width of it’s parent panel if it is the only button to be displayed (this messagebox form is being displayed full screen in a mobile application), but you get the idea.


public partial class MyMessagebox : Form
{
    private static MyMessagebox messageBox;
    private static DialogResult messageboxResult;

    public MyMessagebox ()
    {
        InitializeComponent();
    }

    public static DialogResult Show(string messageText)
    {
        messageBox = new MyMessagebox ();
        messageBox.lbMessageboxMessage.Text = messageText;
        ShowButtons(messageBox, MyMessageBoxButtons.Ok);
        messageBox.ShowDialog();

        return messageboxResult;
    }

    public static DialogResult Show(string messageText, string messageCaption, MyMessageBoxButtons buttons)
    {
        messageBox = new myMessagebox();
        messageBox.lbMessageboxMessage.Text = messageText;
        messageBox.lbMessageboxCaption.Text = messageCaption;
        ShowButtons(messageBox, buttons);
        messageBox.ShowDialog();

        return messageboxResult;
    }
    
    private static void ShowButtons(MyMessageBox myMessageBox, MyMessageBoxButtons buttons)
    {
        myMessageBox.btnMessageboxOk.Enabled = true;
        myMessageBox.btnMessageboxOk.Visible = true;

        if (buttons != MyMessageBoxButtons.Ok)
        {
            myMessageBox.btnMessageboxCancel.Enabled = true;
            myMessageBox.btnMessageboxCancel.Visible = true;
        }
        else
        {
            myMessageBox.btnMessageboxOk.Width = myMessageBox.pnMessageboxDetail.Width;
        }
    }

    private void btnMessageboxOk_Click(object sender, EventArgs e)
    {
        messageboxResult = DialogResult.OK;
        messageBox.Close();
    }

    private void btnMessageboxCancel_Click(object sender, EventArgs e)
    {
        messageboxResult = DialogResult.Cancel;
        messageBox.Close();
    }        
}

Using Reflection to open a form by name

Here is a quick example of how the System.Reflection namespace can be utilised to open form instances in a method that accepts the form name as a string. It also shows how you can pass across variables to forms with overloaded constructors.

It’s particularly useful when working with a main menu structure, such as I have in a current project loaded into a Treeview.


private void LoadNewForm(string formName)
{
    try
    {
        Assembly assembly = Assembly.GetExecutingAssembly();
        Type type = assembly.GetType(formName);

        //for this form I have an overloaded constructor, accepting a 'User' object
        ConstructorInfo ci = type.GetConstructor(new Type[1] { typeof(User) }); 
        object[] argVals = new object[] { CurrentUser };  //pass 'CurrentUser' variable to form constructor
        Form frm = (Form)ci.Invoke(argVals);

        frm.Show();
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.ToString());
    }
} 

Scrolling a picturebox

Can’t be bothered extending the picturebox control to allow scrolling?…me either! Dump a panel on a form and set it’s ‘AutoScroll’ property to true, now place a picturebox on this panel and set it’s SizeMode to ‘AutoSize’. And there you have it, a scrollable picturebox without writing a single line of code!…just the way I like it! 🙂

Drag and Drop from TreeView

This is pretty straight forward, in the sample code below it shows how to add a TreeView node with an image into a ListBox. You’ll need an ImageList loaded with images to link both the TreeView and ListBox to and set the ListView to a view that allows images, i.e ‘Tile’.

private TreeNode sourceNode;

private void treeView1_ItemDrag(object sender, ItemDragEventArgs e)
{
    sourceNode = (TreeNode)e.Item;

    if (sourceNode.Parent != null)
        DoDragDrop(sourceNode, DragDropEffects.Move);
}

private void DragEnter_Event(object sender, DragEventArgs e)
{
    if (e.Data.GetData(typeof(TreeNode)) != null)
        e.Effect = DragDropEffects.Move;
    else
        e.Effect = DragDropEffects.None;
}

private void listView1_DragDrop(object sender, DragEventArgs e)
{
    listView.Items.Add(sourceNode.Text, sourceNode.ImageIndex);
}

Just add the DragEnter event to the _DragEnter event of the controls you want to allow the DragDropEffects.Move to show on.

Minimize application to System Tray

A quick snippet to show how to minimize an application to the Windows System Tray using a NotifyIcon. Set the NotifyIcon text to be whatever you want displayed when you hover over it in the System Tray and be sure to set the Icon property too, else nothing will get displayed! Then add the following in the form_resize event:

private void wfMain_Resize(object sender, EventArgs e)
{
    if (FormWindowState.Minimized == WindowState)
        Hide();
}

And to restore it to it’s former state. simply add the following in the DoubleClick event of your NotifyIcon:

private void notifyIcon1_DoubleClick(object sender, EventArgs e)
{
    this.Show();
    WindowState = FormWindowState.Normal;
}

Decimal TextBox Mk2

Decided my decimal textbox was a bit shit so had another quick go this afternoon, this time I did away with pasting altogether and disabled right click on the control by creating a blank contextmenu and assigning it to the textbox. Anyway here it is:


using System;
using System.Windows.Forms;

namespace DecimalTextBox
{
    public partial class DTB : TextBox
    {
        public int DecimalLength { get; set; }
        int[] numZeros;
        ContextMenu cmPaste = new ContextMenu();

        public DTB()
        {
            this.ContextMenu = cmPaste;  
        }

        protected override void OnKeyPress(KeyPressEventArgs e)
        {
            e.Handled = CheckDecimal(this, e, DecimalLength);
        }

        protected override void OnLeave(EventArgs e)
        {
            if (this.Text == "")
                this.Text += "0";
            if (this.Text.IndexOf(".") == -1)
                this.Text += ".";
                        
            numZeros = new int[this.Text.IndexOf(".") + DecimalLength - this.Text.Length + 1];

            if (this.Text.IndexOf(".") >= this.Text.Length - DecimalLength)
            {
                for (int i = 0; i < numZeros.Length; i++)
                {
                    numZeros[i] = 0;
                    this.Text += numZeros[i];
                }
            }
        }
        
        private static bool CheckDecimal(object sender, KeyPressEventArgs e, int numDecimals)
        {
            if (char.IsNumber(e.KeyChar) || e.KeyChar == '.')
            {
                TextBox tb = sender as TextBox;
                int cursorPosLeft = tb.SelectionStart;
                int cursorPosRight = tb.SelectionStart + tb.SelectionLength;
 
                string result = tb.Text.Substring(0, cursorPosLeft) + e.KeyChar + tb.Text.Substring(cursorPosRight);
                string[] parts = result.Split('.');
                
                if (parts.Length > 1)
                {
                    if (parts[1].Length > numDecimals || parts.Length > 2)
                    {
                        return true;
                    }
                }
                return false;
            }
            else return (e.KeyChar != (char)Keys.Back);
        }
    }
}

Prevent Form being moved

I recently had a need to dock a form to the right hand side of the screen and prevent users from dragging it away. The form can be minimised into the system tray but not resized or moved, of course I could have removed the forms control box and drawn my own but wanted it to fit with the other forms in the project.

Method 1: Override WndProc.

protected override void WndProc(ref Message msg)
{
    const int WM_SYSCOMMAND = 0x0112;
    const int SC_MOVE = 0xF010;

    switch (msg.Msg)
    {
        case WM_SYSCOMMAND:
            int cmd = msg.WParam.ToInt32() & 0xfff0;
    
            if (cmd == SC_MOVE)
                return;
        
            break;
    }

    base.WndProc(ref msg);
}

Method 2: This is the preferred method and allows the form to be dragged onto multiple monitors but will always dock to the right hand side of whichever screen it is dragged to.

private const int scrnBuffer = 5;
private Screen scrn;

private void Form1_Move(object sender, EventArgs e)
{
    SizeForms();
}

private void SizeForms()
{
    scrn = Screen.FromControl(this);

    this.Height = scrn.WorkingArea.Height - scrnBuffer * 2;
    this.Location = new Point(scrn.WorkingArea.Right - this.Width - scrnBuffer, scrn.WorkingArea.Top + scrnBuffer);
}

Control Arrays in .NET

Back in the day when I dabbled briefly in VB6, creating an array of controls was pretty straight forward and it was something that I found hard to adjust to when I moved across to .NET. It’s actually very easy, I’ll show you…

Start a new Winforms project and drop a button on a form, then copy and paste the following code into the button’s click event:

private void button1_Click(object sender, EventArgs e)
{
    int numBtns = 5;   //number of buttons to create.

    for (int i = 0; i < numBtns; i++)
    {
        Button btns = new Button();
        btns.Size = button1.Size;
        btns.Location = new Point(0, 0 + btns.Height * i);   //so the buttons don't overlap.
        btns.Name = "Button" + i;
        btns.Text = btns.Name;
        btns.Click +=new EventHandler(btns_Click);   //so we know when a button is clicked.
        this.Controls.Add(btns);   //add them to the form.
    }
}

public void btns_Click(object sender, EventArgs e)
{
    Button myBtn = (Button)sender;   //the clicked button is sent across as 'sender' so cast this as a Button
    MessageBox.Show("You Clicked" + myBtn.Name);    //and you can use the object as you wish!
}

BAM! Simple as that, we’ve created an array of buttons and assigned them an on_click event handler. There is a Tag property that can be set on the Button control and can also be used to identify which button was clicked.

Transparent ListView Control???

Ok so the ListView control does not support transparency, this I know because last week I spent a couple of hours, first trying to override it’s OnPaint method and finally frustratedly trawling the internet to find out why I could not!  Essentially the ListView control in VS is just a wrapper to the old Win32 control, meaning there’s no simple way of implementing transparency.

Being a newbie to .NET I did not want the complication of learning and using hooks to achieve the desired effect, so did what I tend to always do in situations such as these and cheated!  By creating a pixel array of the background (which has a nice gradient effect) directly behind the ListView box, I was able to create a new Bitmap and use this as the Background Image of the LV, thus giving the illusion of transparency!  Ok ok, yes it’s a cheat and yes, it’s not a very good one but hey it looks the part!

My code is as follows and is almost certainly the least optimal way of achieving this…next step is to look more closely at the Bitmap class for a neater and faster method:

private void MakeTransparent(Control ctrl, int x, int y)
{
    Bitmap bMap = new Bitmap(this.BackgroundImage);
    Color[,] pixelArray = new Color[ctrl.Width, ctrl.Height];

    for (int i = 0; i < ctrl.Width; i++)
    {
        for (int j = 0; j < ctrl.Height; j++)
        {
            pixelArray[i, j] = bMap.GetPixel(x + i, y + j);
        }
    }

    Bitmap bmp = new Bitmap(ctrl.Width, ctrl.Height);

    for (int i = 0; i < ctrl.Width; i++)
    {
        for (int j = 0; j < ctrl.Height; j++)
        {
            bmp.SetPixel(i, j, pixelArray[i, j]);
        }
    }

    ctrl.BackgroundImage = bmp;
    ctrl.Location = new Point(x, y);
}

And the result…