Вы находитесь на странице: 1из 149

1. Windows Forms Deployment WinForms FAQ Home 1.1 How can I run an EXE from within my application? 1.

2 What are the common issues in redirecting assemblies using the publisher policy files? 1.3 How can I get a list of all processes running on my system? 1.4 How can I find all programs with a GUI (not just arbitrary windows) that are running on my local machine? 1.5 How can I make sure there is only one instance of my application running? 1.6 How do I determine which operating system is running? 1.7 How can I get all IP addresses for my local machine? 1.8 My user does not have .NET installed. Will he be able to run my Windows Forms application? 1.9 How do I get the path to my running EXE? 1.10 How can I programmatically obtain the name of the assembly that the code is executing in ? 1.11 How can I see what is installed in the Global Assembly on a machine? 1.12 How do I set the company name that is returned by System.Windows.Forms.Application.CompanyName?

1.1 How can I run an EXE from within my application? Use the Process class found in the System.Diagnostics namespace. [C#] Process proc = new Process(); proc.StartInfo.FileName = @"Notepad.exe"; proc.StartInfo.Arguments = ""; proc.Start(); [VB.NET] Dim proc As New Process() proc.StartInfo.FileName = "Notepad.exe" proc.StartInfo.Arguments = "" proc.Start()

1.2 What are the common issues in redirecting assemblies using the publisher policy files? 1) Make sure to follow proper naming conventions for the policy dll. For example, if the original assembly name is TestAssembly.dll then the corresponding policy assembly should be called "policy.1.0.TestAssembly.dll" to make this redirection work for all "1.0.*" version bindings of the

original assembly. 2) While specifying the name for the assembly in the policy file, do not include the ".dll" extension. This is wrong: >assemblyIdentity name="TestAssembly.dll" publicKeyToken="f638d0a8d5996dd4" culture="neutral" /< Instead use: >assemblyIdentity name="TestAssembly" publicKeyToken="f638d0a8d5996dd4" culture="neutral" /< 3) Make sure to sign the policy assembly with the same strong name as the original. 4) Make sure to distribute the policy file along with the policy assembly. Installing the policy assembly in the GAC alone will not suffice. Note that any change made to the policy file after creating the policy assembly will not take effect. 5) Always use /link (to the policy file) in the "al" command while creating the policy assembly. Do not use /embed. It doesn't seem to be supported. Some good links: http://msdn.microsoft.com/library/default.asp?url=/library/enus/cpguide/html/cpconcreatingpublisherpolicyfile.asp http://www.newtelligence.com/downloads/downloads-basta2001.aspx http://www.only4gurus.com/DotNet/studies/managevers.htm 1.3 How can I get a list of all processes running on my system? Use the static Process.GetProcesses() found in the System.Diagnostics namespace. [C#] Using System.Diagnostics; ... foreach ( Process p in Process.GetProcesses() ) Console.WriteLine( p ); // string s = p.ToString(); [VB.NET] Imports System.Diagnostics ... Dim p As Process For Each p In Process.GetProcesses() Console.WriteLine(p) ' string s = p.ToString() Next p

1.4 How can I find all programs with a GUI (not just arbitrary windows) that are running on my local machine? You could use EnumWindows with p/Invoke, but using the static Process.GetProcesses() found in the System.Diagnostics namespace will avoid the interop overhead. [C#] Using System.Diagnostics; ... foreach ( Process p in Process.GetProcesses(System.Environment.MachineName) ) { if( p.MainWindowHandle != IntPtr.Zero) { //this is a GUI app Console.WriteLine( p ); // string s = p.ToString(); } } [VB.NET] Imports System.Diagnostics ... Dim p As Process For Each p In Process.GetProcesses(System.Environment.MachineName) If p.MainWindowHandle <> IntPtr.Zero Then 'this is a GUI app Console.WriteLine(p) ' string s = p.ToString(); End If Next p There is one potential problem on Windows 98. If a process was started with ProcessStartInfo.UseShellExecute set to true, this MainWindowHandle is not available. 1.5 How can I make sure there is only one instance of my application running? Saar Carmi shows how to do this in his sample found on C# Corner. In it, he uses the Process class in System.Diagnostics to implement this functionality using code such as [C#] public static Process RunningInstance() { Process current = Process.GetCurrentProcess(); Process[] processes = Process.GetProcessesByName (current.ProcessName); //Loop through the running processes in with the same name foreach (Process process in processes) { //Ignore the current process

if (process.Id != current.Id) { //Make sure that the process is running from the exe file. if (Assembly.GetExecutingAssembly().Location.Replace("/", "\\") == current.MainModule.FileName) { //Return the other process instance. return process; } } } //No other instance was found, return null. return null; } [VB.NET] Public Shared Function RunningInstance() As Process Dim current As Process = Process.GetCurrentProcess() Dim processes As Process() = Process.GetProcessesByName(current.ProcessName) 'Loop through the running processes in with the same name Dim process As Process For Each process In processes 'Ignore the current process If process.Id <> current.Id Then 'Make sure that the process is running from the exe file. If [Assembly].GetExecutingAssembly().Location.Replace("/", "\") = current.MainModule.FileName Then 'Return the other process instance. Return process End If End If Next process 'No other instance was found, return null. Return Nothing End Function 'RunningInstance

1.6 How do I determine which operating system is running? The Environment class in the System namespace has this information. [C#] string versionText = Environment.OSVersion.Version.ToString(); [VB.NET] Dim versionText As String = Environment.OSVersion.Version.ToString() The Version property has member properties such as Major and Minor that give additional

information. Note that XP is windows version 5.1 1.7 How can I get all IP addresses for my local machine? [C#] string s =""; System.Net.IPAddress[] addressList = Dns.GetHostByName(Dns.GetHostName()).AddressList; for (int i = 0; i < addressList.Length; i ++) { s += addressList[i].ToString() + "\n"; } textBox1.Text = s; [VB.NET] Dim s As String = "" Dim addressList As System.Net.IPAddress() = Dns.GetHostByName(Dns.GetHostName()).AddressList Dim i As Integer For i = 0 To addressList.Length - 1 s += addressList(i).ToString() + ControlChars.Lf Next i textBox1.Text = s

1.8 My user does not have .NET installed. Will he be able to run my Windows Forms application? No, the .NET runtime platform has to be on any machine that will run your Windows Forms application. Microsoft has made the .NET runtime platform installation (dotnetredist.exe) available as a free download from Microsoft .NET Framework Redistributable . 1.9 How do I get the path to my running EXE? The Application class has a static member ExecutablePath that has this information. [C#] textBox1.Text = Application.ExecutablePath; [VB.NET] TextBox1.Text = Application.ExecutablePath

1.10 How can I programmatically obtain the name of the assembly that the code is executing in ? The following code snippet demonstrates how you can obtain the name of the assembly that the

code is executing in: [C#] MessageBox.Show(System.Reflection.Assembly.GetEntryAssembly().GetName().Name); [VB.NET] MessageBox.Show(System.Reflection.Assembly.GetEnTryAssembly().GetName().Name)

1.11 How can I see what is installed in the Global Assembly on a machine? Use Windows Explorer to view the C:\WINNT\assembly folder. If the .NET Framework is installed, the Windows Explorer will show a custom view of this folder. Use the detailed view to see the details of each assembly. 1.12 How do I set the company name that is returned by System.Windows.Forms.Application.CompanyName? This is an assembly attribute. The VS development environment sets it in the AssemblyInfo.cs (vb) file. If you open that file, you will see a block of assembly attributes that you can set, including company and version numbers. [assembly: AssemblyCompany("Syncfusion, Inc.")]

2. Windows Forms Controls WinForms FAQ Home 2.1 How do you prevent serialization of certain child controls in your Composite Control? 2.2 How do I get hold of the currently focused Control? 2.3 Why do calling Focus() on a control not set focus on it? 2.4 How do I listen to windows messages in my Control? 2.5 How do I programatically change the color of a control? 2.6 How can I change the Border color of my control? 2.7 Why should I provide a Non-Client border to my Control derived class? 2.8 How do I provide a 2 pixel 3d border in the Non-Client area of my Control derived class? 2.9 How do I provide a 1 pixel border in the NonClient area of my Control? 2.10 How do I invalidate a control including it's NonClient area? 2.11 How can I implement a scrollable picture box? 2.12 How can I put Controls, a ProgressBar for example, into a StatusBar? 2.13 How do I implement an ownerdrawn statusbar so I can put a progressbar in it? 2.14 How would I change the icon that appears on the toolbox for a custom control? 2.15 I would like to prevent validation in my textbox when the user clicks on my Cancel button, how do I do this?

2.16 A control's Validating event is hit even when the user clicks on the Close box. How can I avoid this behavior? 2.17 Why does adding images to an ImageList in the designer cause them to lose their alpha channel? 2.18 Why do the XP Icons that have alpha channel not draw properly when associated with controls like ListView? 2.19 Why do the XP Icons when drawn using Graphics.DrawImage not draw transparently? 2.20 How do I prevent resizing of my Controls by the user, via Docking or anchoring or manual resizing during design-time? 2.21 How do I listen to the screen resolution change in my control? 2.22 How do I determine which button in a Toolbar is clicked? 2.23 I set a Control's Visible property to true and in the immediate next statement, it returns false. Why doesn't setting the Visible property 'take'? 2.24 I'm trying to make the background of my linklabel transparent so a picturebox will show through it. However, if I set the link label's BackColor property to Transparent the label still has a white background. Why? 2.25 How do I dynamically load a control from a DLL? 2.26 How can I make my controls transparent so the form's background image can show through them? 2.27 How do I create an editable listbox with an in-place TextBox and Button? 2.28 What control do I use to insert Separator lines between Controls in my Dialog? 2.29 How can I programmatically manipulate Anchor styles? 2.30 What is the best method to override in custom Controls to perform custom initialization during runtime? 2.31 How do I determine the width/height of the Non-Client area (like the border in a textbox) of a Control? 2.32 What is the (DynamicProperties) item listed on a control's property page in VS.NET? 2.33 In the property browser for a custom control, how do I disable a property initially, but enable it later based on some other property changing? 2.34 How can I have the control designer create the custom editor by calling the constructor with the additional parameters rather than the default constructor? 2.35 How can I make a Panel or Label semi-transparent on a Windows Form? 2.36 How do I make the arrow keys be accepted by a control (such as a button) and not handled automatically by the framework's focus management? 2.37 How can I add a control to a Window Form at runtime?

2.1 How do you prevent serialization of certain child controls in your Composite Control? One solution is to use a panel that has a picturebox placed on it with DockStyle.Fill. This will

make the picturebox assume the size of the panel. In addition, set the DockPadding.All property to the width of the desired border. Then in the Panel's OnPaint method, call the baseclass and then paint the desired borders. Here are both VB and C# projects that illustrate how you might go about this. The derived PicturePanel class has properties that allow you to set the bordersize and color as well as the image that is to be displayed. This sample retrieves the image from an embedded 2.2 How do I get hold of the currently focused Control? The .Net framework libraries does not provide you an API to query for the focused Control. You have to invoke a windows API to do so: [C#] public class MyForm : Form { [DllImport("user32.dll", CharSet=CharSet.Auto, CallingConvention=CallingConvention.Winapi)] internal static extern IntPtr GetFocus(); private Control GetFocusedControl() { Control focusedControl = null; // To get hold of the focused control: IntPtr focusedHandle = GetFocus(); if(focusedHandle != IntPtr.Zero) // Note that if the focused Control is not a .Net control, then this will return null. focusedControl = Control.FromHandle(focusedHandle); return focusedControl; } } [VB.Net] Public Class Form1 ' Declare the GetFocused method here: _ Public Shared Function GetFocus() As IntPtr End Function Private Function GetFocusedControl() As Control Dim focusedControl As Control = Nothing ' To get hold of the focused control: Dim focusedHandle As IntPtr = GetFocus() If IntPtr.Zero.Equals(focusedHandle) Then ' Note that if the focused Control is not a .Net control, then this will return null. focusedControl = Control.FromHandle(focusedHandle) End If Return focusedControl

End Function End Class

2.3 Why do calling Focus() on a control not set focus on it? We have a small sample that shows how to do this. Download PropTab.zip. After you download and unzip the sample, build the project. Both the control assembly and a small driver assembly get built. After that add the control to your toolbox using 'Customise toolbox...'. Then drag and drop an instance of the added control onto the form in the driver winforms sub-project. When you select this control the properties window should have a tab with a bitmap 'T' as shown below.

These are the steps involved. Before following these steps please download and take a look at the sample. That will greatly help when following these steps. Control related steps Assume that you have a control that has two sets of properties, one set that you wish to have displayed in the main property tab and another set that you wish to have displayed in the second tab.
y y

Mark those properties that you wish to display in the first tab with the BrowsableAttribute to true. Mark those properties that you wish to display in the second tab with the BrowsableAttribute set to false. Create another attribute. Name it anything you want and give it a single boolean property. Initialize this property to true.

Other steps
y

Derive a class from System.Windows.Forms.PropertyGridInternal.PropertiesTab. You

have to override a few methods. The most important of these is GetProperties. We override GetProperties as shown below to use our CustomAttribute as the filtering attribute instead of the BrowsableAttribute that the PropertyGrid uses by default. public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object component, Attribute[] attrs) { return TypeDescriptor.GetProperties(component, new Attribute[] {new BrowsableAttribute(false), new CustomTabDisplayAttribute(true)}); } y Create a embedded resource bitmap with the same name as the derived tab's type. This bitmap had to be 16x16. A brief note of explanation on the sample. The sample shows a user control that displays its own tab for some properties. These properties are marked with the filtering attribute, CustomTabDisplayAttribute. The rest of the properties are just displayed in the normal property tab and need no special attention. To get a deeper understanding of how this works, please refer to these FAQs on TypeConverters and TypeDescriptors.
y y

TypeDescriptors TypeConverters

2.4 How do I listen to windows messages in my Control? In a derived class you should override WndProc as follows (listening to the WM_KEYUP message, for example): [C#] public class MyCombo : ComboBox { private const int WM_KEYUP = 0x101; protected override void WndProc(ref System.Windows.Forms.Message m) { if(m.Msg == WM_KEYUP) { return; //ignore the keyup } base.WndProc(ref m); } } [VB.NET] Public Class MyTextBox Inherits TextBox

Private WM_KEYUP As Integer = &H101 Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message) If m.Msg = WM_KEYUP Then Return 'ignore the keyup End If MyBase.WndProc(m) End Sub 'WndProc End Class 'MyTextBox

2.5 How do I programatically change the color of a control? Use the properties BackColor and ForeColor. button1.BackColor = Color.White; button1.ForeColor = Color.Blue;

2.6 How can I change the Border color of my control? Override the OnPaint. Here is some code for a derived Button. [C#] public class MyButton : Button { protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); int borderWidth = 1; Color borderColor = Color.Blue; ControlPaint.DrawBorder(e.Graphics, e.ClipRectangle, borderColor, borderWidth, ButtonBorderStyle.Solid, borderColor, borderWidth, ButtonBorderStyle.Solid, borderColor, borderWidth, ButtonBorderStyle.Solid, borderColor, borderWidth, ButtonBorderStyle.Solid); } } [VB.NET] Public Class MyButton Inherits Button Protected Overrides Sub OnPaint(e As PaintEventArgs) MyBase.OnPaint(e) Dim borderWidth As Integer = 1 Dim borderColor As Color = Color.Blue ControlPaint.DrawBorder(e.Graphics, e.ClipRectangle, borderColor, borderWidth, ButtonBorderStyle.Solid, borderColor, borderWidth, ButtonBorderStyle.Solid, borderColor, borderWidth, ButtonBorderStyle.Solid, borderColor, borderWidth, ButtonBorderStyle.Solid) End Sub 'OnPaint

End Class 'MyButton

2.7 Why should I provide a Non-Client border to my Control derived class? Providing a border in the non-client region of your control rather than in the ClientRectangle has very many advantages: y When you include a scrollbar in your control, the scrollbar will appear inside the border, rather than to the outside if you drew the border in the client area. y When you allow custom painting of the control, your user will not draw over the NC border. y Your own client painting code will be simplified in that you will not have to bother about taking the border into account while painting the client area. The next faq will tell you how to include a non-client border. 2.8 How do I provide a 2 pixel 3d border in the Non-Client area of my Control derived class? You can do so as follows by overriding the CreateParams property in your Control. The advantage with this approach is that drawing is handled by the system as soon as you set the flag below. protected override CreateParams CreateParams { get { CreateParams cparams; cparams = base.CreateParams; if(this.need3DBorder) { cparams.ExStyle &= ~512; cparams.Style &= ~8388608 /*WS_BORDER*/; cparams.ExStyle = cparams.ExStyle | 512 /*WS_EX_DLGFRAME*/; } return cparams; } }

2.9 How do I provide a 1 pixel border in the NonClient area of my Control? You will have to first provide some space in the NC area by setting the WS_BORDER flag in CreateParams and then draw the border yourself by listening to the WM_NCPAINT message in your Control, as follows: protected override CreateParams CreateParams

{ get { System.Windows.Forms.CreateParams cp = base.CreateParams; if(this.needFlatBorder) { cparams.ExStyle &= ~512 /*WS_EX_CLIENTEDGE*/; cparams.Style &= ~8388608 /*WS_BORDER*/; cp.Style |= 0x800000; // WS_BORDER } } } protected override void WndProc(ref Message m) { if(m.Msg == 133/*WM_NCPAINT*/) { this.DrawFlatNCBorder(ref m); } base.WndProc(ref m); } private void DrawFlatNCBorder(ref Message msg) { IntPtr hRgn1 = (IntPtr) msg.WParam; // The update region is clipped to the window frame. When wParam is 1, the entire window frame needs to be updated. IntPtr hdc = NativeMethods.GetDCEx(msg.HWnd, hRgn1, 1/*DCX_WINDOW*/|0x0020/*DCX_PARENTCLIP*/); if (hdc != IntPtr.Zero) { using (Graphics g = Graphics.FromHdc(hdc)) { Rectangle bounds = new Rectangle(0,0,this.Width,this.Height); ControlPaint.DrawBorder(g,bounds,this.borderColor,ButtonBorderStyle.Solid); // create a clipping region for remaining parts to be drawn excluding // the border we did just drew bounds.Inflate(-1, -1); IntPtr hRgn2 = NativeMethods.CreateRectRgn(bounds.Left, bounds.Top, bounds.Right, bounds.Bottom); if(hRgn2 == (IntPtr)1) { // Provide a new clipping region. msg.WParam = (IntPtr) hRgn2; } else {

// combine with existing clipping region. NativeMethods.CombineRgn(hRgn1, hRgn1, hRgn2, NativeMethods.RGN_AND); NativeMethods.DeleteObject(hRgn2); } } msg.Result = (IntPtr) 1; NativeMethods.ReleaseDC(msg.HWnd, hdc); } Invalidate(); }

2.10 How do I invalidate a control including it's NonClient area? You can do so as follows in your Control: private void InvalidateWindow() { NativeMethods.RedrawWindow(this.Handle, IntPtr.Zero, IntPtr.Zero, 0x0400/*RDW_FRAME*/ | 0x0100/*RDW_UPDATENOW*/ | 0x0001/*RDW_INVALIDATE*/); }

2.11 How can I implement a scrollable picture box? Normally, the context menu will be shown at the center of the control when the keyboard is used to invoke the context menu of a control (Shift+F10, for example). You can customize the location as follows. 1. Override WndProc in the grid and check if the Message.Msg is WM_CONTEXTMENU (0x007b) 2. If so, check if the Message.LParam is -1, which means this was due to a keyboard message as opposed to the user right-clicking the mouse. 3. Now, call the associated ContextMenu's Show method explicitly at the selected row position. 4. Make sure NOT to call the base class after showing the menu explicitly. protected override void WndProc(ref Message m) { if(m.Msg == 0x007B /*WM_CONTEXTMENU*/) { // LParam == -1 means that this is due to a keyboard message if((int)m.LParam == -1) {

this.contextMenu.Show(); return; } } } [VB.Net] Protected Overrides Sub WndProc(ByRef m As Message) If m.Msg = 0x007B Then 'WM_CONTEXTMENU ' LParam == -1 means that this is due to a keyboard message If (CType(m.LParam,Integer)) = -1 Then Me.contextMenu.Show() Return End If End If End Sub

2.12 How can I put Controls, a ProgressBar for example, into a StatusBar? You cannot place controls into a StatusBar control in the designer. However, you can add any no. of Controls to the StatusBar programatically through it's Controls property. After adding the Controls, set their Visible, Location and Bounds property appropriately. You could then create a status bar that looks like this, for example:

2.13 How do I implement an ownerdrawn statusbar so I can put a progressbar in it? Check out the code posted originally in VB by Jacob Grass, and translated to C# by Jason Lavigne on the dotnet.discussion newsgroup at develop.com. 2.14 How would I change the icon that appears on the toolbox for a custom control? You can do this in different ways explained below. In all the cases the bitmap or icon should follow these rules:
y y

The bitmap or icon dimension should be 16X16 with 16 colors. The left-bottom pixel-color will be assumed to be the transparent color.

Technique 1: Use a bitmap (not an icon, in the embedded resource) file implicitly without specifying the ToolboxBitmapAttribute for the type: Say, you have a custom control MyControl in the namespace MyNamespace, create a bmp file MyControl.bmp following the above rules. Add this file to your project at the top-level and make it an embedded resource. The project's default namespace should be MyNamespace. If the control's namespace and the project's default namespace don't match then move the bitmap to appropriate subfolders so that they match. If this is not possible, typically when the namespaces are not related at all then you cannot use this technique, use instead one of the techniques below using the ToolboxBitmap attribute. Create the assembly and the next time you add it to the toolbox the custom image in MyControl.bmp should be available in the toolbox. This is the easiest technique to implement as it doesn't require you to use the ToolboxBitmapAttribute in your type definition. Technique 2: Use ToolboxBitmap attribute. Example 1: Use a bitmap (not icon) in the embedded resource with the same name as the type. Default Assembly Namespace: "MyAssemblyNamespace" namespace MyAssemblyNamespace { [ToolboxBitmap(typeof(MyCustomType))] public class MyCustomType : Component {...} } In the above scenario the runtime will look for a embedded bmp file of name MyCustomType.bmp in the project's root directory. Note that the default namespace and the type's namespace match. Example 2: If you want your icons in sub-directories then change the attribute like this: [ToolboxAttribute(typeof(MyCustomType), "ToolboxIcons.MyCustomType.bmp")] or [ToolboxAttribute(typeof(MyCustomType), "ToolboxIcons.MyCustomType.ico")] where the bmp or ico file (yap, now, when you explicitly specify the resource, you can use an ico

file) is in a sub-directory called "ToolboxIcons". Example 3: Sometimes your type's namespace and the default assembly namespace may be unrelated, in which case you have to use a different type that has the same namespace as the default assembly namespace to scope the embedded image file. Default namespace: "MyAssemblyNamespace" namespace MyAssemblyNamespace { public class SomeType {...} } namespace DifferentNamespace { // Using SomeType which has the same namespace as the default assembly namespace to scope the embedded resource. [ToolboxBitmap(typeof(SomeType), "MyCustomType.ico")] public class MyCustomType {...} } In this case the runtime will look for the above resource at the top-most directory. If your icons were in a subdirectory named "ToolboxIcons" then the attribute would look like this: [ToolboxBitmap(typeof(SomeType), "ToolboxIcons.MyCustomType.ico")]

2.15 I would like to prevent validation in my textbox when the user clicks on my Cancel button, how do I do this? Say textBox1 and cancelButton and the control names, then this is how you could do this: [C#] // Handler to the Validating event of the TextBox. private void TextBox_Validating(object sender, System.ComponentModel.CancelEventArgs e) { if (!this.cancelButton.Focused) { // Do this only when the cancel button is not clicked. if(invalidState) e.Cancel = true; } } [VB.Net] ' Handler to the Validating event of the TextBox. Private Sub TextBox_Validating(ByVal sender As Object, ByVal e As

System.ComponentModel.CancelEventArgs) If Not Me.cancelButton.Focused Then ' Do this only when the cancel button is not clicked. If invalidState Then e.Cancel = True End If End If End Sub

2.16 A control's Validating event is hit even when the user clicks on the Close box. How can I avoid this behavior? One way is to add code to your Validating handler and only execute the validation routine if the mouse is in the client area. This will avoid the click on the title bar and its system menu. You might also want to add special handling for the tab key so your validation is hit independent of the mouse location when you tab off the control. private bool tabKeyPressed = false; private void textBox1_Validating(object sender, System.ComponentModel.CancelEventArgs e) { if(tabKeyPressed || this.ClientRectangle.Contains(this.PointToClient(Cursor.Position))) { if(boolNotOKValues) //do your validating e.Cancel = true; //failed } tabKeyPressed = false; } protected override bool ProcessDialogKey(Keys keyData) { tabKeyPressed = keyData == Keys.Tab; return base.ProcessDialogKey(keyData); }

2.17 Why does adding images to an ImageList in the designer cause them to lose their alpha channel? Looks like the ImageList editor loses the transparency when it does some internal copy/clone of the images. However, it seems that it does work when you add the images in code to the ImageList. One workaround (not so tidy) is to add the images to the ImageList in the design time (so that your design-time will be closer to the runtime) and then clear that ImageList and refill it with the

images again, in code. Take a look at this faq on how to add images to your project and retrieve them programatically during runtime. Adding image files to a project as an embedded resource and retrieving them programatically. 2.18 Why do the XP Icons that have alpha channel not draw properly when associated with controls like ListView? Make sure that you include the manifest file that will enable XP themes support for you application. Then the icons with alpha channel will draw semi-transparently. 2.19 Why do the XP Icons when drawn using Graphics.DrawImage not draw transparently? Note that it's only the ImageList class that can draw an Icon with alpha channel properly. So, instead of using the Graphics.DrawImage method, first associate this icon to a ImageList and then use the ImageList.Draw method to draw the icon. Also, note that this is possible only in XP with XP Themes enabled for that application. 2.20 How do I prevent resizing of my Controls by the user, via Docking or anchoring or manual resizing during design-time? The best place to ensure a particular height/width for you control is in the SetBoundsCore override of your Control, as follows: protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { int prefHeight = this.GetPreferredHeight(); // Ensure that the height is atleast as big as prefHeight if(height < prefHeight) height = prefHeight; base.SetBoundsCore(x, y, width, height, specified); } Protected Overrides Sub SetBoundsCore(ByVal x As Integer, ByVal y As Integer, ByVal width As Integer, ByVal height As Integer, ByVal specified As BoundsSpecified) Dim prefHeight As Integer = Me.GetPreferredHeight() ' Ensure that the height is atleast as big as prefHeight If height < prefHeight Then height = prefHeight End If MyBase.SetBoundsCore(x, y, width, height, specified) End Sub

2.21 How do I listen to the screen resolution change in my control? You should override WndProc in your control/form and listen to the WM_DISPLAYCHANGE message. Refer to this faq a WndProc override sample: How do I listen to windows messages in my Control? http://www.syncfusion.com/faq/windowsforms/search/1064.aspx 2.22 How do I determine which button in a Toolbar is clicked? When you click on a button on a Toolbar the click is sent to the Toolbar, so you need to check the ToolBarButtonClickEventArgs's button property to determine the button on the Toolbar that was clicked. [C#] private void toolBar1_ButtonClick(object sender, System.Windows.Forms.ToolBarButtonClickEventArgs e) { //check if toolBarButton1 was clicked if (e.Button == toolBarButton1) { MessageBox.Show("Button 1 clicked"); } } [VB.NET] Private Sub toolBar1_ButtonClick(ByVal sender As Object, ByVal e As System.Windows.Forms.ToolBarButtonClickEventArgs) 'check if toolBarButton1 was clicked If e.Button = toolBarButton1 Then MessageBox.Show("Button 1 clicked") End If End Sub

2.23 I set a Control's Visible property to true and in the immediate next statement, it returns false. Why doesn't setting the Visible property 'take'? A control's Visible property will also depend on it's parent control's (if any) visible property. If the parent control is not visible, the control will also return false. 2.24 I'm trying to make the background of my linklabel transparent so a picturebox will show through it. However, if I set the link label's BackColor property to Transparent the label still has a white background. Why?

Controls with a "Transparent" color actually render their parent's background, so you're seeing the White background of the Form, not the PictureBox. Three easy ways to deal with this:
y y y

Use a Panel with it's "BackgroundImage" property set instead of a PictureBox, and parent the LinkLabels to the panel (PictureBoxes generally don't have children) Set the BackgroundImage of the Form to the image (basically the same as 1 above, but avoids the extra control) In code, set the Parent of the LinkLabel to be the PictureBox. You'll need to update the LinkLabel's position to match the new origin of the parent if the PictureBox isn't at (0,0)

(Shawn Burke on microsoft.public.dotnet.framework.windowsforms newsgroup) 2.25 How do I dynamically load a control from a DLL? You use System Reflection to dynamically load a control. If the DLL is named "SpecControls.DLL" and the class you want is "SpecControls.ColorControl", then use this code. [C#] // load the assembly System.Reflection.Assembly assembly = Assembly.LoadFrom("SpecControls.DLL"); // get the type Type t = assembly.GetType("MyControls.MyControl"); // create an instance and add it. // Control c = (Control)Activator.CreateInstance(t); parent.Controls.Add(c); [VB.NET] ' load the assembly Dim assembly1 As System.Reflection.Assembly = Assembly.LoadFrom("SpecControls.DLL") ' get the type Dim t As Type = assembly1.GetType("MyControls.MyControl") ' create an instance and add it. ' Dim c As Control = CType(Activator.CreateInstance(t), Control) parent.Controls.Add(c)

2.26 How can I make my controls transparent so the form's background image can show through them? By default, a control's background color will be the same as the container's background. In the picture below, the form's background was set to Color.Red. In design mode, you can see the four controls have the red background, but since the form's Image was set to a bitmap, you cannot see the form's red background. To make the controls' background transparent, you can set the alpha blend value of their background to zero. In code, you could use: public Form1() {

InitializeComponent(); checkBox1.BackColor = Color.FromArgb(0, checkBox1.BackColor); button1.BackColor = Color.FromArgb(0, button1.BackColor); linkLabel1.BackColor = Color.FromArgb(0, linkLabel1.BackColor); label1.BackColor = Color.FromArgb(0, label1.BackColor); // Or use the System.Drawing.Color.Transparent color. } In design mode, you can set the alpha blend value to zero by typing the four component in the property grid. So, for each control's BackColor property, you would type 0,255,0,0 to make it a transparent red. Here is a VB project using this idea. 2.27 How do I create an editable listbox with an in-place TextBox and Button? The attached EditableList UserControl implements an editable listbox with an in-place TextBox and Button allowing users to directly edit the contents of the list box. When the user clicks on a selected item, a textbox and a button is shown over the selected item and the user can directly edit the selected item text. The button can be programmed to show for example a OpenFileDialog to allow user to select a file (useful while implementing a Files list). 2.28 What control do I use to insert Separator lines between Controls in my Dialog? Use the Label Control with the BorderStyle set to Fixed3D and height set to 2. 2.29 How can I programmatically manipulate Anchor styles? You can do this using the bitwise operators &, | and ^ ( And, Or and Xor (or &, Or, ^) in VB.Net). Here is code that will toggle label1 being anchored on the left. [C#] private void button1_Click(object sender, System.EventArgs e) { if ((label1.Anchor & AnchorStyles.Left) == 0) { //add it label1.Anchor = label1.Anchor | AnchorStyles.Left; } else if ((label1.Anchor & AnchorStyles.Left) != 0) { //remove label1.Anchor = label1.Anchor ^ AnchorStyles.Left; } } [VB.NET]

Private Sub button1_Click(sender As Object, e As System.EventArgs) If(label1.Anchor And AnchorStyles.Left) = 0 Then 'add it label1.Anchor = label1.Anchor Or AnchorStyles.Left ElseIf(label1.Anchor And AnchorStyles.Left) <> 0 Then 'remove label1.Anchor = label1.Anchor Xor AnchorStyles.Left End If End Sub 'button1_Click

2.30 What is the best method to override in custom Controls to perform custom initialization during runtime? When custom initialization is to be done during runtime on certain Controls, the best way is to implement the ISupportInitialize interface in that Control. Then the BeginInit method will be called as soon as the Control gets created and EndInit will be called after the design-time initialization of that control. 2.31 How do I determine the width/height of the Non-Client area (like the border in a textbox) of a Control? One generic way for all Controls is to get the difference between the ClientRectangle's width and the Control.Bounds' width. That should give the border width (and in general the NC area width); similarly for height. 2.32 What is the (DynamicProperties) item listed on a control's property page in VS.NET? Clicking Advanced... under this DynamicProperties option in the control properties displays certain control properties that can be set through an XML app.config file that is added to your project. This file stores the dynamic property values and is automatically read during the initialization process on the form at design time. You can manually edit this file to change the properties and next when you run the application, controls will pick up the new values. This file is used strictly for design time initializations. 2.33 In the property browser for a custom control, how do I disable a property initially, but enable it later based on some other property changing? Implement ICustomTypeDescriptor, and provide your own PropertyDescriptor for that property that changes it's return value for IsReadOnly. 2.34 How can I have the control designer create the custom editor by calling the constructor with the additional parameters rather than the default constructor?

You can do this by creating the editor yourself rather than allowing TypeDescriptor to do it: 1) Shadow the property you care about in your designer... protected override void PreFilterProperties(IDictionaryProperties props) { PropertyDescriptor basePD = props["MyProperty"]; props["MyProperty"] = new EditorPropertyDescriptor(basePD); } 2) Create a property descriptor that "wraps" the original descriptor private class EditorPropertyDescriptor : PropertyDescriptor { private PropertyDescriptor basePD; public EditorPropertyDescriptor(PropertyDescriptor base) { this.basePD = base; } // now, for each property and method, just delegate to the base... public override TypeConverter Converter { get { return basePD.Converter; } } public override bool CanResetValue(object comp) { return basePD.CanResetValue(comp); } // and finally, implement GetEditor to create your special one... 3) create your editor by hand when it's asked for public override object GetEditor(Type editorBaseType) { if (editorBaseType == typeof(UITypeEditor)) { return new MyEditor(Param1, param2, param3); } return basePD.GetEditor(editorBaseType); } } (from sburke_online@microsoft..nospam..com on microsoft.public.dotnet.framework.windowsforms) 2.35 How can I make a Panel or Label semi-transparent on a Windows Form? Type conversion is usually possible in one of the following ways:

1) Using explicit methods in source type like: public class SizeF { ... public Size ToSize(); } There is usually a corresponding explicit or implicit operator that does the same conversion. The operators are however not available in VB.Net. 2) Using FromXXX static methods exposed in the destination type: class Image { ... public static Image FromStream(Stream); } 3) Using implicit or explicit operators defined in the source or destination type. This will allow you to perform implicit or explicit casts from the source type to the destination type. SizeF sizeF = size; // Possible because type Size has an implicit type conversion operator that converts a Size to SizeF PointF pointF = (PointF)sizeF; // Possible because type SizeF has an explicit type conversion operator that converts a SizeF to PointF. There is usually a corresponding ToXXX method that does the same conversion. You can use either this or the ToXXX methods in C#. 4) Using TypeConverters. Some types come with TypeConverters that allow you to convert to or convert from another type. This conversion is usually not documented and can be discovered only by writing some test code. For example, the System.Drawing.Icon type's TypeConverter converts the Icon into a byte[]. You can use this functionality in your code as follows: TypeConverter tc = TypeDescriptor.GetConverter(typeof(System.Drawing.Icon)); byte[] blob = tc.ConvertTo(myIcon, typeof(byte[])); It's usually time consuming to figure out whether a TypeConverter's ConvertTo or ConvertFrom method can perform the necessary conversion. The attached TypeConverterLookup tool lets you figure that out easily on any type declared in any assembly available in the GAC or available in the same directory as the tool's exe. If you have types in custom assemblies, then just copy over that assembly to the same directory as the tool, you can then specify the type in the tool. 2.36 How do I make the arrow keys be accepted by a control (such as a button) and not handled automatically by the framework's focus

management? By default, the arrow keys are not handled by a control's key processing code, but instead are filtered out for focus management. Hence, the control's KeyDown, KeyUp and KeyPressed events are not hit when you press an arrow. If you want your control to handle these keyboard events, you tell the framework by overriding your control's IsInputKey method. protected override bool IsInputKey(Keys key) { switch(key) { case Keys.Up: case Keys.Down: case Keys.Right: case Keys.Left: return true; } return base.IsInputKey(key); }

2.37 How can I add a control to a Window Form at runtime? To add a control at runtime, you do three steps:
y y y

1. Create the control 2. Set control properties 3. Add the control to the Form's Controls collection

In general, if you need help on exactly what code you need to add, just look at the code generated by the designer when you add the control at design time. You can generally use the same code at runtime. Here are code snippets that create a textBox at runtime. [C#] //step 1 TextBox tb = new TextBox(); //step2 tb.Location = new Point( 10, 10); tb.Size = new Size(100, 20); tb.Text = "I was created at runtime"; //step3 this.Controls.Add(tb); [VB.NET] 'step 1 Dim tb as TextBox = New TextBox()

'step2 tb.Location = New Point( 10, 10) tb.Size = New Size(100, 20) tb.Text = "I was created at runtime" 'step3 Me.Controls.Add(tb)

3. Windows Forms Books 3.1 Windows Forms Programming in C#? 3.2 Programming Microsoft Windows with C#? 3.3 C# Design Patterns?

WinForms FAQ Home

3.1 Windows Forms Programming in C#? Windows Forms Programming in C# by Chris Sells ISBN: 0321116208 An excellent book for learning Windows Forms using C# for both Beginners and experienced Programmers. The presentation is nice and crisp the chapter size is usually around 50 pages, presenting all the necessary details and at the same time maintaining the reader's interest in the topic being presented. 3.2 Programming Microsoft Windows with C#? Programming Windows(r) with C# (Core Reference) by Charles Petzold ISBN: 0735613702 This is an excellent book for both Windows Forms and GDI+. As the title suggests it is oriented towards C# programmers. VB programmers should have no trouble following along. Petzold writes lucid prose. Update: There is a VB.NET version of this book available now. Programming Microsoft Windows with Microsoft Visual Basic .NET (Core Reference). ISBN: 0735617996. 3.3 C# Design Patterns? C# Design patterns by James W. Cooper. ISBN: 0201844532 This is a good book to learn about patterns through a C#/Windows Forms lens. Several of the samples are UI related and use Windows Forms.

4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 4.10 4.11 4.12 4.13 4.14 4.15 4.16

4.17 4.18 4.19 4.20 4.21 4.22 4.23

Does MySql work with ADO.NET? Where can I find a discussion of databinding and windows forms? How do I test for a null value in DataView.RowFilter? I don't have SQL Server. Is there any way to run the samples that need SQL server? How can I import a CSV file into a DataTable? How can I prevent a ComboBox and DataGrid bound to the same DataTable from sharing the same current position? I get a 'This would cause two bindings in the collection to bind to the same property' error message. What might cause this? How do I bind a TextBox to a nested relation? How do I call a SQL stored procedure? I programatically change a bound TextBox value, but the value does not get pushed back into the bound datasource. How can I make sure the DataSource is updated? Why does the CheckedListBox lose checked state when placed in a tab page that gets hidden and shown? How do I bind a mdb file to a datagrid? How do I bind a listbox or a combobox to a MDB file? In my databound ComboBox, how do I set the SelectedItem property? Are server side cursors supported in .NET? I am populating a ListBox by binding it to an ArrayList. The initial display is OK, but later changes to my ArrayList are not shown in my ListBox. How can I get the changes to show properly? My SQL server runs on a non-default port. How can I specify this port in my connection string? What is a DataSet? How do I add updating support to a dataset? How can I programmatically move through a dataset that has bound controls? When I try to update a dataset I get an error that the system is unable to find "Table"? How can I bind an ArrayList to a DataGrid? When I try to bind two comboboxes to the same datatable, both boxes show the same values, changing one changes the other. How do I make them have distinct values?

4.1 Does MySql work with ADO.NET? MYSQL can be used with ADO.NET. There is more information available here: http://www.mysql.com/articles/dotnet/ 4.2 Where can I find a discussion of databinding and windows forms?

Take a look at the Microsoft KB article, INFO: Roadmap for Windows Forms Data Binding (Q313482). This article gets you started. It also lists other Microsoft KB articles that have to do with using the Windows Forms DataGrid and databinding. 4.3 How do I test for a null value in DataView.RowFilter? You can use the IsNull operator. [C#] //the outer quotes are double quotes and the inner quotes are 2 single quotes //Fax is the column mapping name this.dataView1.RowFilter = "IsNull(Fax, '') = ''"; // for a numeric null in custNo (suggested by Bob Gibson) this.dataView1.RowFilter = "IsNull(custNo, 0) = 0"; [VB.NET] 'the outer quotes are double quotes and the inner quotes are 2 single quotes 'Fax is the column mapping name Me.dataView1.RowFilter = "IsNull(Fax, '') = ''" ' for a numeric null in custNo (suggested by Bob Gibson) Me.DataView1.RowFilter = "IsNull(custNo, 0) = 0"

4.4 I don't have SQL Server. Is there any way to run the samples that need SQL server? You can use MSDE, a SQL server compatible database engine that MS makes available for free. You can download and install MSDE from a whole variety of sources. Here is one source. MSDE also comes with Visual Studio.NET / .NET framework SDK. It is available under $(VS Install drive):\Program Files\Microsoft Visual Studio .NET\FrameworkSDK\Samples\Setup\msde. You can then run $(VS Install drive):\Program Files\Microsoft Visual Studio .NET\FrameworkSDK\Samples\Setup\configsamples.exe to configure the northwind database that the samples use. 4.5 How can I import a CSV file into a DataTable? Here is a solution suggested by Elan Zhou (MS) on the microsoft.public.dotnet.languages.vb newsgroup. You can also use the following code: 1. Put a DataGrid on the form. 2. Try the following sample code: (Suppose the csv is c:\test.csv.) Imports System.Data Imports System.Data.OleDb Public Class Form1 Inherits System.Windows.Forms.Form

Dim objDataset1 As DataSet() Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Dim sConnectionString As String = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\;Extended Properties=Text;" Dim objConn As New OleDbConnection(sConnectionString) objConn.Open() Dim objCmdSelect As New OleDbCommand("SELECT * FROM test.csv", objConn) Dim objAdapter1 As New OleDbDataAdapter() objAdapter1.SelectCommand = objCmdSelect Dim objDataset1 As New DataSet() objAdapter1.Fill(objDataset1, "Test") DataGrid1.DataSource = objDataset1.Tables(0).DefaultView objConn.Close() End Sub End Class

4.6 How can I prevent a ComboBox and DataGrid bound to the same DataTable from sharing the same current position? If you have two controls bound to the same datasource, and you do not want them to share the same position, then you must make sure that the BindingContext member of one control differs from the BindingContext member of the other control. If they have the same BindingContext, they will share the same position in the datasource. If you add a ComboBox and a DataGrid to a form, the default behavior is for the BindingContext member of each of the two controls to be set to the Form's BindingContext. Thus, the default behavior is for the DataGrid and ComboBox to share the same BindingContext, and hence the selection in the ComboBox is synchronized with the current row of the DataGrid. If you do not want this behavior, you should create a new BindingContext member for at least one of the controls. [C#] private void Form1_Load(object sender, System.EventArgs e) { this.myDataTable = GetATable(); //get a datatable somehow... this.dataGrid1.DataSource = myDataTable; //set a new binding context for the combobox this.comboBox1.BindingContext = new BindingContext(); this.comboBox1.DataSource = myDataTable; this.comboBox1.DisplayMember = "Col1"; this.comboBox1.ValueMember = "Col1"; } [VB.NET] Private Sub Form1_Load(ByVal sender as Object, ByVal e as System.EventArgs) Me.myDataTable = GetATable() 'get a datatable somehow...

Me.dataGrid1.DataSource = myDataTable 'set a new binding context for the combobox Me.comboBox1.BindingContext = New BindingContext() Me.comboBox1.DataSource = myDataTable Me.comboBox1.DisplayMember = "Col1" Me.comboBox1.ValueMember = "Col1" End Sub

4.7 I get a 'This would cause two bindings in the collection to bind to the same property' error message. What might cause this? As the message suggests, the code is calling Control.DataBindings.Add twice with what amounts to the same parameters. One way this might happen is if you call the same code more than once in your program to reload your datasource for some reason, and in this code, you have lines such as: Me.TextBox1.DataBindings.Add("Text", myDataTable, "Col1Name") Me.TextBox2.DataBindings.Add("Text", myDataTable, "Col2Name") Me.TextBox3.DataBindings.Add("Text", myDataTable, "Col3Name") On the second call, this would attempt to add a duplicate binding to the DataBindings collection. One solution is to Clear the DataBindings collection before you add your new binding. Me.TextBox1.DataBindings.Clear(); Me.TextBox1.DataBindings.Add("Text", myDataTable, "Col1Name") Me.TextBox2.DataBindings.Clear(); Me.TextBox2.DataBindings.Add("Text", myDataTable, "Col2Name") Me.TextBox3.DataBindings.Clear(); Me.TextBox3.DataBindings.Add("Text", myDataTable, "Col3Name")

4.8 How do I bind a TextBox to a nested relation? Say you have a Parent table related to a Child table related to a GrandChildTable, and you want to bind a TextBox to a column in the GrandChild table. To do this you have to have nested relations defined, and you use this nesting to create the DataMember parameter for the the DataBinding that you add to the TextBox. Below are some code snippets. They show the DataMember as "ParentToChild.ChildToGrandChild.Name" which creates a path from the parent table down to the field in the nested relation by stringing the relation names together separated with a period. You can also download both C# and VB projects. Dim dSet As New DataSet() 'get the tables Dim parentTable As DataTable = GetParentTable() Dim childTable As DataTable = GetChildTable() Dim grandChildTable As DataTable = GetGrandChildTable()

dSet.Tables.AddRange(New DataTable() {parentTable, childTable, grandChildTable}) 'setup the relations Dim parentColumn As DataColumn = parentTable.Columns("parentID") Dim childColumn As DataColumn = childTable.Columns("ParentID") dSet.Relations.Add("ParentToChild", parentColumn, childColumn) parentColumn = childTable.Columns("childID") childColumn = grandChildTable.Columns("ChildID") dSet.Relations.Add("ChildToGrandChild", parentColumn, childColumn) Me.TextBox1.DataBindings.Add("Text", parentTable, "ParentToChild.ChildToGrandChild.Name")

4.9 How do I call a SQL stored procedure? You can call stored procedures in basically the same manner as executing other SQL commands. When creating the SqlCommand, set the query string to be the name of the stored procedure, and then set the CommandType to be CommandType.StoredProcedure. if(sqlConn.State == ConnectionState.Closed)sqlConn.Open(); SqlCommand cmd = new SqlCommand("sp_my_stored_procedure",sqlConn); cmd.CommandTimeout = 180; cmd.CommandType = CommandType.StoredProcedure; After you setup the command type, you need to pass in any parameters required for the stored procedure. Here is an example of one input and one output parameter. SqlParameter parm; parm = cmd.Parameters.Add(new SqlParameter("@oid", SqlDbType.VarChar,50)); parm.Direction = ParameterDirection.Input; cmd.Parameters["@oid"].Value = OrderID; parm = cmd.Parameters.Add(new SqlParameter("@custName", SqlDbType.VarChar,50)); parm.Direction = ParameterDirection.Output; If the stored procedure is returning a selection query at the end, you can fill your DataSet and retrieve any tables. SqlDataAdapter tempDA = new SqlDataAdapter(); tempDA.TableMappings.Add("your mapping","your mapping"); tempDA.SelectCommand = cmd; DataSet dataSet1 = new DataSet(); tempDA.Fill(dataSet1); DataTable resultTable = dataSet1.Tables[0]; Or, if no tables are being returned, you can execute the command as a non-query cmd.ExecuteNonQuery();

4.10 I programatically change a bound TextBox value, but the value

does not get pushed back into the bound datasource. How can I make sure the DataSource is updated? You can call the EndCurrentEdit method on the bindingmanager for the TextBox. [C#] this.textBox1.Text = "XXXX"; //set the value this.textBox1.DataBindings["Text"].BindingManagerBase.EndCurrentEdit(); //end the edit [VB.NET] Me.TextBox1.Text = "XXXX" <e.textbox1.databindings("text").bindingmanagerbase.endcurrentedit() </e.textbox1.databindings("text").bindingmanagerbase.endcurrentedit()

4.11 Why does the CheckedListBox lose checked state when placed in a tab page that gets hidden and shown? In Usenet posts, MS has acknowledged this bug. The problem is essentially that any time the visibility changes on a CheckedListBox, it loses its previous selections. Naturally this happens all the time in tab controls when changing tabs. This derived Control saves it's state while getting hidden and reloads it while getting shown: [C#] # region Workaround for CheckedListBox bug ////////////////////////////////////////////////////////////////////// // When CheckedList box becomes invisible (e.g. when the tab page // containing this control is overlapped with another tab page), // checked state of the items are getting lost. This workaround will // fix this problem ////////////////////////////////////////////////////////////////////// private bool[] isItemChecked; protected override void OnDataSourceChanged(EventArgs e) { base.OnDataSourceChanged(e); int cnt = this.Items.Count; isItemChecked = new Boolean[cnt]; for(int i = 0; i < cnt; i++) { isItemChecked[i] = GetItemChecked(i); } } protected override void OnItemCheck(ItemCheckEventArgs e) { base.OnItemCheck(e); isItemChecked[e.Index] = (e.NewValue == CheckState.Checked); } protected override void OnVisibleChanged(EventArgs e) {

base.OnVisibleChanged(e); if (this.Visible == true) { // Reset the checked states when it becomes visible. for(int i =0; i < this.Items.Count; i++) { SetItemChecked(i, isItemChecked[i]); } } } #endregion [VB.Net] Public Class MyCheckedListBox Inherits CheckedListBox '//////////////////////////////////////////////////////////////////// ' When CheckedList box becomes invisible (e.g. when the tab page ' containing this control is overlapped with another tab page), ' checked state of the items are getting lost. This workaround will ' fix this problem '//////////////////////////////////////////////////////////////////// Private isItemChecked() As Boolean Protected Overrides Sub OnDataSourceChanged(e As EventArgs) MyBase.OnDataSourceChanged(e) Dim cnt As Integer = Me.Items.Count isItemChecked = New [Boolean](cnt) {} Dim i As Integer For i = 0 To cnt - 1 isItemChecked(i) = GetItemChecked(i) Next i End Sub 'OnDataSourceChanged Protected Overrides Sub OnItemCheck(e As ItemCheckEventArgs) MyBase.OnItemCheck(e) isItemChecked(e.Index) = e.NewValue = CheckState.Checked End Sub 'OnItemCheck Protected Overrides Sub OnVisibleChanged(e As EventArgs) MyBase.OnVisibleChanged(e) If Me.Visible = True Then ' Reset the checked states when it becomes visible. Dim i As Integer For i = 0 To (Me.Items.Count) - 1 SetItemChecked(i, isItemChecked(i)) Next i End If End Sub 'OnVisibleChanged End Class 'MyCheckedListBox

(Contributed by Eric and Reddy Duggempudi in Syncfusion Windows Forms FAQ forums). 4.12 How do I bind a mdb file to a datagrid? You can use the classes in the System.Data.OleDb namespace to read a MDB file into a datagrid. You instantiate a OleDbConnection object using a connection string to your MDB file. You then instantiate a OleDbDataAdapter that uses this connection object and a SQL query. Next you create a DataSet object, use the OleDbDataAdapter to fill this dataset, and finally attached this dataset to your datagrid. Here is the code that does this. private void Form1_Load(object sender, System.EventArgs e) { // Set the connection and sql strings // assumes your mdb file is in your root string connString = @"Provider=Microsoft.JET.OLEDB.4.0;data source=C:\northwind.mdb"; string sqlString = "SELECT * FROM customers"; // Connection object OleDbConnection connection = new OleDbConnection(connString); // Create data adapter object OleDbDataAdapter dataAdapter = new OleDbDataAdapter(sqlString, connection); // Create a dataset object and fill with data using data adapter's Fill method DataSet dataSet = new DataSet(); dataAdapter.Fill(dataSet, "customers"); // Attach dataset's DefaultView to the datagrid control dataGrid1.DataSource = dataSet.Tables["customers"].DefaultView; }

4.13 How do I bind a listbox or a combobox to a MDB file? You can use the classes in the System.Data.OleDb namespace to read a MDB file into a ListBox and a ComboBox. You instantiate a OleDbConnection object using a connection string to your MDB file. You then instantiate a OleDbDataAdapter that uses this connection object and a SQL query. Next you create a DataSet object, use the OleDbDataAdapter to fill this dataset, and finally attached this dataset to your control. Here is the code that does this. private void Form1_Load(object sender, System.EventArgs e) { // Set the connection and sql strings // assumes your mdb file is in your root string connString = @"Provider=Microsoft.JET.OLEDB.4.0;data source=C:\northwind.mdb"; string sqlString = "SELECT * FROM customers"; // Connection object OleDbConnection connection = new OleDbConnection(connString);

// Create data adapter object OleDbDataAdapter dataAdapter = new OleDbDataAdapter(sqlString, connection); // Create a dataset object and fill with data using data adapter's Fill method DataSet dataSet = new DataSet(); dataAdapter.Fill(dataSet, "customers"); // Attach dataset's DefaultView to the combobox listBox1.DataSource = dataSet.Tables["customers"].DefaultView; listBox1.DisplayMember = "CustomerID"; // Attach dataset's DefaultView to the combobox comboBox1.DataSource = dataSet.Tables["customers"].DefaultView; comboBox1.DisplayMember = "CustomerID"; } Notice that this code uses the same dataset object for both the listbox and the combobox. When done in this manner, the two controls are linked so that the selection in one control will be the selection in the other control. If you edit an entry in the combobox, the same entry is the listbox is changed. To avoid this linkage, just have a second dataset object for your second control. 4.14 In my databound ComboBox, how do I set the SelectedItem property? Use the FindStringExact method. For example, if you have set the ComboBox's ValueMember property to "OrderID", then you could call comboBox1.SelectedIndex = comboBox1.FindStringExact(stringOrderID); where stringOrderID is the "orderID" of the row to be selected. 4.15 Are server side cursors supported in .NET? You can covert a dll into it's IL using the ildasm.exe utility. This is usually installed in: C:\Program Files\Microsoft Visual Studio .NET\FrameworkSDK\Bin If you want to run this utility via command line it would help if you add the above path to your Environment Path variable. Here is a sample command line that will disassemble a dll: ildasm MyFile.exe /output:MyFile.il You can reassemble the above il after making some minor changes via the ilasm utility. This is usually done to make some minor changes to an assembly like modifying the version no. of another assembly to which it links to. 4.16 I am populating a ListBox by binding it to an ArrayList. The initial display is OK, but later changes to my ArrayList are not

shown in my ListBox. How can I get the changes to show properly? In an ArrayList, the 'plumbing' is not available to support two-way binding as with a dataset. So, you have to handle the synchronization yourself. One way to do this is to set the listBox1.DataSource to null and then reset it to your ArrayList. Another way is to use the CurrencyManager as shown in the code below. private System.Windows.Forms.ListBox listBox1; private System.Windows.Forms.Button button1; private ArrayList myArrayList; public Form1() { // // Required for Windows Form Designer support // InitializeComponent(); myArrayList = new ArrayList(); myArrayList.Add("orange"); myArrayList.Add("green"); myArrayList.Add("blue"); myArrayList.Add("red"); listBox1.DataSource = myArrayList; } ...... //change the arraylist private void button1_Click(object sender, System.EventArgs e) { myArrayList[1] = "pink"; myArrayList.Add("lavendar"); //use currency manger to sync up the listbox BindingManagerBase bm = this.listBox1.BindingContext[myArrayList]; CurrencyManager cm = (CurrencyManager) bm; if (cm != null) cm.Refresh(); //Or, you can just reset the datasource //listBox1.DataSource = null; //listBox1.DataSource = myArrayList; }

4.17 My SQL server runs on a non-default port. How can I specify this port in my connection string? The data source component of your connection string should contain the port right after the IP address (or name). It should be separated from the IP by a comma.

data source=192.168.123.1, port number; Take a look at this web site for great information on several connection strings. http://www.connectionstrings.com/ 4.18 What is a DataSet? Think of a DataSet object as a local in memory copy of database tables. With the client server model, client applications held onto a connection and updated and added records at will. With ADO.NET the dataset presents a disconnected model. Data as well as data changes are contained in the dataset with no physical connection to the datasource. Changes can be reconciled against any datasource at any time. A Dataset is not limited to database tables. It can work with XML or for that matter any other data. 4.19 How do I add updating support to a dataset? To be able to write changes back to the datasource, the data adapter object that populates your dataset should have commands set for updating, deleting etc. Fortunately, there is a class called SqlCommandBuilder that generates these commands from our Select command. All we have to do is instantiate this class passing it in the data adapter that we use. Enclosed is a complete sample: simpledata4.zip // Command builder will generate the command required to update the // datasource from your select statement SqlCommandBuilder commandBuilder = new SqlCommandBuilder(this.dataAdapter); After this is done whenever you wish to write changes back to the data source simply call Update on the data adapter as shown below. if(this.dataSet != null && this.dataSet.HasChanges()) this.dataAdapter.Update(this.dataSet, "Customers");

4.20 How can I programmatically move through a dataset that has bound controls? You have to access a property called the Binding Context and then retrieve the BindingContext associated with the dataset and data member that you used for binding. After you have access to this object you just set the position property. You can move backward and forward through the dataset. Download a working sample that shows this: simpledata5.zip form.BindingContext[this.dataSet, "Customers"].Position -= 1;

Remember that when you scroll through the dataset all associated controls will scroll since they all depend on the same context. This is useful if you want to have several controls that display sections of a row operate in tandem. 4.21 When I try to update a dataset I get an error that the system is unable to find "Table"? Are you calling Update on the dataset like this without specifying the name of the table that you are updating. The problem is that when table names are not given the system assumes a table name of 'Table'. This of course causes the update to fail. this.dataAdapter.Update(this.dataSet); If so just change this line to this.dataAdapter.Update(this.dataSet, "Customers"); // replace 'Customers' with the table that you have

4.22 How can I bind an ArrayList to a DataGrid? Here is a technique for binding an arraylist of objects where the objects contain public property that can appear as columns in the datagrid. In this example, the object contains 2 public doubles, one named value and the other named sqrt. To bind this arraylist to a datagrid, add a custom tablestyle that has a MappingName of "ArrayList", and then use the property names as the MappingName for each column. Below are some code snippets. You can download a working project that also has code to delete rows and add new rows to the bound arraylist. private void Form1_Load(object sender, System.EventArgs e) { CreateArrayList(); BindArrayListToGrid(); } private void BindArrayListToGrid() { dataGrid1.DataSource = arrayList1; //create a custom tablestyle and add two columnstyles DataGridTableStyle ts = new DataGridTableStyle(); ts.MappingName = "ArrayList"; int colwidth = (dataGrid1.ClientSize.Width - ts.RowHeaderWidth SystemInformation.VerticalScrollBarWidth - 5) / 2; //create a column for the value property DataGridTextBoxColumn cs = new DataGridTextBoxColumn(); cs.MappingName = "value"; //public property name cs.HeaderText = "Random Number"; cs.Format = "f4"; cs.Width = colwidth; ts.GridColumnStyles.Add(cs);

//create a column for the sqrt property cs = new DataGridTextBoxColumn(); cs.MappingName = "sqrt"; //public property name cs.HeaderText = "Square Root"; cs.Format = "f4"; cs.Width = colwidth; ts.GridColumnStyles.Add(cs); dataGrid1.TableStyles.Clear(); dataGrid1.TableStyles.Add(ts); } private void CreateArrayList() { arrayList1 = new ArrayList(); //add some items Random r = new Random(); for (int i = 0; i < 20; ++i) arrayList1.Add(new RandomNumber(r.NextDouble())); } //create a struct or class that defines what you want in each row //the different columns in the row must be public properties public struct RandomNumber { private double number; public RandomNumber(double d) { number = d; } public double value { get{ return number; } set{ number = value;} } public double sqrt { get {return Math.Sqrt(this.value);} } }

4.23 When I try to bind two comboboxes to the same datatable, both boxes show the same values, changing one changes the other. How do I make them have distinct values? Make sure the two comboboxes use different BindngContext objects. BindingContext bc = new BindingContext(); this.comboBox1.BindingContext = bc;

comboBox1.DataSource = _dataSet.Tables["orders"]; comboBox1.ValueMember = "CustomerID"; comboBox1.DisplayMember = "CustomerID"; bc = new BindingContext(); this.comboBox2.BindingContext = bc; comboBox2.DataSource = _dataSet.Tables["orders"]; comboBox2.ValueMember = "CustomerID"; comboBox2.DisplayMember = "CustomerID";

5. Windows Forms Datagrid WinForms FAQ Home 5.1 How can I programatically set the rowheight of a row in my DataGrid? 5.2 How can I autosize the rowheights in my datagrid? 5.3 How do I prevent sorting a single column in my DataGrid? 5.4 How do I programmatically select a row in DataGrid? 5.5 How can I translate a point to a cell in the datagrid? 5.6 I lost the settings in my DataGrid set during design-time? 5.7 How do I prevent a click into the grid highlighting a cell? 5.8 How do I prevent the datagrid from displaying its append row (the row at the end with an asterisk)? 5.9 How can I catch a double-click into a column header cell? 5.10 How can I select the entire row when the user clicks on a cell in the row? 5.11 How can I get text from a column header in a MouseUp event? 5.12 How do I hide a column? 5.13 How do I color a individual cell depending upon its value or some external method? 5.14 How can I put a checkbox in a column of my DataGrid? 5.15 How can I restrict the keystrokes that will be accepted in a column of my datagrid? 5.16 How do I make a field auto increment as new rows are added to my datagrid? 5.17 How can I prevent a particular cell from being editable? 5.18 How do I move columns in a datagrid? 5.19 How can I do cell by cell validation in a datagrid? 5.20 How do I programmatically determine the selected rows in a datagrid? 5.21 How can I bind the datagrid to a datasource without using any wizards? 5.22 How can I bind two datagrids in a Master-Detail relationship? 5.23 How do I get the row or column that has been clicked on? 5.24 How do I add an unbound column to my bound datagrid? 5.25 How do I get the row and column of the current cell in my datagrid? 5.26 How can I prevent the Enter key from moving to the next cell when the user is actively editing the cell and presses Enter? 5.27 How do I set the width of a column in my DataGrid?

5.28 How can I implement OLE Drag & Drop between a DataGrid and another OLE DnD object that supports the Text format? 5.29 How can I tell if the current row has changed and whether I am on the AddNew row or not? 5.30 How do I hide the gridlines or set them to a particular color? 5.31 How can I get the selected text in an active gridcell? 5.32 How do I determine whether a checkbox in my datagrid is checked or not? 5.33 How can I move rows by dragging the row header cell? 5.34 How can I control the cursor over my DataGrid? 5.35 After scrolling with the mouse wheel on a selected row in a DataGrid I cannot get it back into view. Is there a work around? 5.36 How can I make the DataGrid column be blank and not display (null) as the default value? 5.37 How can I add a DateTimePicker column style to the DataGrid? 5.38 How can I put a combobox in a column of a datagrid? 5.39 I want to do custom handling of special keys such as the Tab key or F2 in the TextBox of a column in the DataGrid. How do I subclass this TextBox to get at it virtual members? 5.40 How can I have a column of icons in my datagrid? 5.41 How can I make my DataGrid support a single select mode, and not the default multiselect mode? 5.42 How can I get celltips or tooltips to vary from cell to cell in my DataGrid? 5.43 How can I get notification of the changing of a value in a column of comboboxes within my datagrid? 5.44 How can I make the datagrid have no currentcell? 5.45 How can I make my grid never have an active edit cell and always select whole rows (as in a browser-type grid)? 5.46 I have hidden (column width = 0) columns on the right side of my datagrid, but tabbing does not work properly. How can I get tabbing to work? 5.47 How can I get the number of rows in my DataGrid? 5.48 How do I format a date column in a datagrid? 5.49 How can I change the width of the row headers or hide them? 5.50 How do I catch a doubleclick in my datagrid? 5.51 How can I make my last column wide enough to exactly occupy all the client area of the datagrid? 5.52 How can I prevent my user from sizing columns in my datagrid? 5.53 How can I catch the bool values changing in a DataGridBoolColumn? 5.54 When I click on a row header, the row is selected and no cell is active. How can I do this programmatically? 5.55 How can I force the vertical scrollbar in my DataGrid to always be visible? 5.56 How can I autosize a column in my datagrid?

5.57 How can I get rid of the error icon that appears when there is an editing error? 5.58 How do I find the top-left visible cell in a datagrid? 5.59 I want to display negative values with a CR suffix, instead of a minus sign. How can I perform custom formatting on the cells in my datagrid? 5.60 I want to do sort of a database join of two tables. How can I use data from two DataTables in a single DataGrid? 5.61 How do I display a column of buttons such as pushbuttons or combobox buttons? 5.62 How can I put up a confirmation question when the user tries to delete a row in the datagrid by clicking on the row header and pressing the Delete key? 5.63 How can I enable column selections in my datagrid? 5.64 How do I programmatically scroll the datagrid to a particular row? 5.65 How can I place text in the rowheader column of my datagrid? 5.66 How do I set default values for new rows in my datagrid? 5.67 How do I iterate through all the rows and columns in my datagrid? 5.68 How can I specially color only the currentcell of my readonly datagrid? 5.69 How can I make the Enter Key behave like the Tab Key and move to the next cell? 5.70 How do I use the DataColumn.Expression property to add a computed/combined column to my datagrid? 5.71 How can I change the font used in a grid cell on a cell by cell or row by row basis? 5.72 How can I use a mouse to select cell ranges in my datagrid? 5.73 How do I determine the DataGridTableStyle MappingName that should used for a DataGrid to make sure the grid uses my tablestyle? 5.74 I have a derived DataGridColumnStyle. From within my Paint override, how can I get at other values in the DataGrid? 5.75 How do I retrieve the current row from a DataTable bound to a DataGrid after the grid has been sorted? 5.76 How can I catch when the user clicks off the grid, say to close the form? 5.77 How can I get a CheckBox column in a DataGrid to react to the first click? 5.78 How can I use events to restrict key input to grid cells? 5.79 How can I format columns in my DataGrid without explicitly adding DataGridColumnStyles? 5.80 How can I auto-adjust keyboard input? For example, make typing 12312002 be taken as a valid date, 12/31/2002. 5.81 Can I display the rows in my datagrid in a free-form layout using textboxes on a panel? 5.82 How can I tell whether a scrollbar is visible in my DataGrid is visible? 5.83 How do I autosize the columns in my DataGrid so they always fill the the grid's client area? 5.84 How can I prevent all the cells in my DataGrid from being edited without deriving GridColumnStyle? 5.85 How can I prevent the plus-minus icon that appears next to the row header when I have a

5.86 5.87 5.88 5.89 5.90 5.91

datagrid displayed bound to a datasource that has a relation defined? How can I display master-details-details in three separate grids? In my datagrid, if I press tab to enter a column using a derived columnstyle, the column does not receive focus. Why? How can I detect when a cell starts being edited, not when it becomes current? How can I have a column of bitmaps in a DataGrid? How can I add my custom columnstyles to the designer so I can use them in my DataGrid at design time? How can I programatically add and remove columns in my DataGrid without modifying the DataTable datasource?

5.1 How can I programatically set the rowheight of a row in my DataGrid? You can do this by starting a new thread and executing Application.Run for the status dialog form when the background thread starts running. To communicate changes in percentage use BeginInvoke to executes a specific delegate asynchronously on the thread that the form was created on. Download the ProgressThread progressthread.zip sample for a complete implementation of a BackgroundThreadStatusDialog class that shows a status dialog in a separate thread. In order to use BackgroundThreadStatusDialog from your code you have to update the Progress inside your loop and check the IsCanceled state to detect if the user pressed the Cancel button. private void button1_Click(object sender, System.EventArgs e) { BackgroundThreadStatusDialog statusDialog = new BackgroundThreadStatusDialog(); try { for (int n = 0; n < 1000; n++) { statusDialog.Percent = n/10; int ticks = System.Environment.TickCount; while (System.Environment.TickCount - ticks < 10) ; if (statusDialog.IsCanceled) return; } statusDialog.Close(); MessageBox.Show(statusDialog.IsCanceled ? "Canceled" : "Success"); } finally {

statusDialog.Close(); } }

5.2 How can I autosize the rowheights in my datagrid? Here is a solution suggested by Matthew Benedict. It uses reflection to access the protected DataGridRows collection so he can set the row height. public void AutoSizeGrid() { // DataGrid should be bound to a DataTable for this part to // work. int numRows = ((DataTable)gridTasks.DataSource).Rows.Count; Graphics g = Graphics.FromHwnd(gridTasks.Handle); StringFormat sf = new StringFormat(StringFormat.GenericTypographic); SizeF size; // Since DataGridRows[] is not exposed directly by the DataGrid // we use reflection to hack internally to it.. There is actually // a method get_DataGridRows that returns the collection of rows // that is what we are doing here, and casting it to a System.Array MethodInfo mi = gridTasks.GetType().GetMethod("get_DataGridRows", BindingFlags.FlattenHierarchy | BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); System.Array dgra = (System.Array)mi.Invoke(gridTasks,null); // Convert this to an ArrayList, little bit easier to deal with // that way, plus we can strip out the newrow row. ArrayList DataGridRows = new ArrayList(); foreach (object dgrr in dgra) { if (dgrr.ToString().EndsWith("DataGridRelationshipRow")==true) DataGridRows.Add(dgrr); } // Now loop through all the rows in the grid for (int i = 0; i < numRows; ++i) { // Here we are telling it that the column width is set to // 400.. so size will contain the Height it needs to be. size = g.MeasureString(gridTasks[i,1].ToString(),gridTasks.Font,400,sf); int h = Convert.ToInt32(size.Height); // Little extra cellpadding space h = h + 8; // Now we pick that row out of the DataGridRows[] Array // that we have and set it's Height property to what we // think it should be. PropertyInfo pi = DataGridRows[i].GetType().GetProperty("Height");

pi.SetValue(DataGridRows[i],h,null); // I have read here that after you set the Height in this manner that you should // Call the DataGrid Invalidate() method, but I haven't seen any prob with not calling it.. } g.Dispose(); }

5.3 How do I prevent sorting a single column in my DataGrid? You can implement the IMessageFilter interface in your main form. This amounts to adding an override for PreFilterMessage, and looking for the particular message you need to catch. Here are code snippets that catch an escape key on a keydown. You can download a sample project(C#, VB). In the sample, there are two forms, with several controls. You'll notice that no matter what form or control has input focus, the escape key is caught in the PreFilterMessage override. [C#] public class MyMainForm : System.Windows.Forms.Form, IMessageFilter { const int WM_KEYDOWN = 0x100; const int WM_KEYUP = 0x101; public bool PreFilterMessage(ref Message m) { Keys keyCode = (Keys)(int)m.WParam & Keys.KeyCode; if(m.Msg == WM_KEYDOWN && keyCode == Keys.Escape) { Console.WriteLine("Ignoring Escape..."); return true; } return false; } .... .... .... private void MyMainForm_Load(object sender, System.EventArgs e) { Application.AddMessageFilter(this); } } [VB.NET] Public Class MyMainForm Inherits System.Windows.Forms.Form Implements IMessageFilter Private WM_KEYDOWN As Integer = &H100 Private WM_KEYUP As Integer = &H101 Public Function PreFilterMessage(ByRef m As Message) As Boolean Dim keyCode As Keys = CType(CInt(m.WParam), Keys) And Keys.KeyCode

If m.Msg = WM_KEYDOWN And keyCode = Keys.Escape Then Console.WriteLine("Ignoring Escape...") Return True End If Return False End Function 'PreFilterMessage .... .... .... Private Sub MyMainForm_Load(sender As Object, e As System.EventArgs) Application.AddMessageFilter(Me) End Sub 'MyMainForm_Load End Class 'MyMainForm

5.4 How do I programmatically select a row in DataGrid? We have a small sample that shows how to do this. Download PropTab.zip. After you download and unzip the sample, build the project. Both the control assembly and a small driver assembly get built. After that add the control to your toolbox using 'Customise toolbox...'. Then drag and drop an instance of the added control onto the form in the driver winforms sub-project. When you select this control the properties window should have a tab with a bitmap 'T' as shown below.

These are the steps involved. Before following these steps please download and take a look at the sample. That will greatly help when following these steps. Control related steps Assume that you have a control that has two sets of properties, one set that you wish to have displayed in the main property tab and another set that you wish to have displayed in the second

tab.
y y

Mark those properties that you wish to display in the first tab with the BrowsableAttribute to true. Mark those properties that you wish to display in the second tab with the BrowsableAttribute set to false. Create another attribute. Name it anything you want and give it a single boolean property. Initialize this property to true.

Other steps
y

Derive a class from System.Windows.Forms.PropertyGridInternal.PropertiesTab. You have to override a few methods. The most important of these is GetProperties. We override GetProperties as shown below to use our CustomAttribute as the filtering attribute instead of the BrowsableAttribute that the PropertyGrid uses by default.

public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object component, Attribute[] attrs) { return TypeDescriptor.GetProperties(component, new Attribute[] {new BrowsableAttribute(false), new CustomTabDisplayAttribute(true)}); } y Create a embedded resource bitmap with the same name as the derived tab's type. This bitmap had to be 16x16. A brief note of explanation on the sample. The sample shows a user control that displays its own tab for some properties. These properties are marked with the filtering attribute, CustomTabDisplayAttribute. The rest of the properties are just displayed in the normal property tab and need no special attention. To get a deeper understanding of how this works, please refer to these FAQs on TypeConverters and TypeDescriptors.
y y

TypeDescriptors TypeConverters

5.5 How can I translate a point to a cell in the datagrid? PE refers to portable executable. The file format used for executable programs and for files to be linked together to form executable programs. It includes both metadata information that describes types and members referenced in your program, and the MSIL that is the code base for the program. Source: .NET Framework SDK Documentation 5.6 I lost the settings in my DataGrid set during design-time?

This is possible if you assign a custom DataGridTableStyle to the DataGrid. The properties in the DataGridTableStyle will then replace certain properties in the DataGrid. Take a look at DataGrid class reference for a list of these properties. 5.7 How do I prevent a click into the grid highlighting a cell? Here is a technique for binding an arraylist of objects where the objects contain public property that can appear as columns in the datagrid. In this example, the object contains 2 public doubles, one named value and the other named sqrt. To bind this arraylist to a datagrid, add a custom tablestyle that has a MappingName of "ArrayList", and then use the property names as the MappingName for each column. Below are some code snippets. You can download a working project that also has code to delete rows and add new rows to the bound arraylist. private void Form1_Load(object sender, System.EventArgs e) { CreateArrayList(); BindArrayListToGrid(); } private void BindArrayListToGrid() { dataGrid1.DataSource = arrayList1; //create a custom tablestyle and add two columnstyles DataGridTableStyle ts = new DataGridTableStyle(); ts.MappingName = "ArrayList"; int colwidth = (dataGrid1.ClientSize.Width - ts.RowHeaderWidth SystemInformation.VerticalScrollBarWidth - 5) / 2; //create a column for the value property DataGridTextBoxColumn cs = new DataGridTextBoxColumn(); cs.MappingName = "value"; //public property name cs.HeaderText = "Random Number"; cs.Format = "f4"; cs.Width = colwidth; ts.GridColumnStyles.Add(cs); //create a column for the sqrt property cs = new DataGridTextBoxColumn(); cs.MappingName = "sqrt"; //public property name cs.HeaderText = "Square Root"; cs.Format = "f4"; cs.Width = colwidth; ts.GridColumnStyles.Add(cs); dataGrid1.TableStyles.Clear(); dataGrid1.TableStyles.Add(ts); } private void CreateArrayList() { arrayList1 = new ArrayList(); //add some items

Random r = new Random(); for (int i = 0; i < 20; ++i) arrayList1.Add(new RandomNumber(r.NextDouble())); } //create a struct or class that defines what you want in each row //the different columns in the row must be public properties public struct RandomNumber { private double number; public RandomNumber(double d) { number = d; } public double value { get{ return number; } set{ number = value;} } public double sqrt { get {return Math.Sqrt(this.value);} } }

5.8 How do I prevent the datagrid from displaying its append row (the row at the end with an asterisk)? The DataGrid class does not have a property that controls whether a new row can be added. But the DataView class does have such a property (along with some others such as AllowEdit and AllowDelete). Here is code that will turn off the append row by getting at the dataview associated with the datagrid. string connString = @"Provider=Microsoft.JET.OLEDB.4.0;data source=C:\northwind.mdb"; string sqlString = "SELECT * FROM customers"; // Connection object OleDbConnection connection = new OleDbConnection(connString); // Create data adapter object OleDbDataAdapter dataAdapter = new OleDbDataAdapter(sqlString, connection); // Create a dataset object and fill with data using data adapter's Fill method DataSet dataSet = new DataSet(); dataAdapter.Fill(dataSet, "customers"); // Attach dataset's DefaultView to the datagrid control dataGrid1.DataSource = dataSet.Tables["customers"]; //no adding of new rows thru dataview... CurrencyManager cm = (CurrencyManager)this.BindingContext[dataGrid1.DataSource,

dataGrid1.DataMember]; ((DataView)cm.List).AllowNew = false; If your datagrid contains links, then Matthew Miller suggest adding Navigate handler such as the one below to disallow the AddNew. private void DataGrid1_Navigate(object sender, System.Windows.Forms.NavigateEventArgs ne) { if(ne.Forward) { CurrencyManager cm = (CurrencyManager)BindingContext[DataGrid1.DataSource,DataGrid1.DataMember]; DataView dv = (DataView) cm.List; dv.AllowNew = false; } }

5.9 How can I catch a double-click into a column header cell? You can use the DataGrid's double-click event and its HitTest method to catch a double click on a header. private void dataGrid1_DoubleClick(object sender, System.EventArgs e) { System.Drawing.Point pt = dataGrid1.PointToClient(Cursor.Position); DataGrid.HitTestInfo hti = dataGrid1.HitTest(pt); if(hti.Type == DataGrid.HitTestType.ColumnHeader) { MessageBox.Show("double clicked clicked column header " + hti.Column.ToString()); } }

5.10 How can I select the entire row when the user clicks on a cell in the row? Call the DataGrid.Select method from within its mouseup event. [C#] private void dataGrid1_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e) { System.Drawing.Point pt = new Point(e.X, e.Y); DataGrid.HitTestInfo hti = dataGrid1.HitTest(pt); if(hti.Type == DataGrid.HitTestType.Cell) { dataGrid1.CurrentCell = new DataGridCell(hti.Row, hti.Column); dataGrid1.Select(hti.Row); }

} [VB/NET] Private Sub dataGrid1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles dataGrid1.MouseUp Dim pt = New Point(e.X, e.Y) Dim hti As DataGrid.HitTestInfo = dataGrid1.HitTest(pt) If hti.Type = DataGrid.HitTestType.Cell Then dataGrid1.CurrentCell = New DataGridCell(hti.Row, hti.Column) dataGrid1.Select(hti.Row) End If End Sub

5.11 How can I get text from a column header in a MouseUp event? private void dataGrid1_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e) { System.Drawing.Point pt = new Point(e.X, e.Y); DataGrid.HitTestInfo hti = dataGrid1.HitTest(pt); if(hti.Type == DataGrid.HitTestType.Cell) { MessageBox.Show(dataGrid1[hti.Row, hti.Column].ToString()); } else if(hti.Type == DataGrid.HitTestType.ColumnHeader) { MessageBox.Show(((DataView) DataGrid1.DataSource).Table.Columns[hti.Column].ToString()); } }

5.12 How do I hide a column? There are several ways to hide a column: 1) You can use your DataSet's ColumnMapping property to hide a column. // Creating connection and command sting string conStr = @"Provider=Microsoft.JET.OLEDB.4.0;data source=C:\northwind.mdb"; string sqlStr = "SELECT * FROM Employees"; // Create connection object OleDbConnection conn = new OleDbConnection(conStr); // Create data adapter object OleDbDataAdapter da = new OleDbDataAdapter(sqlStr,conn); // Create a dataset object and fill with data using data adapter's Fill method DataSet ds = new DataSet(); da.Fill(ds, "Employees");

// Hide the column and attach dataset's DefaultView to the datagrid control ds.Tables["Employees"].Columns["LastName"].ColumnMapping = MappingType.Hidden; dataGrid1.DataSource = ds.Tables["Employees"];

2) Another way to hide a column is to set its width to zero. Check out the FAQ How do I set the width of a column in my DataGrid?. 3) Another way to hide a column is to create a custom table style, and as you add columnstyles to your tablestyle, omit the column you want hidden. Check out the FAQ How do I add an unbound column in my bound DataGrid? to see how to create a custom table style. 5.13 How do I color a individual cell depending upon its value or some external method? We give three different methods for doing this. y The first one overrides Paint in a derived columnstyle and sets the backcolor there. y The second uses a delegate to set the color in the Paint override. y The third method adds an event to the derived column style to allow you to set the color in an event handler. Method 1 You can do this by deriving from DataGridTextBoxColumn and overriding the Paint method to conditionally set the backColor and foreColor. The sample code below colors any cell that starts with a letter higher than 'F'. You can download a project (C#, VB) using this class. [C#] public class DataGridColoredTextBoxColumn : DataGridTextBoxColumn { protected override void Paint(System.Drawing.Graphics g, System.Drawing.Rectangle bounds, System.Windows.Forms.CurrencyManager source, int rowNum, System.Drawing.Brush backBrush, System.Drawing.Brush foreBrush, bool alignToRight) { // the idea is to conditionally set the foreBrush and/or backbrush // depending upon some criteria on the cell value // Here, we color anything that begins with a letter higher than 'F' try{ object o = this.GetColumnValueAtRow(source, rowNum); if( o!= null) { char c = ((string)o)[0]; if( c > 'F') { // could be as simple as // backBrush = new SolidBrush(Color.Pink);

// or something fancier... backBrush = new LinearGradientBrush(bounds, Color.FromArgb(255, 200, 200), Color.FromArgb(128, 20, 20), LinearGradientMode.BackwardDiagonal); foreBrush = new SolidBrush(Color.White); } } } catch(Exception ex){ /* empty catch */ } finally{ // make sure the base class gets called to do the drawing with // the possibly changed brushes base.Paint(g, bounds, source, rowNum, backBrush, foreBrush, alignToRight); } } } [VB.NET} Public Class DataGridColoredTextBoxColumn Inherits DataGridTextBoxColumn Public Sub New() End Sub Protected Overloads Overrides Sub Paint(ByVal g As Graphics, ByVal bounds As Rectangle, ByVal source As CurrencyManager, ByVal rowNum As Integer, ByVal backBrush As Brush, ByVal foreBrush As Brush, ByVal alignToRight As Boolean) ' the idea is to conditionally set the foreBrush and/or backbrush ' depending upon some crireria on the cell value ' Here, we color anything that begins with a letter higher than 'F' Try Dim o As Object o = Me.GetColumnValueAtRow(source, rowNum) If (Not (o) Is Nothing) Then Dim c As Char c = CType(o, String).Substring(0, 1) If (c > "F") Then ' could be as simple as ' backBrush = new SolidBrush(Color.Pink); ' or something fancier... backBrush = New LinearGradientBrush(bounds, Color.FromArgb(255, 200, 200), Color.FromArgb(128, 20, 20), LinearGradientMode.BackwardDiagonal) foreBrush = New SolidBrush(Color.White) End If End If Catch ex As Exception ' empty catch Finally

' make sure the base class gets called to do the drawing with ' the possibly changed brushes MyBase.Paint(g, bounds, source, rowNum, backBrush, foreBrush, alignToRight) End Try End Sub End Class Method 2 To use some method to provide the cell color, you can use a similar technique as discussed above. But instead of setting the color based on cell value, call a delegate method instead. This delegate can be passed in using the constructor for the custom column style. You can download a sample that shows how this can be done. Method 3 If you want a more flexible solution, you could expose an event as part of your derived columnstyle that fires in the Paint override. This would allow the handler of the event to set the color value depending upon the row and column parameters that are passed as part of the event args. You can download a sample (C#, VB) that implements this technique. The sample actually handles allowing a cell to be editable on a cell by cell basis through an event. This sample colors the back ground of the disabled cell gray. You could modify the eventargs to include a backcolor, and use this event to color cells based on row and column values removing the event call in the Edit override. 5.14 How can I put a checkbox in a column of my DataGrid? You create a custom DataTableStyle that contains column styles for each column you want to display. You add the column styles in the order you want them to appear. Here are the steps to add an string column, an int column and a bool check column to a DataGrid. You can also download a working project. // code assumes you have a DataSet named myDataSet, a table named "EastCoastSales" and a DataGrid myDataGrid //STEP 1: Create a DataTable style object and set properties if required. DataGridTableStyle ts1 = new DataGridTableStyle(); //specify the table from dataset (required step) ts1.MappingName = "EastCoastSales"; // Set other properties (optional step) ts1.AlternatingBackColor = Color.LightBlue; //STEP 2: Create a string column and add it to the tablestyle DataGridColumnStyle TextCol = new DataGridTextBoxColumn(); TextCol.MappingName = "custName"; //from dataset table TextCol.HeaderText = "Customer Name"; TextCol.Width = 250; ts1.GridColumnStyles.Add(TextCol); //STEP 3: Create an int column style and add it to the tablestyle //this requires setting the format for the column through its property descriptor

PropertyDescriptorCollection pdc = this.BindingContext [myDataSet, "EastCoastSales"].GetItemProperties(); //now created a formated column using the pdc DataGridDigitsTextBoxColumn csIDInt = new DataGridDigitsTextBoxColumn(pdc["CustID"], "i", true); csIDInt.MappingName = "CustID"; csIDInt.HeaderText = "CustID"; csIDInt.Width = 100; ts1.GridColumnStyles.Add(csIDInt); //STEP 4: Add the checkbox DataGridColumnStyle boolCol = new DataGridBoolColumn(); boolCol.MappingName = "Current"; boolCol.HeaderText = "Info Current"; //uncomment this line to get a two-state checkbox //((DataGridBoolColumn)boolCol).AllowNull = false; boolCol.Width = 150; ts1.GridColumnStyles.Add(boolCol); //STEP 5: Add the tablestyle to your datagrid's tablestlye collection myDataGrid.TableStyles.Add(ts1);

5.15 How can I restrict the keystrokes that will be accepted in a column of my datagrid? You can create a custom column style and handle the KeyPress event of its TextBox member. Below is the code showing how this might be done. You can also download a sample project (C#, VB) that shows an implementation of this idea. public class DataGridDigitsTextBoxColumn : DataGridTextBoxColumn { public DataGridDigitsTextBoxColumn(System.ComponentModel.PropertyDescriptor pd, string format, bool b) : base(pd, format, b) { this.TextBox.KeyPress += new System.Windows.Forms.KeyPressEventHandler(HandleKeyPress); } private void HandleKeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e) { //ignore if not digit or control key if(!char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar)) e.Handled = true; //ignore if more than 3 digits if(this.TextBox.Text.Length >= 3 && !char.IsControl(e.KeyChar)) e.Handled = true; } protected override void Dispose(bool disposing)

{ if(disposing) this.TextBox.KeyPress -= new System.Windows.Forms.KeyPressEventHandler(HandleKeyPress); base.Dispose(disposing); } }

5.16 How do I make a field auto increment as new rows are added to my datagrid? DataTable myTable = new DataTable("Customers"); ... DataColumn cCustID = new DataColumn("CustID", typeof(int)); cCustID.AutoIncrement = true; cCustID.AutoIncrementSeed = 1; cCustID.AutoIncrementStep = 1; myTable.Columns.Add(cCustID);

5.17 How can I prevent a particular cell from being editable? You can do this by deriving a custom column style and overriding its virtual Edit member. Below is an override that will prevent the cell in row 1 of the column from getting the edit focus. You can paste this code in the DataGridDigitsTextBoxColumn sample to see it work. If you want a more flexible solution, you could expose an event as part of your derived columnstyle that fires right before the call to the baseclass in the Edit override. This would allow the handler of the event to set the enable value depending upon the row and column parameters that are passed as part of the event args. You can download a sample (C#, VB) that implements this technique. The sample also fires the event right before painting the cell to decide whether to paint a gray background for the disabled cell. You could modify the eventargs to include a backcolor, and use this event to color cells based on row and column values. //this override will prevent the cell in row 1 from getting the edit focus protected override void Edit(System.Windows.Forms.CurrencyManager source, int rowNum, System.Drawing.Rectangle bounds, bool readOnly, string instantText, bool cellIsVisible) { if(rowNum == 1) return; base.Edit(source, rowNum, bounds, readOnly, instantText, cellIsVisible); }

5.18 How do I move columns in a datagrid?

The columns appear in the order that their column styles were added to the tablestyle being used by the grid. If you want to change this order, you would need to create a new table style, and add the columnstyles in the order you want things to appear. Here is some code snippets that suggest how to do this. [C#] public void MoveColumn(DataGrid _dataGrid, string _mappingName, int fromCol, int toCol) { if(fromCol == toCol) return; DataGridTableStyle oldTS = _dataGrid.TableStyles[_mappingName]; DataGridTableStyle newTS = new DataGridTableStyle(); newTS.MappingName = _mappingName; for(int i = 0; i < oldTS.GridColumnStyles.Count; ++i) { if(i != fromCol && fromCol < toCol) newTS.GridColumnStyles.Add(oldTS.GridColumnStyles[i]); if(i == toCol) newTS.GridColumnStyles.Add(oldTS.GridColumnStyles[fromCol]); if(i != fromCol && fromCol > toCol) newTS.GridColumnStyles.Add(oldTS.GridColumnStyles[i]); } _dataGrid.TableStyles.Remove(oldTS); _dataGrid.TableStyles.Add(newTS); } //sample usage private void button1_Click(object sender, System.EventArgs e) { MoveColumn(myDataGrid, "Customers", 3, 1); } [VB.NET] Public Sub MoveColumn(_dataGrid As DataGrid, _mappingName As String, fromCol As Integer, toCol As Integer) If fromCol = toCol Then Return End If Dim oldTS As DataGridTableStyle = _dataGrid.TableStyles(_mappingName) Dim newTS As New DataGridTableStyle() newTS.MappingName = _mappingName Dim i As Integer i=0 While i < oldTS.GridColumnStyles.Count If i <> fromCol And fromCol < toCol Then newTS.GridColumnStyles.Add(oldTS.GridColumnStyles(i)) End If If i = toCol Then newTS.GridColumnStyles.Add(oldTS.GridColumnStyles(fromCol)) End If

If i <> fromCol And fromCol > toCol Then newTS.GridColumnStyles.Add(oldTS.GridColumnStyles(i)) End If i=i+1 End While _dataGrid.TableStyles.Remove(oldTS) _dataGrid.TableStyles.Add(newTS) End Sub 'MoveColumn 'sample usage Private Sub button1_Click(sender As Object, e As System.EventArgs) MoveColumn(myDataGrid, "Customers", 3, 1) End Sub 'button1_Click

5.19 How can I do cell by cell validation in a datagrid? There are problems trying to implement cell by cell validation using the grid's Validating event architecture. The problem is that the grid is not the object handling the data. Instead, a TextBox or some other control is the control managing the changing of the cell contents. One way to implement the validation at the grid level is to handle the CurrentCellChanged event, and if the previous cell's value is not proper, then return to that cell. You can download a sample that implements this process. The sample only handles the validation from cell to cell movement. If you want to handle the validation when the user clicks on the forms Close button, then you would have to add a special event handler for this and do one last validation at this point. 5.20 How do I programmatically determine the selected rows in a datagrid? The method DataGrid.IsSelected can tell you if a particular row is selected. So, you could use IsSelected in a loop through all your rows to finds if multiple rows have been selected. Depending upon the size of your datagrid, this may be a viable solution. If not, you could track the selections yourself by monitoring the key actions and the mouse actions. This would be more work. Thanks to John Hughes to the suggestion to use the dataview. [C#] public ArrayList GetSelectedRows(DataGrid dg) { ArrayList al = new ArrayList(); CurrencyManager cm = (CurrencyManager)this.BindingContext[dg.DataSource, dg.DataMember]; DataView dv = (DataView)cm.List; for(int i = 0; i < dv.Count; ++i) { if(dg.IsSelected(i)) al.Add(i); } return al;

} private void button1_Click(object sender, System.EventArgs e) { string s = "Selected rows:"; foreach(object o in GetSelectedRows(dataGrid1)) { s+=""+o.ToString(); } MessageBox.Show(s); } [VB.NET] Public Function GetSelectedRows(ByVal dg As DataGrid) As System.Collections.ArrayList Dim al As New ArrayList() Dim cm As CurrencyManager = Me.BindingContext(dg.DataSource, dg.DataMember) Dim dv As DataView = CType(cm.List, DataView) Dim i As Integer For i = 0 to dv.Count - 1 If dg.IsSelected(i) Then al.Add(i) End If End Next Return al End Function 'GetSelectedRows Private Sub button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click Dim s As String = "Selected rows:" Dim o As Object For Each o In GetSelectedRows(dataGrid1) s += " " + o.ToString() Next o MessageBox.Show(s) End Sub 'button1_Click

5.21 How can I bind the datagrid to a datasource without using any wizards? Here is a really simple data binding sample. Just drag and drop a datagrid onto a default Windows Forms application. Follow the steps below to bind this grid to the NorthWind db in SQL server. Complete Sample: simpledata.zip // Create a connection SqlConnection connection = new SqlConnection(this.GetConnectionString()); // Create a data adapter. Think of the data adapter as an object that knows how to get the data from the

// data source into a dataset SqlDataAdapter dataAdapter = new SqlDataAdapter(this.GetCommandText(), connection); // fill the dataset using the data adapter DataSet dataSet = new DataSet("Customers"); dataAdapter.Fill(this.dataSet, "Customers"); // bind to the grid grid.DataSource = this.dataSet; // the big picture grid.DataMember = "Customers"; // the specific table that we want to bind to // The connection text looks like this // If your SQL server is running on the default port, you can remove the port attribute. private string GetConnectionString() { string server = "your_server_name"; string serverPort = "port_address"; string catalog = "NorthWind"; string password = "user_pass"; string userId = "user_name"; string connectionString = "data source={0},{1};initial catalog={2};" + "password={3}; user id={4}; packet size=4096"; return string.Format(connectionString, server, serverPort, catalog, password, userId); } // The command text looks like this private string GetCommandText() { string commandText = "Select * from customers"; return commandText; }

5.22 How can I bind two datagrids in a Master-Detail relationship? Please download this sample before reading the rest of this FAQ. Looking through the sample will help follow the description. simpledata2.zip What this boils down to is this: 1) Load both Master and Details queries in a dataset. // I am using the SQL server NorthWind database this.dataAdapterMaster.Fill(this.dataSet, "Customers"); this.dataAdapterDetails.Fill(this.dataSet, "Orders"); 2) Bind the master data grid to the Master dataset table.

// The master view grid.DataSource = this.dataSet; grid.DataMember = "Customers"; 3) Create a relationship that describes how the two tables relate to each other. A primary key foreign key relationship is defined by two attributes. The primary key column in the master table The foreign key column in the details table The created relationship is added to the dataset. this.dataSet.Relations.Add("CustomersToOrders", dataSet.Tables["Customers"].Columns["CustomerID"], dataSet.Tables["Orders"].Columns["CustomerID"]); 4) Set the data member for the details table to be the name of relationship that was added to the dataset. // The name of the relation is to be used as the DataMember for the // details view details.DataSource = this.dataSet; // use the relationship called "CustomersToOrders in the Customers table. // Remember that we called the relationship "CustomersToOrders". details.DataMember = "Customers.CustomersToOrders";

5.23 How do I get the row or column that has been clicked on? You can use the DataGrid's HitTest method, passing it a point in the grid's client coordinate system, and returning a HitTestInfo object that holds all the row and column information that you want. [C#] // X & Y are in the grid' coordinates. If they are in screen coordinates, call dataGrid1.PointToClient method System.Drawing.Point pt = new Point(X, Y); DataGrid.HitTestInfo hti = dataGrid1.HitTest(pt); if(hti.Type == DataGrid.HitTestType.Cell) { MessageBox.Show(dataGrid1[hti.Row, hti.Column].ToString()); } else if(hti.Type == DataGrid.HitTestType.ColumnHeader) { MessageBox.Show(((DataView) DataGrid1.DataSource).Table.Columns[hti.Column].ToString()); } [VB.NET] ' X & Y are in the grid' coordinates. If they are in screen coordinates, call dataGrid1.PointToClient method Dim pt = New Point(X, Y)

Dim hti As DataGrid.HitTestInfo = dataGrid1.HitTest(pt) If hti.Type = DataGrid.HitTestType.Cell Then MessageBox.Show(dataGrid1(hti.Row, hti.Column).ToString()) Else If hti.Type = DataGrid.HitTestType.ColumnHeader Then 'assumes datasource is a dataview MessageBox.Show(CType(DataGrid1.DataSource, DataView).Table.Columns(hti.Column).ToString()) End If End If

5.24 How do I add an unbound column to my bound datagrid? The idea is to create the 'bound' table in your dataset, and then add an extra 'unbound' column. The steps are to derive a custom columnstyle that overrides Paint where you calculate and draw the unbound value. You can also override Edit to prevent the user from selecting your unbound column. Then to get your datagrid to use this special column style, you create a tablestyle and add the column styles to it in the order you want the columns to appear in the datagrid. Here are code snippets that derive the column and use the derived column. You can download a working sample. // custom column style that is an unbound column public class DataGridUnboundColumn : DataGridTextBoxColumn { protected override void Edit(System.Windows.Forms.CurrencyManager source, int rowNum, System.Drawing.Rectangle bounds, bool readOnly, string instantText, bool cellIsVisible) { //do not allow the unbound cell to become active if(this.MappingName == "UnBound") return; base.Edit(source, rowNum, bounds, readOnly, instantText, cellIsVisible); } protected override void Paint(System.Drawing.Graphics g, System.Drawing.Rectangle bounds, System.Windows.Forms.CurrencyManager source, int rowNum, System.Drawing.Brush backBrush, System.Drawing.Brush foreBrush, bool alignToRight) { //clear the cell g.FillRectangle(new SolidBrush(Color.White), bounds); //compute & draw the value //string s = string.Format("{0} row", rowNum); // col 0 + 2 chars from col 1 DataGrid parent = this.DataGridTableStyle.DataGrid; string s = parent[rowNum, 0].ToString() + ((parent[rowNum, 1].ToString())+ " ").Substring(0,2); Font font = new Font("Arial", 8.25f);

g.DrawString(s, font, new SolidBrush(Color.Black), bounds.X, bounds.Y); font.Dispose(); } } //code that uses this unbound column private void Form1_Load(object sender, System.EventArgs e) { // Set the connection and sql strings // assumes your mdb file is in your root string connString = @"Provider=Microsoft.JET.OLEDB.4.0;data source=C:\northwind.mdb"; string sqlString = "SELECT * FROM customers"; OleDbDataAdapter dataAdapter = null; DataSet _dataSet = null; try { // Connection object OleDbConnection connection = new OleDbConnection(connString); // Create data adapter object dataAdapter = new OleDbDataAdapter(sqlString, connection); // Create a dataset object and fill with data using data adapter's Fill method _dataSet = new DataSet(); dataAdapter.Fill(_dataSet, "customers"); connection.Close(); } catch(Exception ex) { MessageBox.Show("Problem with DB access-\n\n connection: " + connString + "\r\n\r\n query: " + sqlString + "\r\n\r\n\r\n" + ex.ToString()); this.Close(); return; } // Create a table style that will hold the new column style // that we set and also tie it to our customer's table from our DB DataGridTableStyle tableStyle = new DataGridTableStyle(); tableStyle.MappingName = "customers"; // since the dataset has things like field name and number of columns, // we will use those to create new columnstyles for the columns in our DB table int numCols = _dataSet.Tables["customers"].Columns.Count; //add an extra column at the end of our customers table _dataSet.Tables["customers"].Columns.Add("Unbound"); DataGridTextBoxColumn aColumnTextColumn ;

for(int i = 0; i < numCols; ++i) { aColumnTextColumn = new DataGridTextBoxColumn(); aColumnTextColumn.HeaderText = _dataSet.Tables["customers"].Columns[i].ColumnName; aColumnTextColumn.MappingName = _dataSet.Tables["customers"].Columns[i].ColumnName; tableStyle.GridColumnStyles.Add(aColumnTextColumn); //display the extra column after column 1. if( i == 1) { DataGridUnboundColumn unboundColStyle = new DataGridUnboundColumn(); unboundColStyle.HeaderText = "UnBound"; unboundColStyle.MappingName = "UnBound"; tableStyle.GridColumnStyles.Add(unboundColStyle); } } // make the dataGrid use our new tablestyle and bind it to our table dataGrid1.TableStyles.Clear(); dataGrid1.TableStyles.Add(tableStyle); dataGrid1.DataSource = _dataSet.Tables["customers"]; } }

5.25 How do I get the row and column of the current cell in my datagrid? You can use the CurrentCell property of the DataGrid. private void button1_Click(object sender, System.EventArgs e) { intcolNum = dataGrid1.CurrentCell.ColumnNumber; int rowNum = dataGrid1.CurrentCell.RowNumber; object cellValue = dataGrid1[rowNum, colNum]; string s = string.Format("row={0} col={1} value={2}", rowNum, colNum, cellValue); MessageBox.Show(s); }

5.26 How can I prevent the Enter key from moving to the next cell when the user is actively editing the cell and presses Enter? Override the method ProcessKeyPreview in your DataGrid. protected override bool ProcessKeyPreview(ref System.Windows.Forms.Message m) { Keys keyCode = (Keys)(int)m.WParam & Keys.KeyCode;

if((m.Msg == WM_KEYDOWN || m.Msg == WM_KEYUP) && keyCode == Keys.Enter ) return false; return true; }

5.27 How do I set the width of a column in my DataGrid? To set a column width, your datagrid must be using a non-null DataGridTableStyle. Once this is in place, you can set the column width by first getting the tablestyle and then using that object to obtain a column style with which you can set the width. Here are some code snippets showing how you might do this. //.... make sure your DataGrid is using a tablestyle dataGrid1.DataSource = _dataSet.Tables["customers"]; DataGridTableStyle dgts = new DataGridTableStyle(); dgts.MappingName = "customers"; dataGrid1.TableStyles.Add(dgts); //...... //method to set a column with by colnumber public void SetColWidth(DataGridTableStyle tableStyle, int colNum, int width) { try { tableStyle.GridColumnStyles[colNum].Width = width; tableStyle.DataGrid.Refresh(); } catch{} //empty catch .. do nothing } //.... // here is how you might call this method private void button1_Click(object sender, System.EventArgs e) { DataGridTableStyle tableStyle = dataGrid1.TableStyles["customers"]; SetColWidth(tableStyle, 1, 200); }

5.28 How can I implement OLE Drag & Drop between a DataGrid and another OLE DnD object that supports the Text format? The attached samples (C#, VB) have a derived datagrid that supports OLE D&D with any OLE D&D provider that handles a Text formatted data object. The derived grid handles six events to allow it to be both a drop source and a drop target. The sample project has two datagrids where you can drag cell text back and forth. You can also open Excel, and drag text between Excel and either datagrid.

Here are the events that are handled in this sample. y MouseDown - Used to save the row and column of a mousedown, 'mousedowncell'. y MouseMove - Checks to see if you drag off the mousedowncell, and if so, starts a the DoDragDrop. y MouseUp - Used to reset the mousedowncell. y DragEnter - Checks to see if the data object has text, and if so, allows a Copy operation. (This could be changed to support Move/Copy.) y DragOver - Used to set a NoDrop cursor if you move over the mousedowncell (if mousedowncell has been set). y DragDrop - Used to drop the text into the datagrid. 5.29 How can I tell if the current row has changed and whether I am on the AddNew row or not? The DataGrid's CurrentCellChanged event is hit even if you just change cells in the current row. If you want an event that is only hit when you change rows, then you have to look at the binding manager. This object has both a CurrentChanged event and a PositionChanged event which are hit when you change rows. To decide whether you are on the AddNew row or not, you can again use the binding manager and compare the number of rows it returns with the number of rows in your data table. Below is some code snippets showing how you might get at this information. private System.Windows.Forms.DataGrid dataGrid1; private BindingManagerBase bindingManager; private void Form1_Load(object sender, System.EventArgs e) { // Creating connection and command sting string conStr = @"Provider=Microsoft.JET.OLEDB.4.0;data source=C:\northwind.mdb"; string sqlStr = "SELECT * FROM Employees"; // Create connection object OleDbConnection conn = new OleDbConnection(conStr); // Create data adapter object OleDbDataAdapter da = new OleDbDataAdapter(sqlStr,conn); // Create a dataset object and fill with data using data adapter's Fill method DataSet ds = new DataSet(); da.Fill(ds, "Employees"); dataGrid1.DataSource = ds.Tables["Employees"]; bindingManager = this.BindingContext[dataGrid1.DataSource]; bindingManager.PositionChanged += new System.EventHandler(RowChanged); } private void RowChanged(object sender, System.EventArgs e) { Console.WriteLine("RowChanged " + bindingManager.Position.ToString() ); bool lastRow = bindingManager.Count > ((DataTable)dataGrid1.DataSource).Rows.Count;

if(lastRow) Console.WriteLine("lastRow"); }

5.30 How do I hide the gridlines or set them to a particular color? If your datagrid does not have a custom TableStyle associated with it, then there are two DataGrid properties that control the color and visibility of the gridlines. DataGrid..GridLineColor DataGrid.GridLineStyle If you have a custom TableStyle. you need to set the color within the TableStyle. Here is code that makes the line color the same as the background color, and hence hides the gridlines. You can download a sample. private void Form1_Load(object sender, System.EventArgs e) { // Set the connection and sql strings // assumes your mdb file is in your root string connString = @"Provider=Microsoft.JET.OLEDB.4.0;data source=C:\northwind.mdb"; string sqlString = "SELECT * FROM customers"; OleDbDataAdapter dataAdapter = null; DataSet _dataSet = null; try { // Connection object OleDbConnection connection = new OleDbConnection(connString); // Create data adapter object dataAdapter = new OleDbDataAdapter(sqlString, connection); // Create a dataset object and fill with data using data adapter's Fill method _dataSet = new DataSet(); dataAdapter.Fill(_dataSet, "customers"); connection.Close(); } catch(Exception ex) { MessageBox.Show("Problem with DB access-\n\n connection: " + connString + "\r\n\r\n query: " + sqlString + "\r\n\r\n\r\n" + ex.ToString()); this.Close(); return; } // Create a table style that will hold the new column style // that we set and also tie it to our customer's table from our DB

DataGridTableStyle tableStyle = new DataGridTableStyle(); tableStyle.MappingName = "customers"; tableStyle.GridLineColor = dataGrid1.BackColor; dataGrid1.TableStyles.Add(tableStyle); dataGrid1.DataSource = _dataSet.Tables["customers"]; }

5.31 How can I get the selected text in an active gridcell? You need to get the DataGridTextBoxColumn.TextBox member, and retrieve the SelectedText from it for the active cell. The sample shows how you can do this as part of handling this TextBox's rightClick. This code assumes you have specifically added DataGridTextBoxColumn for each column style. You can also download a VB sample. private void HandleMouseDown(object sender, System.Windows.Forms.MouseEventArgs e) { if(e.Button == MouseButtons.Right) { DataGridTableStyle ts = dataGrid1.TableStyles["customers"]; DataGridTextBoxColumn cs = (DataGridTextBoxColumn)ts.GridColumnStyles[dataGrid1.CurrentCell.ColumnNumber]; MessageBox.Show("Selected: " + cs.TextBox.SelectedText); } }

5.32 How do I determine whether a checkbox in my datagrid is checked or not? If the column is a boolean column, you can just cast the object returned by the indexer to a bool and use it. if((bool)dataGridTopics[row, column]) MessageBox.Show("I am true"); else MessageBox.Show("I am false");

5.33 How can I move rows by dragging the row header cell? One way to implement this is to derive a DataGrid and override the virtual OnMouseDown, OnMouseMove and OnMouseUp methods. In your overrides, if the mousedown is on a row header, track the initial mousedown row, and as it moves, draw a line to indicate a target position. Then on the mouseup, handle moving the row. You can download a sample (C#, VB) that illustrates how this might be done.

5.34 How can I control the cursor over my DataGrid? One way you can do this is to derive the DataGrid and override its WndProc method to handle the WM_SETCURSOR method yourself. In your override, you can do hit testing to decide when you want to set the cursor, or when you want to call the base class and let it set the cursor. Here are sample projects (VB and C#) showing the technique. Public Class MyDataGrid Inherits DataGrid Private Const WM_SETCURSOR As Integer = 32 Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message) If m.Msg <> WM_SETCURSOR Then MyBase.WndProc(m) Else 'see if you want the cursor - in col 1, rows 2, 3, 4 Dim pt As Point = Me.PointToClient(Control.MousePosition) Dim hti As DataGrid.HitTestInfo = Me.HitTest(pt.X, pt.Y) If hti.Column = 1 AndAlso hti.Row > 1 AndAlso hti.Row < 5 Then Cursor.Current = Cursors.Hand 'if not, call the baseclass Else MyBase.WndProc(m) End If End If End Sub 'WndProc Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Forms.MouseEventArgs) If Cursor.Current.Equals(Cursors.Hand) Then MessageBox.Show("My MouseDown") Else MyBase.OnClick(e) End If End Sub 'OnMouseDown End Class 'MyDataGrid

5.35 After scrolling with the mouse wheel on a selected row in a DataGrid I cannot get it back into view. Is there a work around? When you select a row in the DataGrid and scroll it out of view using the mouse wheel, you cannot get it back into view. The following is a workaround posted by one Windows Forms User: [C#] this.dataGrid1.MouseWheel+=new MouseEventHandler(dataGrid1_MouseWheel); private void dataGrid1_MouseWheel(object sender, MouseEventArgs e) { this.dataGrid1.Select();

} [VB.NET] AddHandler Me.dataGrid1.MouseWheel, addressof dataGrid1_MouseWheel Private Sub dataGrid1_MouseWheel(ByVal sender As Object, ByVal e As MouseEventArgs) Me.dataGrid1.Select() End Sub

5.36 How can I make the DataGrid column be blank and not display (null) as the default value? When you go to a new row in a DataGrid, the columns display (null). To prevent this behavior and make all the columns be empty, you can set the DefaultValue property of the DataColumn to be String.Empty. 5.37 How can I add a DateTimePicker column style to the DataGrid? This MSDN reference shows how how to create DateTimePicker column style in the DataGrid. 5.38 How can I put a combobox in a column of a datagrid? The CollectionEditor allows adding and removing items from a collection at design time. If the items in this collection implement IComponent or if they are derived from Component, the items in your collection can be persisted in code. Download collectioneditorsample.zip for a complete sample project. Here are some steps you should follow: 1) Make sure your item is derived from Component or implements Icomponent. For example: public class SomeItem : Component { private string label = ""; public SomeItem() { } public SomeItem(string label) { this.label = label; } public string Label { get { return label;

} set { label = value; } } public override string ToString() { return String.Format("SomeItem: ( Label = '{0}' )", label); } } 2) Next implement your Collection. You have to implement the Ilist interface. The CollectionEditor will determine the type of instances to be added to your collection using reflection inspecting the return type of the Item property (Indexer). [ Description("The set of properties to be edited with CollectionEditor."), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), Editor(typeof(System.ComponentModel.Design.CollectionEditor), typeof(System.Drawing.Design.UITypeEditor)) ] public SomeItemCollection SomeItems { get { if (someItemCollection == null) { someItemCollection = CreateItemCollection(); } return someItemCollection; } } protected SomeItemCollection CreateItemCollection() { return new SomeItemCollection(this); } public class SomeItemCollection : IList { // Fields private SomeItemDisplayer owner; public event EventHandler Changed; // Constructors public SomeItemCollection(SomeItemDisplayer owner) { this.owner = owner; }

internal ArrayList InnerArray { get { return owner.someItems; } } public void OnChanged() { if (this.Changed != null) this.Changed(this, EventArgs.Empty); } /// /// The CollectionEditor will determine the type of objects to be created by /// looking at the property type of the following method. CollectionEditor /// internally uses reflection to get the PropertyInfo for the "Item" property. /// This method must be public. /// public SomeItem this[int index] { set { if (value == null) throw new ArgumentNullException("value"); if (index < 0 || index >= this.InnerArray.Count) throw new ArgumentOutOfRangeException(String.Format("Invalid Argument {0}: {1}", "index", index.ToString())); this.InnerArray[index] = value; OnChanged(); } get { if (index < 0 || index >= this.InnerArray.Count) throw new ArgumentOutOfRangeException("Invalid Argument {0}: {1}", "index", index.ToString()); return (SomeItem) this.InnerArray[index]; } } public void AddRange(object[] items) { InnerArray.AddRange(items); OnChanged(); } ///

/// This implementation for the Item property for the IList interface. It's /// property type is object. /// object IList.this[int index] { set { // forward call to public indexer this[index] = (SomeItem) value; } get { // forward call to public indexer return this[index]; } } public /*IEnumerable*/ IEnumerator GetEnumerator() { return InnerArray.GetEnumerator(); } public /*ICollection*/ int Count { get { return InnerArray.Count; } } public /*IList*/ void RemoveAt(int index) { if (index < 0 || index >= this.InnerArray.Count) throw new ArgumentOutOfRangeException(String.Format("Invalid Argument {0}: {1}", "index", index.ToString())); this.InnerArray.RemoveAt(index); OnChanged(); } public /*IList*/ void Remove(object value) { int n = this.InnerArray.IndexOf(value,0); if (n != -1) this.RemoveAt(n); } public /*IList*/ void Insert(int index, object item) { if (item == null) throw new ArgumentNullException("item");

if (index < 0 || index > this.InnerArray.Count) throw new ArgumentOutOfRangeException(String.Format("Invalid Argument {0}: {1}","index", index.ToString())); this.InnerArray.Insert(index,item); OnChanged(); } public /*IList*/ int IndexOf(object value) { if (value == null) throw new ArgumentNullException(String.Format("Invalid Argument {0}: {1}","value", "null")); return this.InnerArray.IndexOf(value,0); } public /*IList*/ bool IsReadOnly { get { return false; } } public /*IList*/ void Clear() { InnerArray.Clear(); this.owner.Invalidate(); } public /*IList*/ bool Contains(object value) { return this.IndexOf(value) != -1; } void System.Collections.ICollection.CopyTo(Array dest, int index) { int count = this.InnerArray.Count; for (int n1 = 0; n1 < count; n1++) dest.SetValue(this.InnerArray[n1], (n1 + index)); } int System.Collections.IList.Add(object item) { int n = this.InnerArray.Add(item); OnChanged(); return n; } bool System.Collections.IList.IsFixedSize { get { return false;

} } bool System.Collections.ICollection.IsSynchronized { get { return false; } } object System.Collections.ICollection.SyncRoot { get { return this; } } } 3) Reference this collection in your control or component that should be designable. You need to supply a DesignerSerializationVisibility and an Editor attribute: private SomeItemCollection someItemCollection = null; ArrayList someItems = new ArrayList(); [ Description("The set of properties to be edited with CollectionEditor."), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), Editor(typeof(System.ComponentModel.Design.CollectionEditor), typeof(System.Drawing.Design.UITypeEditor)) ] public SomeItemCollection SomeItems { get { if (someItemCollection == null) { someItemCollection = CreateItemCollection(); } return someItemCollection; } } protected SomeItemCollection CreateItemCollection() { return new SomeItemCollection(this); }

5.39 I want to do custom handling of special keys such as the Tab key or F2 in the TextBox of a column in the DataGrid. How do I subclass this TextBox to get at it virtual members? You have to access a property called the Binding Context and then retrieve the BindingContext associated with the dataset and data member that you used for binding. After you have access to this object you just set the position property. You can move backward and forward through the dataset. Download a working sample that shows this: simpledata5.zip form.BindingContext[this.dataSet, "Customers"].Position -= 1; Remember that when you scroll through the dataset all associated controls will scroll since they all depend on the same context. This is useful if you want to have several controls that display sections of a row operate in tandem. 5.40 How can I have a column of icons in my datagrid? You need to derive a custom column style, override its Paint method and draw the image. In the attached samples, (VB and C#), there are two custom column styles. One style is a stand-alone unbound column that just displays an image. The second custom column style adds the image to the left side of a bound column. In both cases, the actual image that is displayed is from an imagelist passed into the column in its constructor. The index of the image to be drawn on a particular row is determined by a delegate passed into the column style through its constructor. 5.41 How can I make my DataGrid support a single select mode, and not the default multiselect mode? One way to do this is to derive a DataGrid, override its OnMouseDown and OnMouseMove methods. In the OnMouseDown, handle selecting and unselecting in your code without calling the base class if the click is on the header. In the OnMouseMove, don't call the baseclass to avoid dragging selections. Below is a code snippet for a sample derived DataGrid. You can download a full project (C#, VB). public class MyDataGrid : DataGrid { private int oldSelectedRow = -1; protected override void OnMouseMove(System.Windows.Forms.MouseEventArgs e) { //don't call the base class if left mouse down if(e.Button != MouseButtons.Left) base.OnMouseMove(e); } protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs e) { //don't call the base class if in header

DataGrid.HitTestInfo hti = this.HitTest(new Point(e.X, e.Y)); if(hti.Type == DataGrid.HitTestType.Cell) { if(oldSelectedRow > -1) this.UnSelect(oldSelectedRow); oldSelectedRow = -1; base.OnMouseDown(e); } else if(hti.Type == DataGrid.HitTestType.RowHeader) { if(oldSelectedRow > -1) this.UnSelect(oldSelectedRow); if((Control.ModifierKeys & Keys.Shift) == 0) base.OnMouseDown(e); else this.CurrentCell = new DataGridCell(hti.Row, hti.Column); this.Select(hti.Row); oldSelectedRow = hti.Row; } } }

5.42 How can I get celltips or tooltips to vary from cell to cell in my DataGrid? Felix Wu gives this solution in the microsoft.public.dotnet.frameworks.windowsforms newgroup. You can download a VB.NET sample. Override the OnPaint event of the TextBox. For example: protected override void OnPaint(PaintEventArgs e) { SolidBrush drawBrush = new SolidBrush(ForeColor); //Use the ForeColor property // Draw string to screen. e.Graphics.DrawString(Text, Font, drawBrush, 0f,0f); //Use the Font property } Note: You need to set the ControlStyles to "UserPaint" in the constructor. public MyTextBox() { // This call is required by the Windows.Forms Form Designer. this.SetStyle(ControlStyles.UserPaint,true); InitializeComponent(); // TODO: Add any initialization after the InitForm call }

5.43 How can I get notification of the changing of a value in a column of comboboxes within my datagrid? This solution is based off the combobox for datagrid columns found in this FAQ. That solution replaces the standard textbox with a combobox. To get notifications of the changes, a delegate is passed into the constructor for the custom column style. This delegate is called anytime the combobox value changes. It passes the row number and value as arguments. You can download sample code (C#, VB) that shows the implementation. 5.44 How can I make the datagrid have no currentcell? There appears to be no method to turn off a currentcell. When a cell is being edited, it is the TextBox embedded in the columnstyle that has the focus, and is displaying the highlighted text. You will notice in this situation, if you click the grid's title bar above the column headers, this TextEdit control loses focus, and the datagrid appears to have no current cell. We can simulate this click from code, and use it to expose a method in our datagrid to SetNoCurrentCell. Below is some code to illustrate this idea. public class MyDataGrid : DataGrid { public const int WM_LBUTTONDOWN = 513; // 0x0201 public const int WM_LBUTTONUP = 514; // 0x0202 [System.Runtime.InteropServices.DllImport("user32.dll")] static extern bool SendMessage(IntPtr hWnd, Int32 msg, Int32 wParam, Int32 lParam); public void SetNoCurrentCell() { //click on top left corner of the grid SendMessage( this.Handle, WM_LBUTTONDOWN, 0, 0); SendMessage( this.Handle, WM_LBUTTONUP, 0, 0); } } Here is some VB code. Public Class MyDataGrid Inherits DataGrid Public WM_LBUTTONDOWN As Integer = 513 Public WM_LBUTTONUP As Integer = 514 Shared _ Function SendMessage(hWnd As IntPtr, msg As Int32, wParam As Int32, lParam As Int32) As Boolean Public Sub SetNoCurrentCell()

'click on top left corner of the grid SendMessage(Me.Handle, WM_LBUTTONDOWN, 0, 0) SendMessage(Me.Handle, WM_LBUTTONUP, 0, 0) End Sub 'SetNoCurrentCell End Class 'MyDataGrid

5.45 How can I make my grid never have an active edit cell and always select whole rows (as in a browser-type grid)? For a single row select datagrid, you can get both these behaviors by using a custom column style and overriding its Edit method. In your override, handle unselecting and selecting the current row, and DO NOT call the base class. Not calling the base class keeps the cell from becoming active. Here is a code snippet suggesting how this might be done. You can download a full working project (CS, VB). public class DataGridNoActiveCellColumn : DataGridTextBoxColumn { private int SelectedRow = -1; protected override void Edit(System.Windows.Forms.CurrencyManager source, int rowNum, System.Drawing.Rectangle bounds, bool readOnly,string instantText,bool cellIsVisible) { //make sure previous selection is valid if(SelectedRow > -1 && SelectedRow < source.List.Count + 1) this.DataGridTableStyle.DataGrid.UnSelect(SelectedRow); SelectedRow = rowNum; this.DataGridTableStyle.DataGrid.Select(SelectedRow); } } If you want to handle multi-selections, then there is more work to be done. One solution is to still override Edit as above, but have an empty implementation. Do not have the code the handles the selected row and do not call the baseclass. To handle the selections in this case, subclass the datagrid and override its OnMouseDown virtual method to change all cell clicks into row header clicks. Also override OnCurrentCellChanged to handle moving the current cell with the keyboard. You can download a sample (C#, VB) that implements this functionality. 5.46 I have hidden (column width = 0) columns on the right side of my datagrid, but tabbing does not work properly. How can I get tabbing to work? As you tabbed to the right side of your grid, you have to tabbed through these zero width column and that is causing the tab key to appear not to work properly. One solution is to handle the grid's CurrentCellChanged event, and if you are on a border cell (among the hidden columns), then explicitly set the proper currentcell. //columns 3-6 are hidden with 0-column width... private int LEFTHIDDENCOLUMN = 3;

private int RIGHTHIDDENCOLUMN = 6; private void dataGrid1_CurrentCellChanged(object sender, System.EventArgs e) { if(dataGrid1.CurrentCell.ColumnNumber == LEFTHIDDENCOLUMN) dataGrid1.CurrentCell = new DataGridCell(dataGrid1.CurrentCell.RowNumber + 1, 0); else if(dataGrid1.CurrentCell.ColumnNumber == RIGHTHIDDENCOLUMN) dataGrid1.CurrentCell = new DataGridCell(dataGrid1.CurrentCell.RowNumber, LEFTHIDDENCOLUMN - 1); }

5.47 How can I get the number of rows in my DataGrid? One way you can do this is through the BindingManager.Count property. [C#] int numRows = dataGridDetails.BindingContext[dataGridDetails.DataSource, dataGridDetails.DataMember].Count; [VB.NET] Dim numRows as Integer = i dataGridDetails.BindingContext(dataGridDetails.DataSource, dataGridDetails.DataMember).Count;

5.48 How do I format a date column in a datagrid? If you have added a table style to your datagrid (so individual column styles have been generated), then you can use code such as this to set the Format property of the particular column style. [C#] //add format col 3 columnstyle where column 3 holds a date... DataGridTextBoxColumn dgtbc; dgtbc = dataGrid1.TableStyles[0].GridColumnStyles[3] as DataGridTextBoxColumn; if(dgtbc != null) dgtbc.Format = "g"; // or "u" or whatever format you want to see [VB.NET] 'add format col 3 columnstyle where column 3 holds a date... Dim dgtbc as DataGridTextBoxColumn dgtbc = CType(dataGrid1.TableStyles(0).GridColumnStyles(3), DataGridTextBoxColumn) If Not dgtbc is Nothing Then dgtbc.Format = "g" ' or "u" or whatever format you want to see End If

5.49 How can I change the width of the row headers or hide them? You can get at the width and visibility of the header row/column through a DataGridTableStyle.

DataGridTableStyle has properties such as RowHeadersVisible and RowHeadersWidth. DataGridTableStyle also controls things like selections colors and GridLine styles. // Create a table style that will hold the new column style // that we set and also tie it to our customer's table from our DB DataGridTableStyle tableStyle = new DataGridTableStyle(); tableStyle.MappingName = "customers"; //hide the column headers tableStyle.ColumnHeadersVisible = false; //change width of the row headers tableStyle.RowHeadersWidth = 100; // make the dataGrid use our new tablestyle and bind it to our table dataGrid1.TableStyles.Clear(); dataGrid1.TableStyles.Add(tableStyle);

5.50 How do I catch a doubleclick in my datagrid? The problem is that the first click of a double click may be caught by the datagrid (and used to activate the cell) while the second click goes to the TextBox for the columnstyle object. This means the TextBox thinks this is a singleclick, and does not fire its doubleclick event. One solution is to mark the time of the click caught by the datagrid. Then look at this time in the TextBox's mousedown handler to see if in fact the single click being looked at by the TextBox is part of a double click. You can download a sample (C#, VB) that illustrates how this might be done. 5.51 How can I make my last column wide enough to exactly occupy all the client area of the datagrid? If you have added a TableStyle for your grid, then the code below should set the right column width to be the empty space from a button click. If you need to dynamically do this in response to the user sizing other columns, then there may be more work. But if you only need to do it at the end of your Form_Load, then this code might be sufficient. It assumes your datasource is a datatable. You can download a sample project (C#, VB). private void button1_Click(object sender, System.EventArgs e) { int numCols = ((DataTable)(dataGrid1.DataSource)).Columns.Count; //the fudge -4 is for the grid borders int targetWidth = dataGrid1.ClientSize.Width - SystemInformation.VerticalScrollBarWidth 4; int runningWidthUsed = this.dataGrid1.TableStyles["customers"].RowHeaderWidth; for(int i = 0; i < numCols - 1; ++i) runningWidthUsed += this.dataGrid1.TableStyles["customers"].GridColumnStyles[i].Width; if(runningWidthUsed < targetWidth) this.dataGrid1.TableStyles["customers"].GridColumnStyles[numCols - 1].Width =

targetWidth - runningWidthUsed; }

5.52 How can I prevent my user from sizing columns in my datagrid? You can do this by subclassing your grid and overriding OnMouseMove, and not calling the baseclass if the point is on the columnsizing border. [C#] public class MyDataGrid : DataGrid { protected override void OnMouseMove(System.Windows.Forms.MouseEventArgs e) { DataGrid.HitTestInfo hti = this.HitTest(new Point(e.X, e.Y)); if(hti.Type == DataGrid.HitTestType.ColumnResize) { return; //no baseclass call } base.OnMouseMove(e); } } [VB.NET] Public Class MyDataGrid Inherits DataGrid Protected Overrides Sub OnMouseMove(ByVal e As System.Windows.Forms.MouseEventArgs) Dim hti As DataGrid.HitTestInfo = Me.HitTest(New Point(e.X,e.Y)) If hti.Type = DataGrid.HitTestType.ColumnResize Then Return 'no baseclass call End If MyBase.OnMouseMove(e) End Sub End Class The above code prevents the sizing cursor from appearing, but as Stephen Muecke pointed out to us, if the user just clicks on the border, he can still size the column. Stephen's solution to this problem is to add similar code in an override of OnMouseDown. [C#] protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs e) { DataGrid.HitTestInfo hti = this.HitTest(new Point(e.X, e.Y)); if(hti.Type == DataGrid.HitTestType.ColumnResize) { return; //no baseclass call }

base.OnMouseDown(e); } [VB.NET] Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Forms.MouseEventArgs) Dim hti As DataGrid.HitTestInfo = Me.HitTest(New Point(e.X,e.Y)) If hti.Type = DataGrid.HitTestType.ColumnResize Then Return 'no baseclass call End If MyBase.OnMouseDown(e) End Sub

5.53 How can I catch the bool values changing in a DataGridBoolColumn? There is no event fired when the boolean value changes. In the attached sample (C#, VB), a BoolValueChanged event has been added to a columnstyle derived from DataGridBoolColumn. Catching the changes requires some effort. The strategy is to save the current value when the cell begins being edited. This is done in an override of Edit. Then in a Paint override, checks are done to see if there is a click in the checkbox cell or if the space bar is hit. If either of these situations happen when the cell is actively being edited, the bool value is changed and the event fired. 5.54 When I click on a row header, the row is selected and no cell is active. How can I do this programmatically? This code adds a method to a derived grid that simulates a mouseclick on the rowheader of the row passed into the method. public class MyDataGrid : DataGrid { public const int WM_LBUTTONDOWN = 513; // 0x0201 public const int WM_LBUTTONUP = 514; // 0x0202 [System.Runtime.InteropServices.DllImport("user32.dll")] static extern bool SendMessage(IntPtr hWnd, Int32 msg, Int32 wParam, Int32 lParam); public void ClickRowHeader(int row) { //get a point to click Rectangle rect = this.GetCellBounds(row, 0); Int32 lparam = MakeLong(rect.Left - 4, rect.Top + 4); //click it SendMessage( this.Handle, WM_LBUTTONDOWN, 0, lparam); SendMessage( this.Handle, WM_LBUTTONUP, 0, lparam); } static int MakeLong(int LoWord, int HiWord)

{ return (HiWord << 16) | (LoWord & 0xffff); } } ..... ..... //usage - myDataGrid is of type MyDataGrid. private void button2_Click(object sender, System.EventArgs e) { myDataGrid.ClickRowHeader(2); }

5.55 How can I force the vertical scrollbar in my DataGrid to always be visible? Derive a DataGrid. In your derived grid, add a handler for the VertScrollBar.VisibleChanged event. In your handler, if the scrollbar is not visible, size it and position it, and then show it. The code below assumes no horizontal scrollbar is necessary. If it is present, you would have to adjust the sizing code. C# public class MyDataGrid : DataGrid { public MyDataGrid() { //make scrollbar visible & hook up handler this.VertScrollBar.Visible = true; this.VertScrollBar.VisibleChanged += new EventHandler(ShowScrollBars); } private int CAPTIONHEIGHT = 21; private int BORDERWIDTH = 2; private void ShowScrollBars(object sender, EventArgs e) { if(!this.VertScrollBar.Visible) { int width = this.VertScrollBar.Width; this.VertScrollBar.Location = new Point(this.ClientRectangle.Width - width BORDERWIDTH, CAPTIONHEIGHT); this.VertScrollBar.Size = new Size(width, this.ClientRectangle.Height CAPTIONHEIGHT - BORDERWIDTH); this.VertScrollBar.Show(); } } }

VB.NET Public Class MyDataGrid Inherits DataGrid Public Sub New() 'make scrollbar visible & hook up handler Me.VertScrollBar.Visible = True AddHandler Me.VertScrollBar.VisibleChanged, AddressOf ShowScrollBars End Sub 'New Private CAPTIONHEIGHT As Integer = 21 Private BORDERWIDTH As Integer = 2 Private Sub ShowScrollBars(sender As Object, e As EventArgs) If Not Me.VertScrollBar.Visible Then Dim width As Integer = Me.VertScrollBar.Width Me.VertScrollBar.Location = New Point(Me.ClientRectangle.Width - width BORDERWIDTH, CAPTIONHEIGHT) Me.VertScrollBar.Size = New Size(width, Me.ClientRectangle.Height CAPTIONHEIGHT - BORDERWIDTH) Me.VertScrollBar.Show() End If End Sub 'ShowScrollBars End Class 'MyDataGrid

5.56 How can I autosize a column in my datagrid? One way to do this is to use MeasureString to compute the size of the text in each cell, and then take the maximum value. Below is a code snippet that does this. It assumes your datagrid is bound to a datatable. You can download a full working sample. (C#,VB). public void AutoSizeCol(int col) { float width = 0; int numRows = ((DataTable) dataGrid1.DataSource).Rows.Count; Graphics g = Graphics.FromHwnd(dataGrid1.Handle); StringFormat sf = new StringFormat(StringFormat.GenericTypographic); SizeF size; for(int i = 0; i < numRows; ++ i) { size = g.MeasureString(dataGrid1[i, col].ToString(), dataGrid1.Font, 500, sf); if(size.Width > width) width = size.Width; } g.Dispose(); dataGrid1.TableStyles["customers"].GridColumnStyles[col].Width = (int) width + 8; // 8 is for leading and trailing padding }

5.57 How can I get rid of the error icon that appears when there is an editing error? Adam Chester gives this solution in a posting on the microsoft.public.dotnet.framework.windowsforms newgroup. DataTable dt = (DataTable)dataGrid1.DataSource; foreach(DataRow row in dt.GetErrors()) { row.RowError = ""; foreach(DataColumn col in dt.Columns) row.SetColumnError(col, ""); }

5.58 How do I find the top-left visible cell in a datagrid? In a Windows Forms DataGrid, there is no property exposed that gives you this information. But here is little trick that will allow you to get the top-left visible cell. 1) Add a private member Point pointInCell00 to the form containing the datagrid. 2) After the datagrid has been initialized, but before your user has been able to scroll it (say at the end of the Form_Load event), use code such as this to initialize pointInCell00 to be a point in cell 0,0. pointInCell00 = new Point(dataGrid1.GetCellBounds(0,0).X + 4, dataGrid1.GetCellBounds(0,0).Y + 4); 3) Then add this method to return DataGridCell that is the top-left cell. public DataGridCell TopLeftVisibleCell() { DataGrid.HitTestInfo hti = dataGrid1.HitTest(this.pointInCell00); return new DataGridCell(hti.Row, hti.Column); } //sample usage... private void button1_Click(object sender, System.EventArgs e) { DataGridCell dgc = TopLeftVisibleCell(); MessageBox.Show(dgc.RowNumber.ToString() + " " + dgc.ColumnNumber.ToString()); }

5.59 I want to display negative values with a CR suffix, instead of a minus sign. How can I perform custom formatting on the cells in my datagrid?

Take a look at this Microsoft KB article, HOW TO: Extend the Windows Form DataGridTextBoxColumn Control to Custom-Format Data (Q318581). It subclasses a DataGridColumnStyle and overrides both GetColumnValueAtRow and Commit to implement this behavior. 5.60 I want to do sort of a database join of two tables. How can I use data from two DataTables in a single DataGrid? Take a look at this Microsoft KB article, HOW TO: Extend the Windows Form DataGridTextBoxColumn to Display Data From Other Tables (C#,VB). It subclasses a DataGridColumnStyle and overrides GetColumnValueAtRow, Commit and ReadOnly to implement this behavior. 5.61 How do I display a column of buttons such as pushbuttons or combobox buttons? This sample (download C#, download VB) derives two custom columnstyles that display buttons. One displays a pushbutton with the cell text used as the button label. The second columnstyle displays text plus a dropdown button similar to a combobox button. Both columnstyles have an event that your form can handle to respond to clicks on the buttons. The row and column of the click are passed as part of the event arguments. Both columnstyles derive from DataGridTextBoxColumn, and override Paint and Edit. The Edit override does not call the baseclass to avoid allowing the cell going into the edit mode. In the Paint override, the text is drawn, and a bitmap showing the button face is drawn. There is no mouse handling within a columnstyle. To catch the click action, the columnstyle must handle the datagrid's MouseDown and MouseUp events. In the columnstyle handlers for these events, the handler draws the depressed button as well as firing the columnstyle's ColumnButtonClick event. Your handler for this ColumnButtonClick event should take whatever action as a result of the buttonclick. In the sample projects, the handler just displays a messagebox. 5.62 How can I put up a confirmation question when the user tries to delete a row in the datagrid by clicking on the row header and pressing the Delete key? You can handle this by subclassing your grid and overriding either PreProcessMessage or ProcessDialogKey. The code below assumes your datasource is a dataview. If it is not, you could just remove that check [C#] public override bool PreProcessMessage( ref Message msg ) { Keys keyCode = (Keys)(int)msg.WParam & Keys.KeyCode;

if(msg.Msg == WM_KEYDOWN && keyCode == Keys.Delete && ((DataView) this.DataSource).AllowDelete) { if(MessageBox.Show("Delete this row?", "", MessageBoxButtons.YesNo) == DialogResult.No) return true; } return base.PreProcessMessage(ref msg); } [VB.NET] (courtesy of Erik Johansen) Public Class DataGrid_Custom Inherits DataGrid Private Const WM_KEYDOWN = &H100 Public Overrides Function PreProcessMessage(ByRef msg As System.Windows.Forms.Message) As Boolean Dim keyCode As Keys = CType((msg.WParam.ToInt32 And Keys.KeyCode), Keys) If msg.Msg = WM_KEYDOWN And keyCode = Keys.Delete Then If MessageBox.Show("Delete This Row?", "Confirm Delete", MessageBoxButtons.YesNo) = DialogResult.No Then Return True End If End If Return MyBase.PreProcessMessage(msg) End Function End Class

5.63 How can I enable column selections in my datagrid? Here is a sample (C#, VB) that has column selections implemented. It derives a datagrid, adds a columns selection array list, and maintains this list in an override of OnMouseDown. Then to handle actually drawing the selected columns, it uses a derived column style, and sets the backBrush color and foreBrush color to the selection values in the override of the Paint method when it needs to draw a selected cell. As an alternative to drawing the selection in a derived column style, you could just draw the selection in your derived grid by overriding OnPaint, calling the base class and then looping through the columns selections, filling a rectangle over the selected columns. The problem with this technique is that you would either have to redraw text in column with the correct forecolor, or you would have to use an alphablended color to let the text show through the selection rectangle. Using an alphablended color would eliminate the need to a column style override, but would give a different appearance than the 'standard' selections that you see in a datagrid. The sample allows the row selections to continue to work so the column style override makes sense here. 5.64 How do I programmatically scroll the datagrid to a particular

row? The DataGrid has a protected GridVScrolled member that can be used to scroll the grid. To use it, you can derive from DataGrid and add a ScrollToRow method. Here is a code snippet. Public Class MyDataGrid Inherits DataGrid Sub ScrollToRow(ByVal row As Integer) If Not Me.DataSource Is Nothing Then Me.GridVScrolled(Me, New ScrollEventArgs(ScrollEventType.LargeIncrement, row)) End If End Sub End Class This solution uses information provided by Daniel Herling (MS) in the microsoft.public.dotnet.framework.windowsforms.databinding newsgroup. 5.65 How can I place text in the rowheader column of my datagrid? There is no text property exposed for a rowheader cell. But you can handle the Paint event and draw header text yourself. You can download sample projects (C#, VB) that illustrate one technique for doing so. The sample loads the datagrid in the form's Load event. In addition, this event is used to set the rowheaderwidth of the datagrid, and to remember the point where cell 0,0 is located. This point will allow us to find the toprow number when we need it to start drawing the header text. A handler for the datagrid's Paint event is used to draw the text. It finds the toprow using the point from the original cell 0,0, and using the toprow determines the correct text for each rowheader. Finally, to avoid the complication of the user changing rowheights, we derive a new grid to prevent this. private void dataGrid1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { int row = TopRow(); int yDelta = dataGrid1.GetCellBounds(row, 0).Height + 1; int y = dataGrid1.GetCellBounds(row, 0).Top + 2; CurrencyManager cm = (CurrencyManager) this.BindingContext[dataGrid1.DataSource, dataGrid1.DataMember]; while(y < dataGrid1.Height - yDelta && row < cm.Count) { //get & draw the header text... string text = string.Format("row{0}", row); e.Graphics.DrawString(text, dataGrid1.Font, new SolidBrush(Color.Black), 12, y); y += yDelta; row++; }

} Here is a datagrid with red row headers containing text.

5.66 How do I set default values for new rows in my datagrid? You use the DataColumn.DefaultValue property to provide default values for new rows. You access this property through the DataTable associated with the DataGrid, [C#] this.dataGrid1.DataSource = this._dataSet.Tables["orders"]; .... .... this._dataSet.Tables["Orders"].Columns[1].DefaultValue = "CustID"; // default value for column 1 this._dataSet.Tables["Orders"].Columns["OrderDate"].DefaultValue = DateTime.Now; // default value for OrderDate column [VB.NET] Me.dataGrid1.DataSource = Me._dataSet.Tables("Orders") .... .... Me._dataSet.Tables("Orders").Columns(1).DefaultValue = "CustID" ' default value for column 1 Me._dataSet.Tables("Orders").Columns("OrderDate").DefaultValue = DateTime.Now ' default value for OrderDate column

5.67 How do I iterate through all the rows and columns in my datagrid? You use the row index and column index as indexers on the DataGrid object.

[C#] private void button1_Click(object sender, System.EventArgs e) { CurrencyManager cm = (CurrencyManager)this.BindingContext[this.dataGrid1.DataSource]; int rowCount = cm.Count; //assumes datasource is a datatable... int colCount = ((DataTable)this.dataGrid1.DataSource).Columns.Count; for(int row = 0; row < rowCount; row++) { for(int col = 0; col < colCount; col++) { object cellValue = this.dataGrid1[row, col]; Console.Write(cellValue.ToString() + " "); } Console.WriteLine(""); } } [VB.NET] Private Sub button1_Click(sender As Object, e As System.EventArgs) Dim cm As CurrencyManager = CType(Me.BindingContext(Me.dataGrid1.DataSource), CurrencyManager) Dim rowCount As Integer = cm.Count 'assumes datasource is a datatable... Dim colCount As Integer = CType(Me.dataGrid1.DataSource, DataTable).Columns.Count Dim row As Integer For row = 0 To rowCount - 1 Dim col As Integer For col = 0 To colCount - 1 Dim cellValue As Object = Me.dataGrid1(row, col) Console.Write((cellValue.ToString() + " ")) Next col Console.WriteLine("") Next row End Sub 'button1_Click

5.68 How can I specially color only the currentcell of my readonly datagrid? You can use the first technique listed in this FAQ: How do I color an individual cell depending upon its value or some external method? The idea is to derive a custom columnstyle. override its Paint method to specially color the background of the currentcell. Also, override the Edit to avoid the current cell becoming active. Below is a code snippet showing how you can do this. You can also download samples(C#, VB).

Public Class DataGridColoredTextBoxColumn Inherits DataGridTextBoxColumn Private column As Integer ' column where this columnstyle is located... Public Sub New() column = -2 End Sub Protected Overloads Overrides Sub Paint(ByVal g As Graphics, ByVal bounds As Rectangle, ByVal source As CurrencyManager, ByVal rowNum As Integer, ByVal backBrush As Brush, ByVal foreBrush As Brush, ByVal alignToRight As Boolean) Try Dim grid As DataGrid = Me.DataGridTableStyle.DataGrid 'first time set the column properly If column = -2 Then Dim i As Integer i = Me.DataGridTableStyle.GridColumnStyles.IndexOf(Me) If i > -1 Then column = i End If End If If grid.CurrentRowIndex = rowNum And grid.CurrentCell.ColumnNumber = column Then backBrush = New LinearGradientBrush(bounds, Color.FromArgb(255, 200, 200), Color.FromArgb(128, 20, 20), LinearGradientMode.BackwardDiagonal) foreBrush = New SolidBrush(Color.White) End If Catch ex As Exception ' empty catch Finally ' make sure the base class gets called to do the drawing with ' the possibly changed brushes MyBase.Paint(g, bounds, source, rowNum, backBrush, foreBrush, alignToRight) End Try End Sub Protected Overloads Overrides Sub Edit(ByVal source As System.Windows.Forms.CurrencyManager, ByVal rowNum As Integer, ByVal bounds As System.Drawing.Rectangle, ByVal [readOnly] As Boolean, ByVal instantText As String, ByVal cellIsVisible As Boolean) 'do nothing... don't call the basecalss End Sub End Class

5.69 How can I make the Enter Key behave like the Tab Key and move to the next cell?

You can override ProcessCmdKey, catch the Enter Key, and swap it for a Tab key by sending a Tab, and not processing the Enter Key. [C#] public class MyDataGrid : DataGrid { protected override bool ProcessCmdKey(ref System.Windows.Forms.Message msg, System.Windows.Forms.Keys keyData) { if(msg.WParam.ToInt32() == (int) Keys.Enter) { SendKeys.Send("{Tab}"); return true; } return base.ProcessCmdKey(ref msg, keyData); } } [VB.NET] Public Class MyDataGrid Inherits DataGrid Protected Overrides Function ProcessCmdKey(ByRef msg As System.Windows.Forms.Message, keyData As System.Windows.Forms.Keys) As Boolean If msg.WParam.ToInt32() = CInt(Keys.Enter) Then SendKeys.Send("{Tab}") Return True End If Return MyBase.ProcessCmdKey(msg, keyData) End Function 'ProcessCmdKey End Class 'MyDataGrid

5.70 How do I use the DataColumn.Expression property to add a computed/combined column to my datagrid? The idea is to load your datatable in a normal fashion. Once the datatable is loaded, you can add an additional column that is computed from the other columns in your datatable. In the sample(CS, VB), we load the CustomerID, CompanyName, ContactName and ContactTitle from the Customers table in the NorthWind database. We then add an additional column that concatenates the ContactName and ContactTitle into one column. To add the additional column, we create a DataColumn, set a mapping name, and then use the Expression property to define how the column is to be computed. [C#] DataColumn dc = new DataColumn("Contact", typeof(string)); dc.Expression = "ContactName + ':' +ContactTitle"; _dataSet.Tables["customers"].Columns.Add(dc); [VB.NET]

Dim dc As DataColumn dc = New DataColumn("Contact", GetType(System.String)) dc.Expression = "ContactName + ':' +ContactTitle" _dataSet.Tables("customers").Columns.Add(dc) The sample actually shows two datagrids. The first one uses the default binding to display the entire table including our added column. In the second datagrid, we add a custom DataGridTableStyle to only display the CustomerID, CompanyName and our added column. 5.71 How can I change the font used in a grid cell on a cell by cell or row by row basis? One way to do this is to use a derived columnstyle, override the Paint method and do the text drawing yourself, using whatever font or colors you like. If you add an event to your derived column style that is fired immediately before the text is drawn, and use the event args to get the font and color information, you can let the event handler completely determine the look of any cell. The attached samples (C#, VB) use this technique to create a grid that looks like the grid in the picture. Both color and font varies on a cell basis or row basis in this picture.

5.72 How can I use a mouse to select cell ranges in my datagrid? The Windows Forms DataGrid does not support the selection of a range of cells other than a

group of rows. To select a row, you can click on its row header. But if you want to select a rectangular group of cells, you cannot. To add this support, you can catch the mousedown on a cell, and in mousemove, track whether the mouse is dragged with the button down to other cells. If so, draw a selection rectangle over this group of cells. The attached samples (C#, VB) show how this might be done. The drawing of the selection rectangle is done in an OnPaint override. The samples also try to handle autoscrolling as you hit a grid border so the selection process can continue for cells that are not currently visible. To make the selection rectangle look nicer, the code also hides the current cell when it draws a selection rectangle. The samples have a derived DataGrid that has a public member, SelectedRange, that holds the TopLeft and BottomRight coordinates of the selected range. The derived grid also has a public event, SelectionChanging, that fires prior to the selection changing as you drag your mouse. The event passes both the old selection and the new selection, and allows you to cancel the action if you choose. 5.73 How do I determine the DataGridTableStyle MappingName that should used for a DataGrid to make sure the grid uses my tablestyle? The DataGrid looks for a DataGridTableStyle.MappingName that is the type name of its datasource. So, depending upon what datasource you are using, this may be "ArrayList" for a ArrayList, "MyCollection[]" for an array of MyCollection objects, or "MyTableName" for a datatable, or whatever. Here is a code snippet provide by NoiseEHC on the microsoft.public.dotnet.framework.windowsforms.controls newsgroup that you can use to see exactly what mappingname is required for your datasource. [C#] //usage ShowMappingName(dataGrid1.DataSource); //implementation void ShowMappingName(object src) { IList list = null; Type type = null; if(src is Array) { type = src.GetType(); list = src as IList; } else { if(src is IListSource) src = (src as IListSource).GetList();

if(src is IList) { type = src.GetType(); list = src as IList; } else { MessageBox.Show("error"); return; } } if(list is ITypedList) MessageBox.Show((list as ITypedList).GetListName(null)); else MessageBox.Show(type.Name); } [VB.NET] Private Sub ShowMappingName(ByVal src As Object) Dim list As IList = Nothing Dim t As Type = Nothing If TypeOf (src) Is Array Then t = src.GetType() list = CType(src, IList) Else If TypeOf src Is IListSource Then src = CType(src, IListSource).GetList() End If If TypeOf src Is IList Then t = src.GetType() list = CType(src, IList) Else MessageBox.Show("Error") Return End If End If If TypeOf list Is ITypedList Then MessageBox.Show(CType(list, ITypedList).GetListName(Nothing)) Else MessageBox.Show(t.Name) End If End Sub

5.74 I have a derived DataGridColumnStyle. From within my Paint override, how can I get at other values in the DataGrid?

You can get a reference to the DataGrid with code such as: Dim grid As DataGrid = Me.DataGridTableStyle.DataGrid Once you have the grid, you can use an indexer to get the value of any particular column on the same row. Dim someValue as Object = grid(rowNum, 5) ' value in column 5....

5.75 How do I retrieve the current row from a DataTable bound to a DataGrid after the grid has been sorted? In an unsorted DataGrid bound to a DataTable, you can get a reference to a row in the DataTable through the DataGrid.CurrentRowIndex. [C#] DataTable dt = (DataTable) this.dataGrid1.DataSource; DataRow dr = dt.Rows[this.dataGrid1.CurrentRowIndex); [VB.NET] Dim dt As DataTable = Me.DataGrid1.DataSource Dim dr as DataRow = dt.Rows(Me.DataGrid1.CurrentRowIndex) But if the grid has been sorted, you can no longer get at the current row in the table through the grid's CurrentRowIndex. But for both unsorted and sorted grids, you can get at the current row through the BindingContext and the Current property of the BindingManagerBase. [C#] BindingManagerBase bm = this.dataGrid1.BindingContextr[this.dataGrid1.DataSource, this.dataGrid1.DataMember]; DataRow dr = ((DataRowView)bm.Current).Row; [VB.NET] Dim bm As BindingManagerBase = Me.DataGrid1.BindingContext(Me.DataGrid1.DataSource, Me.DataGrid1.DataMember) Dim dr As DataRow = CType(bm.Current, DataRowView).Row

5.76 How can I catch when the user clicks off the grid, say to close the form? You can catch clicking off a DataGrid in the DataGrid.Validated event. But this event is also hit when you use the Tab key to move around the grid. So, if you want to catch it exclusively for clicking off the grid, you have to ignore the event due to the tab. One way to do this is to derive DataGrid and override ProcessDialogKey, noting whether the key is a tab before processing it. Then in your Validated event handler, you can check for this tab. Here are some snippets. [C#] //the handler that checks the derived grid's field inTabKey private void dataGrid1_Validated(object sender, System.EventArgs e) { if(!this.dataGrid1.inTabKey)

{ Console.WriteLine( "Clicked off grid"); } else this.dataGrid1.inTabKey = false; } //derived class public class MyDataGrid: DataGrid { public bool inTabKey = false; protected override bool ProcessDialogKey(System.Windows.Forms.Keys keyData) { inTabKey = keyData == Keys.Tab; return base.ProcessDialogKey( keyData); } } [VB.NET] 'the handler that checks the derived grid's field inTabKey Private Sub dataGrid1_Validated(sender As Object, e As System.EventArgs) If Not Me.dataGrid1.inTabKey Then Console.WriteLine("Clicked off grid") Else Me.dataGrid1.inTabKey = False End If End Sub 'dataGrid1_Validated 'derived class Public Class MyDataGrid Inherits DataGrid Public inTabKey As Boolean = False Protected Overrides Function ProcessDialogKey(keyData As System.Windows.Forms.Keys) As Boolean nTabKey = keyData = Keys.Tab Return MyBase.ProcessDialogKey(keyData) End Function 'ProcessDialogKey End Class 'MyDataGrid

5.77 How can I get a CheckBox column in a DataGrid to react to the first click? When you first click into a checkbox column, the checked state of the cell does not change. One way you can make the checked state change on the first click is to handle the grid's MouseUp event, and change the check value there. [VB.Net} Private myCheckBoxCol As Integer = 9 'my checkbox column

Private Sub DataGrid2_MouseUp(ByVal sender As Object, ByVal e As MouseEventArgs) Handles DataGrid2.MouseUp Dim hti As DataGrid.HitTestInfo = Me.dataGrid2.HitTest(e.X, e.Y) Try If hti.Type = DataGrid.HitTestType.Cell AndAlso hti.Column = myCheckBoxCol Then Me.dataGrid2(hti.Row, hti.Column) = Not CBool(Me.dataGrid2(hti.Row, hti.Column)) End If Catch ex As Exception MessageBox.Show(ex.ToString()) End Try End Sub 'dataGrid2_MouseUp [C#] private int myCheckBoxCol = 9; //my checkbox column private void dataGrid2_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e) { DataGrid.HitTestInfo hti = this.dataGrid2.HitTest(e.X, e.Y); try { if( hti.Type == DataGrid.HitTestType.Cell && hti.Column == myCheckBoxCol) { this.dataGrid2[hti.Row, hti.Column] = ! (bool) this.dataGrid2[hti.Row, hti.Column]; } } catch(Exception ex) { MessageBox.Show(ex.ToString()); } }

5.78 How can I use events to restrict key input to grid cells? If you make sure your DataGrid is using a DataGridTableStyle, then you can access the TextBox through the GridColumnStyles collection and hook the event there. Here is some code.... [C#] //in formload this.dataGrid2.DataSource = this.dataSet11.Customers; // set the data source //make sure grid has a tablestyle DataGridTableStyle ts = new DataGridTableStyle(); ts.MappingName = this.dataSet11.Customers.TableName; this.dataGrid2.TableStyles.Add(ts); //now we can wire up wire up events for columns 1 and 4 .... DataGridTextBoxColumn tbc = (DataGridTextBoxColumn)ts.GridColumnStyles[0]; tbc.TextBox.KeyPress += new KeyPressEventHandler(CellKeyPress); tbc = (DataGridTextBoxColumn)ts.GridColumnStyles[3];

tbc.TextBox.KeyPress += new KeyPressEventHandler(CellKeyPress);..... //the handler private void CellKeyPress(object sender, KeyPressEventArgs e) { //don't allow 1's if(e.KeyChar == '1') e.Handled = true; } [VB.NET] 'in formload Me.dataGrid2.DataSource = Me.dataSet11.Customers ' set the data source 'make sure grid has a tablestyle Dim ts As New DataGridTableStyle() ts.MappingName = Me.dataSet11.Customers.TableName Me.dataGrid2.TableStyles.Add(ts) 'now we can wire up wire up events for columns 1 and 4 .... Dim tbc as DataGridTextBoxColumn = CType(ts.GridColumnStyles(0), DataGridTextBoxColumn) AddHandler tbc.TextBox.KeyPress, AddressOf CellKeyPress tbc = CType(ts.GridColumnStyles(3), DataGridTextBoxColumn) AddHandler tbc.TextBox.KeyPress, AddressOf CellKeyPress ..... 'the handler Private Sub CellKeyPress(sender As Object, e As KeyPressEventArgs) 'don't allow 1's If e.KeyChar = "1"c Then e.Handled = True End If End Sub 'CellKeyPress

5.79 How can I format columns in my DataGrid without explicitly adding DataGridColumnStyles? To format column output, you do not have to explicitly add DataGridColumns for each column provided you do add a DataGridTableStyle to your DataGrid prior to setting the DataSource property. When you set the DataSource property with a DataGrid that has a tablestyle with an empty columnstyle collection, the framework generates default columnstyle objects for each column in the datasource. You can then access these columnstyles directly and set properties in them such as Format, HeaderText and Width. Download working samples here (VB.NET, C#). Dim dataTableName As String = "theTable" 'add a tablestyle to the grid so there will be custom columnstyles available ' after the datasource has been set....

Dim ts As New DataGridTableStyle() ts.MappingName = dataTableName Me.dataGrid1.TableStyles.Add(ts) Me.dataGrid1.DataSource = GetTheTable(dataTableName) 'now default customcolumnstyles have been created, so we can use them to set properties Dim dgtbc As DataGridTextBoxColumn 'format the int dgtbc = dataGrid1.TableStyles(0).GridColumnStyles(0) If Not (dgtbc Is Nothing) Then dgtbc.Format = "n0" End If 'format the double dgtbc = dataGrid1.TableStyles(0).GridColumnStyles(1) ' If Not (dgtbc Is Nothing) Then dgtbc.Format = "f3" ' 0r "#.000"; End If 'format the double as currency dgtbc = dataGrid1.TableStyles(0).GridColumnStyles(2) ' If Not (dgtbc Is Nothing) Then dgtbc.Format = "c4" End If 'format the date dgtbc = dataGrid1.TableStyles(0).GridColumnStyles(3) ' If Not (dgtbc Is Nothing) Then dgtbc.Format = "d" ' or "g" or "u" or whatever format you want to see dgtbc.Width = 100 'size it End If

5.80 How can I auto-adjust keyboard input? For example, make typing 12312002 be taken as a valid date, 12/31/2002. One possible solution is that as you move off the cell, you get the typed value, and modify it before it is passed onto the DataGrid for its standard processing. One problem is that there are several ways to leave the cell, and you would want to handle each (depending upon your needs). You can leave the cell by clicking on another cell, by tabbing off the cell, by arrowing off the cell, and by pressing Enter on an edited cell. Another problem is that you want to catch these actions early enough to make a change in the typed value that can be passed onto the DataGrid itself. To catch the typed entries before they are handed off to the DataGrid, you have to gain access to the DataGridTextBoxColumn's embedded TextBox. This requires that your DataGrid either has explicitly had these columns styles added to it, or that you minimally add a DataGridTableStyle to your DataGrid so the Framework generates ColumnsStyle objects for you.

To solve these problems, you can derive the dataGrid and override OnMouseDown, OnProcessDialogKey, and OnProcessPreviewKey. The last override will handle both the arrowing off the cell and pressing Enter on an edited cell. You can download a sample (C#, VB.NET) that implements this technique. 5.81 Can I display the rows in my datagrid in a free-form layout using textboxes on a panel? Here is a VB and C# sample showing how you might do this. The idea is to add textboxes and labels to a panel with whatever layout you want to use. Then use DataBindings.Add calls to bind the textboxes to columns in your grid (actually in your grid's datasource). Then, since the grid and the textboxes share the same BindingContext, you can move the BindingManagerBase.Position property to scroll through the rows in your grid, and the textboxes will remain in sync. You can hide the grid just to show the textboxes if you do not want the grid to have a presence. Also, any edits in the textboxes will appear in the DataGrid and vice versa. 5.82 How can I tell whether a scrollbar is visible in my DataGrid is visible? If you are using a derived DataGrid, then you can check the Visible property on the protected VertScrollBar property of DataGrid. So, you could check Me.VertScrollBar.Visible from within your derived DataGrid. To check it without access to the protected scrollbar properties is a little more work, but possible. One technique is to loop through the Controls property of the DataGrid looking for the scrollbar, and then checking its visible property at that time. [C#] //sample usage bool vSrollBarVisible = this.IsScrollBarVisible(this.dataGrid1); ..... private bool IsScrollBarVisible(Control aControl) { foreach(Control c in aControl.Controls) { if (c.GetType().Equals(typeof(VScrollBar))) { return c.Visible; } } return false; } [VB.NET]

'sample usage Dim vScrollBarVisible = Me.IsScrollBarVisible(Me.DataGrid1) ...... Private Function IsScrollBarVisible(ByVal aControl As Control) As Boolean Dim c As Control For Each c In aControl.Controls If c.GetType() Is GetType(VScrollBar) Then Return c.Visible End If Next Return False End Function

5.83 How do I autosize the columns in my DataGrid so they always fill the the grid's client area? If you add a DataGridTableStyle to your Datagrid, then you can use the ColWidth property of the GridColumnStyles to set the width of each column. To dynamically set these widths as the grid is resized, you can handle the SizeChanged event of the the DataGrid. In your handler, you can compute the width of each column by dividing the client width minus the width of the row header column by the number of columns. Now there are a couple of technical points. You have to adjust for a possible vertical scrollbar. And, you have to adjust things for possible integer rounding in the calculations. To handle this last problem, the attached samples (both VB and C#) apply the single computed width to all but the last column. And this last column is just given all the space left. This means the last column may differ in width from the other columns by a couple of pixels. 5.84 How can I prevent all the cells in my DataGrid from being edited without deriving GridColumnStyle? One solution is to remove all the editing controls from the DataGrid.Controls collection. Without these controls, the grid contents cannot be edited. There is a technical problem that requires a little care. You do not want to delete all the controls in DataGrid.Controls as you would lose your scrollbars. The code below deletes all controls except the scrollbars. Alternatively, you could also loop through the controls and only delete the TextBox. [C#] ArrayList al = new ArrayList(); foreach(Control c in this.dataGrid1.Controls) { if(c.GetType() == typeof(VScrollBar) || c.GetType() == typeof(HScrollBar)) { al.Add(c); } }

this.dataGrid1.Controls.Clear(); this.dataGrid1.Controls.AddRange((Control[]) al.ToArray(typeof(Control))); [VB.NET] Dim al As New ArrayList() Dim c As Control For Each c In Me.dataGrid1.Controls If c.GetType() = GetType(VScrollBar) Or c.GetType() = GetType(HScrollBar) Then al.Add(c) End If Next c Me.dataGrid1.Controls.Clear() Me.dataGrid1.Controls.AddRange(CType(al.ToArray(GetType(Control)), Control()))

5.85 How can I prevent the plus-minus icon that appears next to the row header when I have a datagrid displayed bound to a datasource that has a relation defined? Set the DataGrid.AllowNavigation property to false. 5.86 How can I display master-details-details in three separate grids? Here is a sample (both VB and C#) that illustrates how to have a parent table which has a related child table, which also has a related grandchild table. Below are some code snippets. The trick is to always make the main parent table be the DataSource for all the grid, and then set the DataMember to be the relation name where teh relation starts at the parent table. This means the DisplayMember for the Child table is "ParentToChild", the name of that relation. And, the DisplayMember for the grandchild grid is "ParentToChild.ChildToGrandChild" which defines the relation starting at the parent grid through the child grid. Dim dSet As New DataSet() 'get the tables Dim parentTable As DataTable = GetParentTable() Dim childTable As DataTable = GetChildTable() Dim grandChildTable As DataTable = GetGrandChildTable() dSet.Tables.AddRange(New DataTable() {parentTable, childTable, grandChildTable}) 'setup the relations Dim parentColumn As DataColumn = parentTable.Columns("parentID") Dim childColumn As DataColumn = childTable.Columns("ParentID") dSet.Relations.Add("ParentToChild", parentColumn, childColumn) parentColumn = childTable.Columns("childID")

childColumn = grandChildTable.Columns("ChildID") dSet.Relations.Add("ChildToGrandChild", parentColumn, childColumn) 'set the grids Me.dataGrid1.DataSource = parentTable Me.dataGrid2.DataSource = parentTable Me.dataGrid2.DataMember = "ParentToChild" Me.dataGrid3.DataSource = parentTable Me.dataGrid3.DataMember = "ParentToChild.ChildToGrandChild" Me.dataGrid1.AllowNavigation = False Me.dataGrid2.AllowNavigation = False Me.dataGrid3.AllowNavigation = False

5.87 In my datagrid, if I press tab to enter a column using a derived columnstyle, the column does not receive focus. Why? This behavior can be seen when you embedded a control like a textbox or combobox in your derived GridColumnStyle. If you press the tabkey slowly, you may see the cell get focus on the downkey and the cell lose focus on the upkey. One way to avoid this problem is to subclass the embedded control, and override its WndProc method, ignoring the KeyUp. [C#] public class MyCombo : ComboBox { private const int WM_KEYUP = 0x101; protected override void WndProc(ref System.Windows.Forms.Message m) { if(m.Msg == WM_KEYUP) { return; //ignore the keyup } base.WndProc(ref m); } } [VB.NET] Public Class MyTextBox Inherits TextBox Private WM_KEYUP As Integer = &H101 Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message) If m.Msg = WM_KEYUP Then Return 'ignore the keyup End If MyBase.WndProc(m) End Sub 'WndProc End Class 'MyTextBox

5.88 How can I detect when a cell starts being edited, not when it becomes current? You can use the CurrentCellChanged event to detect when the currentcell changes position. But this event will not allow you to catch the start of a cell being edited. One way you can do this is to catch the TextChanged event in the embedded TextBox within the cell. If the text changes, then it might be the beginning of a cell edit provided the text in the TextBox differs from the stored value from the DataSource. The reason you need to check for a different value between the grid DataSource and the TextBox contents is that the TextChanged event is fired initially when the TextBox is initialized when the cell becomes current and moves the value from the grid ataSource to the TextBox. You also have to ignore subsequent hits of TextChanged as the same cell continues to be edited. Here is both a VB and C# sample that implements this strategy to flagged current cell start editing. 5.89 How can I have a column of bitmaps in a DataGrid? You can derive a custom columnstyle and override its Paint method to draw the image. Here are both C# and VB.Net samples. The Paint override offers three ways to draw the bitmap in the cell; size it to fit the cell bounds, size it proportionally to fit the cell bounds, and draw it with it's original size (clipping the bitmap to fit in the cell bounds). In the sample, the DataGrid is anchored on all four sides, so as you size the form, the DataGrid sizes as well. The dataGrid1_Resized event is handled to dynamically change the PreferredRowHeight and PreferredColumnWidth so the cells occupy all of the datagrid's client area. So, as you size the form, the grid cells size also so you can easily see the bitmaps. 5.90 How can I add my custom columnstyles to the designer so I can use them in my DataGrid at design time? To use custom columnstyles in the designer, you need to do three things. 1) Derive a CustomColumnStyle to implement the functionality you want. 2) Derive a DataGridTableStyle class and add a new GridColumnStyles property that uses this derived CollectionEditor. This GridColumnStyle hides the baseclass member. 3) Derive a DataGrid and add a new TableStyles collection that uses your derived tablestyle. Both steps 2 and 3 will require you to derive a CollectionEditor and override CreateNewItemTypes to use the derived classes from each step in the designer. Here is a sample project showing how you might do these things. 5.91 How can I programatically add and remove columns in my DataGrid without modifying the DataTable datasource?

You can control the columns displayed in the DataGrid through the DataGrid.TableStyle[0].GridColumnStyles collection. To do so, create a DataGridTableStyle and set its MappingName property to point to the name of your DataTable which is the DataSource for the DataGrid. Next, add this DataGridTableStyle to the DataGrid.TableStyles property. Finally, set the DataGrid.DataSource property to the DataTable. Doing things in this order, guarantees that the DataGridTableStyle.GridColumnStyles collection will be fully populated showing all the DataTable columns in the the DataGrid. Then to add and remove columns from the DataGrid, you only have to add and remove DataGridColumnStyle objects from this DataGrid.TableStyle[0].GridColumnStyles collection. Removing them is straight-forward through a Remove method call. But inserting them requires more work as there is no InsertAt method defined for this collection. To handle this problem, you can create a new array of DataGridColumnStyles, and populate this array in the necessary order to reflect the DataGrid with an inserted column. Then you can clear the old collection, and create a new collection with this new array. You really are not creating all new DataGridColumnStyle objects, but are simply reordering the existing ones in a new collection. Here is a sample project containing both C# and VB.NET code showing how you might do this.

6. Windows Forms Docking WinForms FAQ Home 6.1 How can I make a control occupy all the client area of a form? 6.2 How can I make my control automatically grow when the parent form is sized? 6.3 What is the difference between a form's Anchor property and a form's Dock property?

6.1 How can I make a control occupy all the client area of a form? private void Form1_Load(object sender, System.EventArgs e) { Bitmap newBmp = new Bitmap(100, 100); Graphics g = Graphics.FromImage(newBmp); g.FillRectangle(new SolidBrush(Color.Red), 0, 0, 33, 100); g.FillRectangle(new SolidBrush(Color.White), 34, 0, 33, 100); g.FillRectangle(new SolidBrush(Color.Blue), 68, 0, 33, 100); pictureBox1.Image = newBmp; //pictureBox1 was dropped on the form }

6.2 How can I make my control automatically grow when the parent form is sized? Change the control's Anchor property so that it is anchored on all 4 sides.

Please note that you can only have 1 control per form anchored in this manner (all 4 sides). And other controls on the form should be anchored by their sides that are not adjacent to special control anchored on all 4 sides. 6.3 What is the difference between a form's Anchor property and a form's Dock property? G. G. Arun Ganesh discusses these properties in Working with Anchoring and Docking Properties in C# on C# Corner.

7. Windows Forms Keyboard Handling WinForms FAQ Home 7.1 How can I prevent a control from getting a particular keystroke? 7.2 How can I tell if an ALT, Shift or CTL key is pressed without catching an event? 7.3 How do I check the state of the virtual keys, Caps lock for example? 7.4 How can I simulate keyboard input in my application? 7.5 How can I catch keyboard messages on a application-wide basis? 7.6 How can I listen for certain keys at the Form level irrespective of which Control has the focus?

7.1 How can I prevent a control from getting a particular keystroke? You can handle the control's KeyPress event and indicate the key has been handled. Below is code that prevents a TextBox from getting an 'A' and the return key. private void textBox1_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e) { if(e.KeyChar == (char)13 || e.KeyChar == 'A') e.Handled = true; }

7.2 How can I tell if an ALT, Shift or CTL key is pressed without catching an event? Use the static property Control.ModifierKeys. Console.WriteLine(Control.ModifierKeys); if( (Control.ModifierKeys & Keys.Shift) != 0) Console.WriteLine("the shift key is down"); if( (Control.ModifierKeys & Keys.Alt) != 0) Console.WriteLine("the alt key is down"); if( (Control.ModifierKeys & Keys.Control) != 0) Console.WriteLine("the control key is down");

7.3 How do I check the state of the virtual keys, Caps lock for example? If the Control.ModifierKeys doesn't address your issue, then use Platform Invoke and call GetKeyState directly. Declare this class first: [ ComVisibleAttribute(false),

SuppressUnmanagedCodeSecurityAttribute() ] internal class NativeMethods { [DllImport("user32.dll", CharSet=CharSet.Auto, ExactSpelling=true, CallingConvention=CallingConvention.Winapi)] public static extern short GetKeyState(int keyCode); public static int HIWORD(int n) { return ((n >> 16) & 0xffff/*=~0x0000*/); } public static int LOWORD(int n) { return (n & 0xffff/*=~0x0000*/); } } Then when you want to check if Caps is down or ON, call: short state = NativeMethods.GetKeyState(0x14 /*VK_CAPTIAL*/); bool capsKeyDown = NativeMethods.HIWORD(state); bool capsKeyON = NativeMethods.LOWORD(state);

7.4 How can I simulate keyboard input in my application? This can be done through the SendKeys class in the System.Windows.Forms namespace. Check it out in the MS help documentation. 7.5 How can I catch keyboard messages on a application-wide basis? You can implement the IMessageFilter interface in your main form. This amounts to adding an override for PreFilterMessage, and looking for the particular message you need to catch. Here are code snippets that catch an escape key on a keydown. You can download a sample project(C#, VB). In the sample, there are two forms, with several controls. You'll notice that no matter what form or control has input focus, the escape key is caught in the PreFilterMessage override. [C#] public class MyMainForm : System.Windows.Forms.Form, IMessageFilter { const int WM_KEYDOWN = 0x100; const int WM_KEYUP = 0x101; public bool PreFilterMessage(ref Message m) { Keys keyCode = (Keys)(int)m.WParam & Keys.KeyCode; if(m.Msg == WM_KEYDOWN && keyCode == Keys.Escape) { Console.WriteLine("Ignoring Escape...");

return true; } return false; } .... .... .... private void MyMainForm_Load(object sender, System.EventArgs e) { Application.AddMessageFilter(this); } } [VB.NET] Public Class MyMainForm Inherits System.Windows.Forms.Form Implements IMessageFilter Private WM_KEYDOWN As Integer = &H100 Private WM_KEYUP As Integer = &H101 Public Function PreFilterMessage(ByRef m As Message) As Boolean Dim keyCode As Keys = CType(CInt(m.WParam), Keys) And Keys.KeyCode If m.Msg = WM_KEYDOWN And keyCode = Keys.Escape Then Console.WriteLine("Ignoring Escape...") Return True End If Return False End Function 'PreFilterMessage .... .... .... Private Sub MyMainForm_Load(sender As Object, e As System.EventArgs) Application.AddMessageFilter(Me) End Sub 'MyMainForm_Load End Class 'MyMainForm

7.6 How can I listen for certain keys at the Form level irrespective of which Control has the focus? When the Form.KeyPreview property is set to true, the Form's KeyPress, KeyDown and KeyUp events will be fired even before the Control with the focus' corresponding events. You may choose to forward these message to the Control after processing them in the Form's event handlers (this happens by default) or set the e.Handled property to true (in the event argument) to prevent the message from being sent to the Control with focus.

8. Windows Forms Layout WinForms FAQ Home 8.1 How can I programmatically set the initial position of a Form so that it is displayed properly after calling ShowDialog()? 8.2 How do I create a custom layout engine? 8.3 I have a form with several controls on it. As I size the form, the controls are being resized continuously. Is it possible to postpone changing the controls position until the resizing is complete?

8.1 How can I programmatically set the initial position of a Form so that it is displayed properly after calling ShowDialog()? In addition to setting the Location property of the form, make sure you also set the StartPosition property of the form to FormStartPosition.Manual. 8.2 How do I create a custom layout engine? Chris Anderson discusses how to implement a custom layout engine and gives sample code in an article on gotnetdot.com. 8.3 I have a form with several controls on it. As I size the form, the controls are being resized continuously. Is it possible to postpone changing the controls position until the resizing is complete? Shawn Burke suggested this solution in a response on the microsoft.public.dotnet.framework.windowsforms newsgroup. The idea is to do the painting on Idle. This means you simple invalidate when being sized and then do your full paint when the size completes. Here is the code that you would add to your form. bool idleHooked = false; protected override void OnResize(EventArgs e) { if (!idleHooked) { Application.Idle += new EventHandler(OnIdle); } } private void OnIdle(object s, EventArgs e) { Invalidate(); PerformLayout(); if (idleHooked) { Application.Idle -= new EventHandler(OnIdle); } }

9. Windows Forms Licensing 9.1 How does .NET support licensing? 9.2 How do I find the file version of an EXE?

WinForms FAQ Home

9.1 How does .NET support licensing? Windows Forms Programming in C# by Chris Sells ISBN: 0321116208 An excellent book for learning Windows Forms using C# for both Beginners and experienced Programmers. The presentation is nice and crisp the chapter size is usually around 50 pages, presenting all the necessary details and at the same time maintaining the reader's interest in the topic being presented. 9.2 How do I find the file version of an EXE? Use System.Diagnostics.FileVersionInfo.

10. Windows Forms Menus WinForms FAQ Home 10.1 How do I clone menus, insert menus, use popup menus, etc. 10.2 How do I add a context menu to a control? 10.3 When using the ContextMenu on multiple Controls, how do I know on which Control the right click was performed? 10.4 How do menu shortcuts work? 10.5 How do I prevent the context menu from showing up on certain keyboard keys (like Keys.Apps)? 10.6 How do I know when a menu is closed/started so that I can refresh my status bar? 10.7 How do I make the context menu appear only when clicked at certain portions of the Control? 10.8 How to add a Separator in a menu? 10.9 How can I add an icon to my menu items? 10.10 How do I cancel a context menu programatically? 10.11 Is there an easy way to create the "Window" menu that shows the list of child forms open? 10.12 Is there an Idle event which will get fired from which I could update my menu item's enabled state? 10.13 How can I work around my menu shortcuts from showing up incorrectly when I use Ctrl+Number? 10.14 How do I control the position of the context menu when it is invoked via the keyboard?

10.15 How do I disable the default context menu of a textbox? 10.16 How can I make the context menu to close after a set time interval? 10.17 How can I enable the mnemonics (underline) to show when my application is launched?

10.1 How do I clone menus, insert menus, use popup menus, etc. Check out G. G. Arun Ganesh's article Working with Menus in C# on C# Corner. 10.2 How do I add a context menu to a control? The frame will manage a context menu for you if you set the control's ContextMenu property. Here is some code adding a context menu to a Label. //Add Menus in your form's constructor... this.pictureContextMenu = new ContextMenu(); this.pictureContextMenu.MenuItems.Add("&Color", new EventHandler(Color_Clicked)); this.pictureContextMenu.MenuItems.Add("&Font", new EventHandler(Font_Clicked)); label1.ContextMenu = this.pictureContextMenu; // // TODO: Add any constructor code after InitializeComponent call // } private void Font_Clicked(object sender, System.EventArgs e) {...} private void Color_Clicked(object sender, System.EventArgs e) { ... }

10.3 When using the ContextMenu on multiple Controls, how do I know on which Control the right click was performed? The ContextMenu.SourceControl property will specify the latest Control on which the context menu was shown. 10.4 How do menu shortcuts work? The focused control's ProcessCmdKey will be called first. 1) The Control class implementation of this method will check if there is a ContextMenu associated with the Control and if so let the context menu handle the shortcut. 2) If not handled, the ProcessCmdKey of the parent will be called recursively until the Form is

reached. 3) When the Form is reached it's MainMenu will be requested to handle the key. You can override ProcessCmdKey of any Control and interrupt the normal processing. Note that you can also override the ProcessCmdKey method is the MainMenu and ContextMenu classes (this is not documented) to override default processing. 10.5 How do I prevent the context menu from showing up on certain keyboard keys (like Keys.Apps)? Override WndProc in your Control and do the following. You should then listen to keyup and show the context menu yourself. [C#] protected override void WndProc(ref Message m) { if(m.Msg == 0x7b /*WM_CONTEXTMENU*/ ) { return; } if(m.Msg == 0x101 /*WM_KEYUP*/) { Keys keys = (Keys)m.WParam.ToInt32(); // Prevent this key from being processed. if(keys == Keys.Apps) return; } base.WndProc(ref m); } [VB.Net] Protected Overrides Sub WndProc(ByRef m As Message) If m.Msg = 0x7b Then 'WM_CONTEXTMENU Return End If If m.Msg = 0x101 Then 'WM_KEYUP Dim keys As Keys = CType(m.WParam.ToInt32(), Keys) ' Prevent this key from being processed. If keys = Keys.Apps Then Return End If End If MyBase.WndProc( m) End Sub

10.6 How do I know when a menu is closed/started so that I can refresh my status bar? There are MenuComplete and MenuStart events in the Form that will inform you of the corresponding menu events in the Form. 10.7 How do I make the context menu appear only when clicked at certain portions of the Control? You can listen to the Popup event, determine where the mouse was clicked and selectively make the menu items visible in the menu as follows: // In C# private void contextMenu1_Popup(object sender, System.EventArgs e) { // Get current mouse click position in the control (assuming pictureBox1 is the control): Point ptClick = this.pictureBox1.PointToClient(Control.MousePosition); // Get the rectangle where you want to show the context menu. Rectangle preferredClickRect = new Rectangle(0, 0, 50, 50); if(preferredClickRect.Contains(ptClick)) { // Show all the menu items so that the menu will appear foreach(MenuItem item in this.contextMenu1.MenuItems) item.Visible = true; } else { // Hide all the menu items so that the menu will not appear foreach(MenuItem item in this.contextMenu1.MenuItems) item.Visible = false; } } ' In VB.Net Private Sub contextMenu1_Popup(ByVal sender As Object, ByVal e As System.EventArgs) ' Get current mouse click position in the control (assuming pictureBox1 is the control): Dim ptClick As Point = Me.pictureBox1.PointToClient(Control.MousePosition) ' Get the rectangle where you want to show the context menu. Dim preferredClickRect As Rectangle = New Rectangle(0,0,50,50) If preferredClickRect.Contains(ptClick) Then ' Show all the menu items so that the menu will appear Dim item As MenuItem For Each item In Me.contextMenu1.MenuItems item.Visible = True Next Else

' Hide all the menu items so that the menu will not appear Dim item As MenuItem For Each item In Me.contextMenu1.MenuItems item.Visible = False Next End If End Sub

10.8 How to add a Separator in a menu? contextmenu.MenuItems.Add("-");

10.9 How can I add an icon to my menu items? You need to implement an owner drawn menu to add icons to it. Take a look at the sample on .netPlus. 10.10 How do I cancel a context menu programatically? First keep track of which control is showing the ContextMenu by listening to the menu's Popup event and querying for the SourceControl. Then when you are ready to cancel the popup, do as follows: [C#] [DllImport("user32.dll", CharSet=CharSet.Auto)] extern internal static IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); private void Timer_Tick(object sender, EventArgs e) { if(menuSourceControl != null) SendMessage(menuSourceControl.Handle, 0x001F/*WM_CANCELMODE*/, IntPtr.Zero, IntPtr.Zero); } [VB.Net] _ extern internal static IntPtr SendMessage(IntPtr hWnd, Integer msg, IntPtr wParam, IntPtr lParam) Private Sub Timer_Tick(ByVal sender As Object, ByVal e As EventArgs) If Not menuSourceControl Is Nothing Then SendMessage(menuSourceControl.Handle, 0x001F, IntPtr.Zero, IntPtr.Zero) End If End Sub

10.11 Is there an easy way to create the "Window" menu that shows the list of child forms open? Create a "Window" menu in you mdi parent form's menu and then drop another MenuItem into it, setting it's MdiList property to true. It will then expand to show all the available mdi children during runtime. 10.12 Is there an Idle event which will get fired from which I could update my menu item's enabled state? This used to be a very useful feature in MFC. You can do something similar with the System.Windows.Forms.Application.Idle event which will get fired when the app becomes Idle. Where you could parse through all the menu items and update their states. 10.13 How can I work around my menu shortcuts from showing up incorrectly when I use Ctrl+Number? When you assign Ctrl1, Ctrl2 etc as shortcuts for menuitems they show up as Ctrl+D1, Ctrl+D2. This can be worked around by creating and adding the menuitem through code as demonstrated below: [C#] //Create the menuitem MenuItem mymenuItem = new MenuItem(); //ShortCut mymenuItem.Shortcut = System.Windows.Forms.Shortcut.Ctrl1; //Add Event Handler for the menuitem mymenuItem.Click +=new EventHandler(this.mymenuItem_Click); //ShortCut Text to be displayed mymenuItem.Text = "My MenuItem" +"\t"+ "Ctrl+1"; //hide shortcut mymenuItem.ShowShortcut = false; //Add it to the bottom of the first menu this.mainMenu1.MenuItems[0].MenuItems.Add(mymenuItem); [VB.NET] 'Create the menuitem Dim mymenuItem As MenuItem = New MenuItem() 'ShortCut mymenuItem.Shortcut = System.Windows.Forms.Shortcut.Ctrl1 'Add Event Handler for the menuitem mymenuItem.Click +=New EventHandler(Me.mymenuItem_Click) 'ShortCut Text to be displayed mymenuItem.Text = "My MenuItem" +"\t"+ "Ctrl+1" 'hide shortcut mymenuItem.ShowShortcut = False

'Add it to the bottom of the first menu Me.mainMenu1.MenuItems(0).MenuItems.Add(mymenuItem)

10.14 How do I control the position of the context menu when it is invoked via the keyboard? Normally, the context menu will be shown at the center of the control when the keyboard is used to invoke the context menu of a control (Shift+F10, for example). You can customize the location as follows. 1. Override WndProc in the grid and check if the Message.Msg is WM_CONTEXTMENU (0x007b) 2. If so, check if the Message.LParam is -1, which means this was due to a keyboard message as opposed to the user right-clicking the mouse. 3. Now, call the associated ContextMenu's Show method explicitly at the selected row position. 4. Make sure NOT to call the base class after showing the menu explicitly. protected override void WndProc(ref Message m) { if(m.Msg == 0x007B /*WM_CONTEXTMENU*/) { // LParam == -1 means that this is due to a keyboard message if((int)m.LParam == -1) { this.contextMenu.Show(); return; } } } [VB.Net] Protected Overrides Sub WndProc(ByRef m As Message) If m.Msg = 0x007B Then 'WM_CONTEXTMENU ' LParam == -1 means that this is due to a keyboard message If (CType(m.LParam,Integer)) = -1 Then Me.contextMenu.Show() Return End If End If End Sub

10.15 How do I disable the default context menu of a textbox? To prevent the default context menu of a TextBox from showing up, assign a empty context menu as shown below: [C#] textBox1.ContextMenu = new ContextMenu(); [VB.Net] textBox1.ContextMenu = New ContextMenu()

10.16 How can I make the context menu to close after a set time interval? To automatically close the context menu after a set time interval, you can use a Timer and send a ESC key stroke after the desired time interval as shown: [C#] private void timer1_Tick(object sender, System.EventArgs e) { SendKeys.Send("{ESC}"); timer1.Stop(); } private void contextMenu1_Popup(object sender, System.EventArgs e) { //set interval to 5 seconds timer1.Interval = 5000; timer1.Start(); } [VB.Net] Private Sub timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) SendKeys.Send("{ESC}") timer1.Stop() End Sub Private Sub contextMenu1_Popup(ByVal sender As Object, ByVal e As System.EventArgs) 'set interval to 5 seconds timer1.Interval = 5000 timer1.Start() End Sub

10.17 How can I enable the mnemonics (underline) to show when my application is launched? Usually the underline appears only after you press the ALT Key, but you can enable it by changing the Operating System Settings. On Windows XP, Right Click Desktop to bring up the Display Properties Dialog and then choose Appearance tab and then the Effects Button and uncheck the checkbox "Hide Underlined letters for keyboard navigation until I press the ALT Key".

11. Windows Forms Mouse Handling WinForms FAQ Home 11.1 How can I resize a borderless form with a rubber-band effect? 11.2 How do I get a mouse cursor position in my control's client coordinates? 11.3 How can I catch the mouse being over a control? 11.4 How do I set the mouse cursor position? 11.5 How can I reset the OnMouseHover timer so that it will fire again without the mouse having to leave the client area of a control? 11.6 How can I drag a window if it doesn't have a title bar or border? 11.7 How can I determine if a mouse button is pressed and moving over my form? 11.8 Why am I not receiving MouseLeave messages in a Control in my Form?

11.1 How can I resize a borderless form with a rubber-band effect? Simon Bond has sample code at C# Corner that implements this sizing technique. Zhanbo Sun suggests a modification that handles a problem he spotted with the original code. 11.2 How do I get a mouse cursor position in my control's client coordinates? Use the Position property of the Cursor class found in the System.Windows.Forms namespace. Here is code that will flag whether the mouse is over button1. Point ptCursor = Cursor.Position; ptCursor = PointToClient(ptCursor); if( button1.Bounds.Contains(ptCursor) ) { //mouse over button1 //.... } else { //mouse not over button1 //.... }

11.3 How can I catch the mouse being over a control? Add a handler for the control's MouseMove event. This will be hit as the mouse moves over the control's Bounds rectangle.

11.4 How do I set the mouse cursor position? The Cursor.Position property is a static property with both get and set methods available. Remember that the Point object used by the property is in screen coordinates. Here is code that move the mouse position down 20 points. Point pt = Cursor.Position; pt.Y += 20; Cursor.Position = pt;

11.5 How can I reset the OnMouseHover timer so that it will fire again without the mouse having to leave the client area of a control? In the microsoft.public.dotnet.framework.windowsforms newsgroup, T. H. in den Bosch posted the following suggestion with a potential warning: I found out that calling Control.ResetMouseEventArgs() does the trick. This is an undocumented internal method, so I don't know if calling it has adverse side effects. 11.6 How can I drag a window if it doesn't have a title bar or border? You can drag such a window by using Win32 APIs to switch the mouse hit to WM_NCLBUTTONDOWN. The code below will allow you to drag by mousing down anywhere in the form's clientarea as long as you don't hit a child control on the form. using System.Runtime.InteropServices; ............ public const int WM_NCLBUTTONDOWN = 0xA1; public const int HTCAPTION = 0x2; [DllImportAttribute ("user32.dll")] public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam); [DllImportAttribute ("user32.dll")] public static extern bool ReleaseCapture(); private void Form2_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) { ReleaseCapture(); SendMessage(Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0); } Here is a solution posed by Jacob Grass (Abiliti Solutions) that does not rely on InteropServices. Private blnMoving As Boolean = False Private MouseDownX As Integer Private MouseDownY As Integer Private Sub Form1_MouseDown(ByVal sender As Object, _ ByVal e As System.Windows.Forms.MouseEventArgs) Handles Form1.MouseDown If e.Button = MouseButtons.Left Then blnMoving = True

MouseDownX = e.X MouseDownY = e.Y End If End Sub Private Sub Form1_MouseUp(ByVal sender As Object, _ ByVal e As System.Windows.Forms.MouseEventArgs) Handles Form1.MouseUp If e.Button = MouseButtons.Left Then blnMoving = False End If End Sub Private Sub Form1_MouseMove(ByVal sender As Object, _ ByVal e As System.Windows.Forms.MouseEventArgs) Handles Form1.MouseMove If blnMoving Then Dim temp As Point = New Point() temp.X = Form1.Location.X + (e.X - MouseDownX) temp.Y = Form1.Location.Y + (e.Y - MouseDownY) Form1.Location = temp End If End Sub

11.7 How can I determine if a mouse button is pressed and moving over my form? Catch the form's MouseMove event which is called when the mouse moves over the form. To determine if a button is pressed during the move, check the event arg's Button property. The code below draw's a line on the form as the mouse moves over it. The line is red when the left button is pressed, white when the right button is pressed and blue when no button is pressed. private Point _point = new Point(-1, -1); .... private void InitializeComponent() { // // Form1 // this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); this.ClientSize = new System.Drawing.Size(292, 273); this.Name = "Form1"; this.Text = "Form1"; this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseMove); } .... private void Form1_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e) { if(_point.X == -1)

_point = new Point(e.X, e.Y); Color c; if(e.Button == MouseButtons.Left) c = Color.Red; else if(e.Button == MouseButtons.Right) c = Color.White; else if(e.Button == MouseButtons.None) c = Color.Blue; else c = this.BackColor; Point p = new Point(e.X,e.Y); Graphics g = Graphics.FromHwnd(this.Handle); Pen pen = new Pen(c,1); g.DrawLine(pen,_point,p); _point = p; pen.Dispose(); }

11.8 Why am I not receiving MouseLeave messages in a Control in my Form? This usually happens when a Control doesn't know that the mouse has left its bounds following a mouse enter. This results in the Control not throwing a MouseLeave event for subsequent mouse moves over it. This is possible if you performed some operation when the mouse was within a Control that made it lose its mouse capture, for example, opening a top-level window over the Control such that the mouse is now over this new window. To work around this problem send a WM_MOUSELEAVE manually to the original Control before the offending operation. class NativeMethods { [DllImport("user32.dll", CharSet=CharSet.Auto)] extern public static IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam) ; } // Send a WM_MOUSELEAVE manually to a window. NativeMethods.SendMessage(control.Handle, NativeMethods.WM_MOUSELEAVE, IntPtr.Zero, IntPtr.Zero);

12. Windows Forms from MFC WinForms FAQ Home 12.1 As a VC++ programmer, what should I look out for when using C#? 12.2 Where can I find a succinct discussion of Windows Forms?

12.3 When I add a destructor to a class, why isn't it hit when my instantiated object goes out of scope? 12.4 How do I convert a string to a double or int? What plays the role of atof and atoi in C#? 12.5 What class or method do I use to retrieve system metrics like the width of a scrollbar? 12.6 In C++, the code "MyClass ht;" creates an object ht on the stack frame. But in C#, this same code compiles, but gives a runtime error. Why? 12.7 I need to encode the LParam argument of a mouse message. How do I do MakeLong , HiWord and LoWord type conversions? 12.8 How do you clear the contents of a list box?

12.1 As a VC++ programmer, what should I look out for when using C#? 1] The conditionals in if-statements must calculate to a boolean value. You cannot write something like if (nUnits) { ... } where nUnits is a int. 2] All code must reside in a class. There are no global variables per se. 3] Bitwise & and | operators can be used with boolean types as logical operators. The && and || operators are also used, and do have the "short-cut calculation" behavior found in C++. The & and | operators do not have this ""short-cut calculation" behavior. 4] Pointers are not available in managed code. 5] In managed code, destructors are not hit immediately when as object goes out of scope. Ie, there is no deterministic destruction. 12.2 Where can I find a succinct discussion of Windows Forms? One solution is to use a panel that has a picturebox placed on it with DockStyle.Fill. This will make the picturebox assume the size of the panel. In addition, set the DockPadding.All property to the width of the desired border. Then in the Panel's OnPaint method, call the baseclass and then paint the desired borders. Here are both VB and C# projects that illustrate how you might go about this. The derived PicturePanel class has properties that allow you to set the bordersize and color as well as the image that is to be displayed. This sample retrieves the image from an embedded resource. It also uses double buffering to minimize flashing as you resize the control. 12.3 When I add a destructor to a class, why isn't it hit when my

instantiated object goes out of scope? Your managed code doesn't have a destructor, just a finalizer. The difference is a destructor (as in C++) fires immediately when an object goes out of scope, but a finalizer is run when the CLR's garbage collector (GC) gets to your object. It's not deterministic when this will occur, but it's very unlikely to occur right after the object goes out of scope. It will happen at a later time, however. (from sburke_online@microsoft..nospam..com on microsoft.public.dotnet.framework.windowsforms) 12.4 How do I convert a string to a double or int? What plays the role of atof and atoi in C#? You use static members of the Convert class found in the System namespace to handle conversions in the .NET framework. string s = "45"; int h = Convert.ToInt32(s); double d = Convert.ToDouble(s);

12.5 What class or method do I use to retrieve system metrics like the width of a scrollbar? In the .NET framework, you use the SystemInformation class from the System.Windows.Forms namespace. This class has comparable information to what GetSystemMetrics() returned in VC6. There is also a Screen class that contains additions display device properties. 12.6 In C++, the code "MyClass ht;" creates an object ht on the stack frame. But in C#, this same code compiles, but gives a runtime error. Why? MyClass ht; does not create any object. Instead, it creates a variable (a reference) of type MyClass, and sets its value to null. No object is created. To create an object, you need to explicitly call the class constructor with a new. MyClass ht = new MyClass(); You can also use ht to refer to an instance of MyClass that has been created (with a new) at some other point in your code. MyClass myClass = new MyClass(); ...... MyClass ht; ...... ht = myClass; // both ht and myClass are references to the same object...

12.7 I need to encode the LParam argument of a mouse message. How do I do MakeLong , HiWord and LoWord type conversions? You can create static methods that encode and decode these values. Here are some. static int MakeLong(int LoWord, int HiWord) { return (HiWord << 16) | (LoWord & 0xffff); } static IntPtr MakeLParam(int LoWord, int HiWord) { return (IntPtr) ((HiWord << 16) | (LoWord & 0xffff)); } static int HiWord(int Number) { return (Number >> 16) & 0xffff; } static int LoWord(int Number) { return Number & 0xffff; }

12.8 How do you clear the contents of a list box? You have to access the Items of the ListBox using the Items property and clear (or otherwise operate on the items) these.

this.lb1.Items.Clear();

13. Windows Forms from VB6 WinForms FAQ Home 13.1 How do I draw a line in VB7 as there is no Line command as there was in VB6? 13.2 What is the replacement for VB6's SendKeys statement? 13.3 In VB6, I used control arrays to have a common handler handle events from several controls. How can I do this in Windows Forms? 13.4 What is different about working with forms in VB.NET? For example, how can I reference one form from another form?

13.1 How do I draw a line in VB7 as there is no Line command as there was in VB6?

Try this code: Dim g as Graphics g = frmMain.CreateGraphics() g.DrawLine(Pens.Black, new Point(0,0), new Point(frmMain.ClientRectangle.Width), frmMain.ClientRectangle.Height) g.Dispose() This should draw a line from the upper left to the bottom right corner of your application, but only if it's visible. If this code isn't in the paint event, when something obscures your application and/or it repaints, this line will *not* be redrawn. (from sburke_online@microsoft..nospam..com on microsoft.public.dotnet.framework.windowsforms) Another possible solution that has a design-time element is to use a label with height 1 and FixedSingle Border. (suggested by Mark Lewis in microsoft.public.dotnet.framework.windowsforms) 13.2 What is the replacement for VB6's SendKeys statement? It is the Send method from the SendKeys class found in the System.Windows.Forms namespace. 13.3 In VB6, I used control arrays to have a common handler handle events from several controls. How can I do this in Windows Forms? You can specify events from different controls be handled by a single method. When you define your handler, you can list several events that are to be handled by listing them in the Handles argument. This statement handles three different textbox's TextChanged event. Private Sub TextsChanged(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles TextBox1.TextChanged, TextBox2.TextChanged, TextBox3.TextChanged In the TextsChanged handler, you may need to identify the particular control that fired the event. This sender parameter holds the control. You can use it with code such as: Dim eventTextBox as TextBox = sender

13.4 What is different about working with forms in VB.NET? For example, how can I reference one form from another form? Check out this Microsoft article on working with multiple forms in VB.NET by Duncan Mackenzie.

14. Windows Forms Patterns

WinForms FAQ Home

14.1 How to display a status dialog in a background thread during a long operation and allow the user to cancel? 14.2 How can I throw my events asynchronously? 14.3 Why are the Tooltips not being shown on a NumericUpDown control? 14.4 How to get the hyperlink and the hyperlink text dragged from IE in my Control's dragdrop event?

14.1 How to display a status dialog in a background thread during a long operation and allow the user to cancel? You can do this by starting a new thread and executing Application.Run for the status dialog form when the background thread starts running. To communicate changes in percentage use BeginInvoke to executes a specific delegate asynchronously on the thread that the form was created on. Download the ProgressThread progressthread.zip sample for a complete implementation of a BackgroundThreadStatusDialog class that shows a status dialog in a separate thread. In order to use BackgroundThreadStatusDialog from your code you have to update the Progress inside your loop and check the IsCanceled state to detect if the user pressed the Cancel button. private void button1_Click(object sender, System.EventArgs e) { BackgroundThreadStatusDialog statusDialog = new BackgroundThreadStatusDialog(); try { for (int n = 0; n < 1000; n++) { statusDialog.Percent = n/10; int ticks = System.Environment.TickCount; while (System.Environment.TickCount - ticks < 10) ; if (statusDialog.IsCanceled) return; } statusDialog.Close(); MessageBox.Show(statusDialog.IsCanceled ? "Canceled" : "Success"); } finally { statusDialog.Close(); } }

14.2 How can I throw my events asynchronously? Your managed code dosent have a destructor, just a finalizer. The difference is a destructor (as in C++) fires immediately when an object goes out of scope, but a finalizer is run when the CLR's garbage collector (GC) gets to your object. It cannot be determined when this will occur, but it's very unlikely to occur right after the object goes out of scope. It will happen at a later time, however. (from sburke_online@microsoft..nospam..com on microsoft.public.dotnet.framework.windowsforms) 14.3 Why are the Tooltips not being shown on a NumericUpDown control? This is because of a bug in the .net framework. When tooltips are set on a control that hosts other controls within it (like the numeric updown), tooltips are not shown on those child controls. To workaround this issue, do the following in code: [C#] foreach(Control c in this.numericUpDown1.Controls) { this.tooltip.SetToolTip(c, "mytooltip"); } [VB.Net] Dim c As Control For Each c In Me.numericUpDown1.Controls Me.tooltip.SetToolTip(c, "mytooltip") Next

14.4 How to get the hyperlink and the hyperlink text dragged from IE in my Control's drag-drop event? Compiled from the newsgroup posts by Andy Fish and Brian: You can do this in the DragDrop event handler of your control: [C#] try { //Use e.Data.GetData("UniformResourceLocator") to get the URL System.IO.Stream ioStream= (System.IO.Stream)e.Data.GetData("FileGroupDescriptor"); byte[] contents = new Byte[512]; ioStream.Read(contents,0,512); ioStream.Close(); StringBuilder sb = new StringBuilder();

//The magic number 76 is the size of that part of the //FILEGROUPDESCRIPTOR structure before the filename starts - cribbed //from another usenet post. for (int i=76; contents[i] != 0; i++) { sb.Append((char)contents[i]); } if (!sb.ToString(sb.Length-4,4).Equals(".url")) { throw new Exception("filename does not end in '.url'"); } filename = sb.ToString(0,sb.Length-4); } catch(Exception ex) { MessageBox.Show(ex.ToString()); } [VB.Net] Try 'Use e.Data.GetData("UniformResourceLocator") to get the URL System.IO.Stream ioStream= CType(e.Data.GetData("FileGroupDescriptor"), System.IO.Stream) Dim contents() As Byte = New Byte(512) {} ioStream.Read(contents,0,512) ioStream.Close() Dim sb As StringBuilder = New StringBuilder() 'The magic number 76 is the size of that part of the 'FILEGROUPDESCRIPTOR structure before the filename starts - cribbed 'from another usenet post. Dim i As Integer For i = 76 To contents(i) 0 Step i + 1 sb.Append(CType(contents(i), Char)) Next If Not sb.ToString(sb.Length-4,4).Equals(".url") Then Throw New Exception("filename does not end in '.url'") End If filename = sb.ToString(0,sb.Length-4) Catch ex As Exception MessageBox.Show(ex.ToString()) End Try

15. Windows Forms Printing 15.1 15.2

WinForms FAQ Home

How do I print a form? How do I display the PrintPreview maximized and control its zooming?

15.1 How do I print a form? I am afraid there is not a very easy way to print a form. You may implement this function with the steps below: 1. Add a print function to your application. To do this, you should add a PrintDocument component to your application. Please drag a PrintDocument from the tool box to your form. After that, you should create a PrintDialog and add the code to print the document. private void buttonPrint_Click(object sender, System.EventArgs e) { PrintDialog printDialog1 = new PrintDialog(); printDialog1.Document = printDocument1; DialogResult result = printDialog1.ShowDialog(); if (result == DialogResult.OK) { printDocument1.Print(); } } For detailed information about print framework, please see "Windows Forms Print Support" in the MSDN (October 2001). 2. Draw the form when printing. This step is a little complex. You should handle the PrintPage of the printDocument1 and draw the form to the printer device. In the event you may copy the form to an image and then draw it to the printer device. private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e) { Graphics graphic = this.CreateGraphics(); Size s = this.Size; Image memImage = new Bitmap(s.Width, s.Height, graphic); Graphics memGraphic = Graphics.FromImage(memImage); IntPtr dc1 = graphic.GetHdc(); IntPtr dc2 = memGraphic.GetHdc(); BitBlt(dc2, 0, 0, this.ClientRectangle.Width, this.ClientRectangle.Height, dc1, 0, 0, 13369376); graphic.ReleaseHdc(dc1); memGraphic.ReleaseHdc(dc2); e.Graphics.DrawImage(memImage,0,0);

} The above referenced the article "Screen Capturing a Form in .NET - Using GDI in GDI+" by Michael Gold. You may find it at: http://www.c-sharpcorner.com/Graphics/ScreenCaptFormMG.asp 3. Declare the API function. Please note the BitBlt function used in Step 2. It is an unmanaged function. You should use DllImportAttribute attribute to import it to your code. Although, this is the Step 3, you may perform this step any time. [System.Runtime.InteropServices.DllImportAttribute("gdi32.dll")] private static extern bool BitBlt( IntPtr hdcDest, // handle to destination DC int nXDest, // x-coord of destination upper-left corner int nYDest, // y-coord of destination upper-left corner int nWidth, // width of destination rectangle int nHeight, // height of destination rectangle IntPtr hdcSrc, // handle to source DC int nXSrc, // x-coordinate of source upper-left corner int nYSrc, // y-coordinate of source upper-left corner System.Int32 dwRop // raster operation code ); For more information about DllImportAttribute attribute please see: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/ frlrfSystemRuntimeInteropServicesDllImportAttributeClassTopic.asp (from Lion Shi (lion_noreply@microsoft.com) on microsoft.public.dotnet.framework.windowsforms) 15.2 How do I display the PrintPreview maximized and control its zooming? You can use the WindowState property of the PrintPreviewDialog class to bring the PrintPreview maximized. To handle zooming, the PrintPreviewDialog has a property, PrintPreviewControl. PrintPreviewControl owns a Zoom property that allows you to set the zoom factor of the PrintPreview.

16. Windows Forms Resources WinForms FAQ Home 16.1 What are the steps to compiling and using multiple language resources? 16.2 How do I load a BMP file that has been added to my solution as an embedded resource? 16.3 Why doesn't a Form containing an ImageList not open in WinRes? 16.4 How do I embed a manifest file into my exe? 16.5 How can I save a temporary disk file from an embedded string resource? 16.6 How do I read embedded resources? 16.7 How can I check if a certain resource is included in a assembly?

16.8 Where can I find information on globalization? 16.9 How do I read or write resources from code? 16.10 How do I create resources to make culture-aware programs without re-compiling code? 16.11 How do I programmatically use resources to make my programs culturally aware? 16.12 How can I dynamically load a resource file?

16.1 What are the steps to compiling and using multiple language resources? Localization Sample illustrates how to load different language resources depending upon a parameter passed in when you run the program from a command line. The sample can display English, French, Spanish and German words using the same exe. For example, typing Test de on the command line will count to three in German. Typing Test fr will count to three in French. The argument passed in is used to determine the culture setting for the resources. In each folder is a batch file that handles compiling the single exe and multiple resources. The tools RESGEN and AL are used in these batch files to generate the resources and resource DLL's. Notice the naming conventions used by the subfolders containing the foreign resource DLLs. 16.2 How do I load a BMP file that has been added to my solution as an embedded resource? If you add a BMP file to your solution using the File|Add Existing Item... Menu Item, then change the Build Action property of this BMP file to Embedded Resource, you can then access this resource with code similar to: // WindowsApplication6 corresponds to Default Namespace in your project settings. // subfolders should be the folder names if any, inside which the bmp is added. If the bmp was added to the top level, you don't have to specify anything here. string bmpName = "WindowsApplication6.subfolders.sync.bmp"; System.IO.Stream strm = null; try { strm = this.GetType().Assembly.GetManifestResourceStream(bmpName); pictureBox1.Image = new Bitmap(strm); // Do the same for Icons // Icon icon1 = new Icon(strm); } catch(Exception e) { MessageBox.Show(e.Message); } finally

{ if(strm != null) strm.Close(); }

16.3 Why doesn't a Form containing an ImageList not open in WinRes? There seems to be a bug in the WinRes utility (1.0) that prevents a form from getting loaded in the WinRes editor (usually an exception like "Object reference not found" occurs). A quick workaround is to always name your ImageLists to be of the form "imageListN" (case sensitive; where N is the smallest no. you could use without a clash with other ImageLists). 16.4 How do I embed a manifest file into my exe? This can be done through the SendKeys class in the System.Windows.Forms namespace. Check it out in the MS help documentation. 16.5 How can I save a temporary disk file from an embedded string resource? //usage: string s = CopyResourceToTempFile(GetType(), "showcase.txt"); //where showcase.txt is an embedded resource static string CopyResourceToTempFile(Type type, string name) { string temp = ""; string nsdot = type.Namespace; if (nsdot == null) nsdot = ""; else if (nsdot != "") nsdot += "."; Stream stream = type.Module.Assembly.GetManifestResourceStream(nsdot + name); if (stream != null) { StreamReader sr = new StreamReader(stream); temp = Path.GetTempFileName(); StreamWriter sw = new StreamWriter(temp, false); sw.Write(sr.ReadToEnd()); sw.Flush(); sw.Close(); } return temp; }

16.6 How do I read embedded resources? Check out the entry titled How do I load a BMP file that has been added to my solution as an embedded resource? in this section. That code can be used for any kind of embedded resource. 16.7 How can I check if a certain resource is included in a assembly? If a resource is not available in a assembly or in satellite assemblies you will get a message like this "Could not find any resources appropriate for the specified culture...". You can open your assembly and any related satellite assemblies in ILDASM (tool that ships with the .NET framework). When your assembly is open in ILDASM take a look at the manifest file. This contains a list of the resources in your assembly. Usually such errors are the result of incorrect namespaces being used or from spelling errors. 16.8 Where can I find information on globalization? Here are some MSDN globalization samples: Visual Basic .NET Code Sample: Working with Resource Files .NET Samples - How To: Globalization and NLS Also look at the following topic in online help inside IDE. Developing World-Ready Applications 16.9 How do I read or write resources from code? You can use the ResourceWriter object to create resource files. Take a look at the discussion and code found on gotdotnet.com 16.10 How do I create resources to make culture-aware programs without re-compiling code? Placing culture dependent resources in a separate file from your compiled program code allows you to write applications that can change cultures without having to recompile code to handle different resources. Take a look at the discussion and code found on gotdotnet.com 16.11 How do I programmatically use resources to make my programs culturally aware? You can use the ResourceManager object to dynamically load a particular resource based on a selected culture. This technique allows you to develop culture-aware programs without having to recompile your application. Take a look at the discussion and code found on gotdotnet.com

16.12 How can I dynamically load a resource file? You use the ResourceManager class found in System.Resources to access and control resources.See the article by Caspar Boekhoudt on C# Corner for a detailed discussion.

17. Windows Forms Scrolling WinForms FAQ Home 17.1 How do I add support for custom scrolling to my own user control? 17.2 Are there any events that get fired when the user scrolls?

17.1 How do I add support for custom scrolling to my own user control? Windows Forms features a ScrollableControl. This will work in most cases where you know the exact dimensions of your control and scroll by pixel. See the MSDN Documentation for ScrollableControl for discussion how to use this control. Sometimes you may need more customized scrolling, for example if you implemented a text editor and you want to scroll lines and not pixels. For more customized scrolling you have to use PInvoke to access the Win32 ScrollWindow method. The following code shows how to enable access to the Win32 ScrollWindow method from your code. [StructLayout(LayoutKind.Sequential)] public struct RECT { public int left; public int top; public int right; public int bottom; public RECT(Rectangle rect) { this.bottom = rect.Bottom; this.left = rect.Left; this.right = rect.Right; this.top = rect.Top; } public RECT(int left, int top, int right, int bottom) { this.bottom = bottom; this.left = left; this.right = right;

this.top = top; } } [DllImport("user32")] public static extern bool ScrollWindow(IntPtr hWnd, int nXAmount, int nYAmount, ref RECT rectScrollRegion, ref RECT rectClip); void MyScrollFunc(int yAmount) { RECT r = new RECT(ClientRectangle); ScrollWindow(Handle, 0, yAmount, ref r, ref r); }

17.2 Are there any events that get fired when the user scrolls? You could override WndProc and listen for (0x114 WM_HSCROLL) and (0x115 WM_VSCROLL) messages (m.Msg will be set to the above values). These messages should be sent when the user scrolls.

18. Windows Forms Common Dialogs WinForms FAQ Home 18.1 How do I use the ColorDialog to pick a color? 18.2 How do I use the FontDialog class to set a control's font? 18.3 How do I implement a Folder Browser class? 18.4 How can I get just the name of a file from the complete path string? 18.5 How can I get just the extension of a file from the complete path string? 18.6 How do I use the OpenFileDialog? 18.7 How do I specify the path for the FolderBrowser instance when it opens the first time?

18.1 How do I use the ColorDialog to pick a color? It is straight-forward. Create an instance of the class and call its ShowDialog method. ColorDialog colorDialog1 = new ColorDialog(); //fontDialog1.ShowColor = true; if(colorDialog1.ShowDialog() != DialogResult.Cancel ) { textBox1.ForeColor = colorDialog1.Color; } textBox1.Text = "this is a test";

18.2 How do I use the FontDialog class to set a control's font?

It is straight-forward. Create an instance of the class and call its ShowDialog method. FontDialog fontDialog1 = new FontDialog(); fontDialog1.ShowColor = true; if(fontDialog1.ShowDialog() != DialogResult.Cancel ) { textBox1.Font = fontDialog1.Font ; textBox1.ForeColor = fontDialog1.Color; } textBox1.Text = "this is a test";

18.3 How do I implement a Folder Browser class? Below is a technique that uses FolderNameEditor and FolderBrowser classes to implement a solution. You can also use iterop to get a solution. Both the FolderNameEditor and FolderBrowser classes used in this solution are described in the Docs as "This type supports the .NET Framework infrastructure and is not intended to be used directly from your code." // add a reference to System.Design.DLL using System.Windows.Forms.Design; ............. public class DirBrowser : FolderNameEditor { FolderBrowser fb = new FolderBrowser(); public string Description { set { _description = value; } get { return _description; } } public string ReturnPath { get { return _returnPath; } } public DirBrowser() { } public DialogResult ShowDialog() { fb.Description = _description; fb.StartLocation = FolderBrowserFolder.MyComputer; DialogResult r = fb.ShowDialog(); if (r == DialogResult.OK) _returnPath = fb.DirectoryPath; else _returnPath = String.Empty; return r; }

private string _description = "Choose Directory"; private string _returnPath = String.Empty; } (Posted by Ryan Farley on the microsoft.public.dotnet.language.csharp newsgroup) 18.4 How can I get just the name of a file from the complete path string? Use FileInfo. Instantiate a FileInfo object with the full path as constructor arg. Then simply call FileInfo.Name and you will get just the name of the file. FileInfo finfo = new FileInfo(strFileName); Console.WriteLine(finfo.Name);

18.5 How can I get just the extension of a file from the complete path string? Use FileInfo. Instantiate a FileInfo object with the full path as constructor arg. Then simply call FileInfo.Extension and you will get just the extension of the file. FileInfo finfo = new FileInfo(strFileName); Console.WriteLine(finfo.Extension);

18.6 How do I use the OpenFileDialog? using System.Text; using System.IO; .... private void button1_Click(object sender, System.EventArgs e) { OpenFileDialog dlg = new OpenFileDialog(); dlg.Title = "Open text file" ; dlg.InitialDirectory = @"c:\" ; dlg.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*" ; if(dlg.ShowDialog() == DialogResult.OK) { StreamReader sr = File.OpenText(dlg.FileName); string s = sr.ReadLine(); StringBuilder sb = new StringBuilder(); while (s != null) { sb.Append(s); s = sr.ReadLine(); }

sr.Close(); textBox1.Text = sb.ToString(); } }

18.7 How do I specify the path for the FolderBrowser instance when it opens the first time? In the 1.1 framework there is a SelectedPath property that will let you do this.

19. Windows Forms ListBox WinForms FAQ Home 19.1 How do I add and delete items from a ListBox? 19.2 How do I implement Drag and Drop support between ListBoxes? 19.3 How can I drag file names from Windows Explorer and drop them into a listbox? 19.4 How do I implement an ownerdrawn listbox? 19.5 How can I set the width of a listbox to fit the text?

19.1 How do I add and delete items from a ListBox? Check out Devinder Arora's article Using ListBox Control in C# on C# Corner. 19.2 How do I implement Drag and Drop support between ListBoxes? The code below minimally handles D&D for a single selection list box by handling four events. The D&D is initiated in MouseDown if the user mouses down on the current selection. The DragEnter event is used to set the dragging effect to copy if you are not over the drag source. The DragDrop event is used to do the drop. And finally, the SelectedIndexChanged event is used to track the current selection for use in the MouseDown event. You can download a working project (C#, VB). This project handles additional events to provide visual queues during the drop. The samples referenced above do not allow dragging and dropping within the same listbox. Here is another set of samples that does allow you to drag and drop within the same listbox. public class ListBoxDragNDrop : ListBox { private int lastMouseUpItemIndex = -1; private bool isDropSource = false; public ListBoxDragNDrop() { this.AllowDrop = true; //allow D&D

this.SelectionMode = SelectionMode.One; //single selection DragDrop += new System.Windows.Forms.DragEventHandler(OnDragDrop); DragEnter += new System.Windows.Forms.DragEventHandler(OnDragEnter); MouseDown += new System.Windows.Forms.MouseEventHandler(OnMouseDown); SelectedIndexChanged += new System.EventHandler(OnSelectedIndexChanged); } private void OnDragDrop(object sender, DragEventArgs e) { if(e.Effect == DragDropEffects.Copy) { Point point = this.PointToClient(new Point(e.X, e.Y)); int index = this.IndexFromPoint(point); if( index > -1 && index < this.Items.Count) Items.Insert(index, e.Data.GetData(DataFormats.Text)); else Items.Add(e.Data.GetData(DataFormats.Text)); } } private void OnDragEnter(object sender, System.Windows.Forms.DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.Text) && !isDropSource ) e.Effect = DragDropEffects.Copy; else e.Effect = DragDropEffects.None; } private void OnMouseDown(object sender, System.Windows.Forms.MouseEventArgs e) { if(MouseButtons == MouseButtons.Left && SelectedIndex == lastMouseUpItemIndex) { isDropSource = true; DoDragDrop(SelectedItem, DragDropEffects.Copy); isDropSource = false; lastMouseUpItemIndex = this.SelectedIndex; } } private void OnSelectedIndexChanged(object sender, System.EventArgs e) { lastMouseUpItemIndex = this.SelectedIndex; } } One more note. If your listboxes contain full file pathnames, you can support dropping these paths onto Windows Explorer by supporting the FileDrop dataformat. In the OnMouseDown override, if you replace the DoDragDrop line with the following code block, you will be able to drop files. //put the file path is a string array

string[] files = new String[1]; files[0] = SelectedItem.ToString(); //create a dataobject holding this array as a filedrop DataObject data = new DataObject(DataFormats.FileDrop, files); //also add the selection as textdata data.SetData(DataFormats.StringFormat, SelectedItem.ToString()); //do the dragdrop DoDragDrop(data, DragDropEffects.Copy);

19.3 How can I drag file names from Windows Explorer and drop them into a listbox? Change the control's Anchor property so that it is anchored on all 4 sides.

Please note that you can only have 1 control per form anchored in this manner (all 4 sides). And other controls on the form should be anchored by their sides that are not adjacent to special control anchored on all 4 sides. 19.4 How do I implement an ownerdrawn listbox? Check out this sample project at gotnetdot.com. It derives from Form and implements the owner drawing by handling the DrawItem event and MeasureItem event. You can also download a sample that implements an owner drawn listbox by deriving from ListBox and overriding the virtual methods OnDrawItem and OnMeasureItem. Here is a OnDrawItem override that draws colored rectangles in the listbox. protected override void OnDrawItem(System.Windows.Forms.DrawItemEventArgs e)

{ //undo the selection rect on the old selection if( oldSelectedRect != e.Bounds && oldSelectedIndex > -1 && oldSelectedIndex < Items.Count) { e.Graphics.DrawRectangle(new Pen((Color) Items[oldSelectedIndex], 2), oldSelectedRect); } //draw the item .. here we just fill a rect if( e.Index > -1 && e.Index < Items.Count) e.Graphics.FillRectangle(new SolidBrush( (Color)Items[e.Index] ), e.Bounds); //draw selection rect if needed if(SelectedIndex == e.Index) { e.Graphics.DrawRectangle(new Pen(Color.Black,2), e.Bounds); oldSelectedRect = e.Bounds; oldSelectedIndex = e.Index; } }

19.5 How can I set the width of a listbox to fit the text? You can subclass ComboBox. In your derived class, make sure you set this.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; this.DropDownStyle = ComboBoxStyle.DropDownList; You also need to handle the DrawItem event to actually implement the drawing. Check out the details in the OwnerDrawnComboBox sample.

20. Windows Forms ComboBox WinForms FAQ Home 20.1 How do I bind the values of an enum to a ComboBox? 20.2 How can I programmatically prevent the combobox from dropping? 20.3 How can I adjust the height of the of my ComboBox dropdown? 20.4 How do I implement an owner drawn combobox? 20.5 How do I add a ComboBox button to a toolbar? 20.6 How do I set the width of a combobox to fit the entries in its list? 20.7 How can I programmatically create a new list for my ComboBox dropdown? 20.8 How can I turn off editing in the textbox portion of a ComboBox, restricting the user to selecting only those options in the drop list?

20.1 How do I bind the values of an enum to a ComboBox? This entry was created using the feedback provided by Jay Harlow in the newsgroups. The enum values can be bound to a combobox as follows: [C#] // Setup the binding as follows: // MyValues is the enum type comboBox1.DataSource = Enum.GetValues(typeof MyValues); [VB] comboBox1.DataSource = Enum.GetValues(Type.GetType(MyValues)) Then in the SelectedValueChanged event for the ComboBox. [C#] private void ComboBox1ValueChanged(object sender, EventArgs e) { MyValues v = (MyValues)this.comboBox1.SelectedValue; } [VB] Private Sub ComboBox1ValueChanged(ByVal sender As Object, ByVal e As EventArgs) Dim v As MyValues = CType(Me.comboBox1.SelectedValue, MyValues) End Sub

20.2 How can I programmatically prevent the combobox from dropping? You can avoid the combobox dropping by overriding its WndProc method and ignoring the WM_LBUTTONDOWN and WM_LBUTTONDBLCLK. [C#] public class MyComboBox : ComboBox

{ protected override void WndProc(ref System.Windows.Forms.Message m) { if(m.Msg == 0x201 //WM_LBUTTONDOWN || m.Msg == 0x203) //WM_LBUTTONDBLCLK return; base.WndProc(ref m); } } [VB.NET] Public Class MyComboBox Inherits ComboBox Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message) If m.Msg = &H201 OrElse m.Msg = &H203 Then 'WM_LBUTTONDOWN or WM_LBUTTONDBLCLK Return End If MyBase.WndProc(m) End Sub 'WndProc End Class 'MyComboBox

20.3 How can I adjust the height of the of my ComboBox dropdown? You can control the height by changing the value of the MaxDropDownItems property of the the Combobox, which is the setting for maximum number of entries that will be displayed by the drop-down list. 20.4 How do I implement an owner drawn combobox? You can subclass ComboBox. In your derived class, make sure you set this.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; this.DropDownStyle = ComboBoxStyle.DropDownList; You also need to handle the DrawItem event to actually implement the drawing. Check out the details in the OwnerDrawnComboBox sample.

20.5 How do I add a ComboBox button to a toolbar? Michael Gold discusses this problem in How to create a ComboBox button in a toolbar in .NET on C# Corner. 20.6 How do I set the width of a combobox to fit the entries in its list? You can iterate through the list to find the longest text extent using MeasureString, and then use this as the combobox width adding a fudge factor for the dropdown button. System.Drawing.Graphics g = comboBox1.CreateGraphics(); float maxWidth = 0f; foreach(object o in comboBox1.Items) { float w = g.MeasureString(o.ToString(), comboBox1.Font).Width; if(w > maxWidth) maxWidth = w; } g.Dispose(); comboBox1.Width = (int) maxWidth + 20; // 20 is to take care of button width

20.7 How can I programmatically create a new list for my ComboBox dropdown? Here are some snippets. (Courtesy of Michael Lang) [C#] DataTable list = new DataTable(); list.Columns.Add(new DataColumn("Display", typeof(string))); list.Columns.Add(new DataColumn("Id", typeof(int))); list.Rows.Add(list.NewRow()); list.Rows.Add(list.NewRow()); list.Rows.Add(list.NewRow()); list.Rows[0][0] = "one";

list.Rows[0][1] = 1; list.Rows[1][0] = "two"; list.Rows[1][1] = 2; list.Rows[2][0] = "three"; list.Rows[2][1] = 3; comboBox1.DataSource = list; comboBox1.DisplayMember = "Display"; comboBox1.ValueMember = "Id"; [VB.NET] Dim list As New DataTable() list.Columns.Add(New DataColumn("Display", GetType(String))) list.Columns.Add(New DataColumn("Id", GetType(Integer))) list.Rows.Add(list.NewRow()) list.Rows.Add(list.NewRow()) list.Rows.Add(list.NewRow()) list.Rows(0)(0) = "one" ' list.Rows(0)(1) = 1 ' list.Rows(1)(0) = "two" ' list.Rows(1)(1) = 2 ' list.Rows(2)(0) = "three" ' list.Rows(2)(1) = 3 ' comboBox1.DataSource = list comboBox1.DisplayMember = "Display" comboBox1.ValueMember = "Id"

20.8 How can I turn off editing in the textbox portion of a ComboBox, restricting the user to selecting only those options in the drop list? Set the combobox's DropDownStyle property to DropDownList.

Вам также может понравиться