You are on page 1of 221

Contents

Windows Forms
Getting Started with Windows Forms
Windows Forms Overview
Creating a New Windows Form
How to: Create a Windows Forms Application from the Command Line
Windows Forms Coordinates
Creating Event Handlers in Windows Forms
Events Overview
Event Handlers Overview
How to: Create Event Handlers at Run Time for Windows Forms
How to: Connect Multiple Events to a Single Event Handler in Windows Forms
Order of Events in Windows Forms
Adjusting the Size and Scale of Windows Forms
How to: Resize Windows Forms
Automatic Scaling in Windows Forms
How to: Respond to Font Scheme Changes in a Windows Forms Application
High DPI Support In Windows Forms
Changing the Appearance of Windows Forms
How to: Change the Borders of Windows Forms
Windows Forms Controls
User Input in Windows Forms
User Input in a Windows Forms Application
Keyboard Input in a Windows Forms Application
How Keyboard Input Works
Using Keyboard Events
How to: Modify Keyboard Input to a Standard Control
How to: Determine Which Modifier Key Was Pressed
How to: Handle Keyboard Input at the Form Level
Mouse Input in a Windows Forms Application
How Mouse Input Works in Windows Forms
Mouse Events in Windows Forms
How to: Distinguish Between Clicks and Double-Clicks
Mouse Pointers in Windows Forms
Mouse Capture in Windows Forms
Drag-and-Drop Functionality in Windows Forms
How to: Simulate Mouse and Keyboard Events in Code
How to: Handle User Input Events in Windows Forms Controls
User Input Validation in Windows Forms
Dialog Boxes in Windows Forms
How to: Display Dialog Boxes for Windows Forms
Windows Forms Data Binding
Data Binding and Windows Forms
Data Sources Supported by Windows Forms
Interfaces Related to Data Binding
Change Notification in Windows Forms Data Binding
How to: Apply the PropertyNameChanged Pattern
How to: Create a Bound Control and Format the Displayed Data
How to: Create a Simple-Bound Control on a Windows Form
How to: Ensure Multiple Controls Bound to the Same Data Source Remain
Synchronized
How to: Ensure the Selected Row in a Child Table Remains at the Correct Position
How to: Implement the IListSource Interface
How to: Implement the INotifyPropertyChanged Interface
How to: Implement the ITypedList Interface
How to: Navigate Data in Windows Forms
Windows Forms Security
Security in Windows Forms Overview
More Secure File and Data Access in Windows Forms
More Secure Printing in Windows Forms
Additional Security Considerations in Windows Forms
ClickOnce Deployment for Windows Forms
Accessibility improvements with .NET Core 3.0
How to: Access Keyed Collections in Windows Forms
Enhancing Windows Forms Applications
Getting Started with Windows Forms
9/1/2020 • 2 minutes to read • Edit Online

With Windows Forms, you can create powerful Windows-based applications. The following topics describe in-
depth how to harness the power of Windows Forms to display data, handle user input, and deploy your
applications easily and with enhanced security.

In This Section
Windows Forms Overview
Contains an overview of Windows Forms and smart client applications.
Creating a New Windows Form
Contains links to topics that describe basic concepts for creating Windows Forms applications.
Creating Event Handlers in Windows Forms
Contains links to topics that describe how to create Windows Forms event handlers.
Adjusting the Size and Scale of Windows Forms
Contains links to topics that show how to adjust the size and scale of Windows Forms.
Changing the Appearance of Windows Forms
Contains links to topics that show how to change the appearance of Windows Forms applications.
Windows Forms Controls
Contains links to topics that describe and show how to use Windows Forms controls and components.
User Input in Windows Forms
Contains links to topics that describe and show how to handle input from the user in Windows Forms applications.
Dialog Boxes in Windows Forms
Contains links to topics that describe the different dialog boxes for use in Windows Forms.
Windows Forms Data Binding
Contains links to topics that describe the Windows Forms data binding architecture and how to use it in Windows
Forms applications.
Windows Forms Security
Contains links to topics that describe how to build Windows Forms applications that have enhanced security.
ClickOnce Deployment for Windows Forms
Contains links to topics that describe how to easily deploy Windows Forms applications.
How to: Access Keyed Collections in Windows Forms
Demonstrates how to access collections with keys rather than indexes.

Related Sections
Enhancing Windows Forms Applications
Contains links to topics that describe more advanced concepts for creating Windows Forms applications.
Windows Forms overview
9/1/2020 • 7 minutes to read • Edit Online

The following overview discusses the advantages of smart client applications, the main features of Windows Forms
programming, and how you can use Windows Forms to build smart clients that meet the needs of today's
enterprises and end users.

Windows Forms and smart client apps


With Windows Forms you develop smart clients. Smart clients are graphically rich applications that are easy to
deploy and update, can work when they are connected to or disconnected from the Internet, and can access
resources on the local computer in a more secure manner than traditional Windows-based applications.
Build rich, interactive user interfaces
Windows Forms is a smart client technology for the .NET Framework, a set of managed libraries that simplify
common application tasks such as reading and writing to the file system. When you use a development
environment like Visual Studio, you can create Windows Forms smart-client applications that display information,
request input from users, and communicate with remote computers over a network.
In Windows Forms, a form is a visual surface on which you display information to the user. You ordinarily build
Windows Forms applications by adding controls to forms and developing responses to user actions, such as mouse
clicks or key presses. A control is a discrete user interface (UI) element that displays data or accepts data input.
When a user does something to your form or one of its controls, the action generates an event. Your application
reacts to these events by using code, and processes the events when they occur. For more information, see Creating
Event Handlers in Windows Forms.
Windows Forms contains a variety of controls that you can add to forms: controls that display text boxes, buttons,
drop-down boxes, radio buttons, and even Web pages. For a list of all the controls you can use on a form, see
Controls to Use on Windows Forms. If an existing control does not meet your needs, Windows Forms also supports
creating your own custom controls using the UserControl class.
Windows Forms has rich UI controls that emulate features in high-end applications like Microsoft Office. When you
use the ToolStrip and MenuStrip control, you can create toolbars and menus that contain text and images, display
submenus, and host other controls such as text boxes and combo boxes.
With the drag-and-drop Windows Forms Designer in Visual Studio, you can easily create Windows Forms
applications. Just select the controls with your cursor and add them where you want on the form. The designer
provides tools such as gridlines and snap lines to take the hassle out of aligning controls. And whether you use
Visual Studio or compile at the command line, you can use the FlowLayoutPanel, TableLayoutPanel and
SplitContainer controls to create advanced form layouts in less time.
Finally, if you must create your own custom UI elements, the System.Drawing namespace contains a large selection
of classes to render lines, circles, and other shapes directly on a form.

NOTE
Windows Forms controls are not designed to be marshaled across application domains. For this reason, Microsoft does not
support passing a Windows Forms control across an AppDomain boundary, even though the Control base type of
MarshalByRefObject would seem to indicate that this is possible. Windows Forms applications that have multiple application
domains are supported as long as no Windows Forms controls are passed across application domain boundaries.
Create forms and controls
For step-by-step information about how to use these features, see the following Help topics.

DESC RIP T IO N H EL P TO P IC

Using controls on forms How to: Add Controls to Windows Forms

Using the ToolStrip Control How to: Create a Basic ToolStrip with Standard Items Using the
Designer

Creating graphics with System.Drawing Getting Started with Graphics Programming

Creating custom controls How to: Inherit from the UserControl Class

Display and manipulate data


Many applications must display data from a database, XML file, XML Web service, or other data source. Windows
Forms provides a flexible control that is named the DataGridView control for displaying such tabular data in a
traditional row and column format, so that every piece of data occupies its own cell. When you use DataGridView,
you can customize the appearance of individual cells, lock arbitrary rows and columns in place, and display
complex controls inside cells, among other features.
Connecting to data sources over a network is a simple task with Windows Forms smart clients. The BindingSource
component represents a connection to a data source, and exposes methods for binding data to controls, navigating
to the previous and next records, editing records, and saving changes back to the original source. The
BindingNavigator control provides a simple interface over the BindingSource component for users to navigate
between records.
You can create data-bound controls easily by using the Data Sources window. The window displays data sources
such as databases, Web services, and objects in your project. You can create data-bound controls by dragging items
from this window onto forms in your project. You can also data-bind existing controls to data by dragging objects
from the Data Sources window onto existing controls.
Another type of data binding you can manage in Windows Forms is settings. Most smart client applications must
retain some information about their run-time state, such as the last-known size of forms, and retain user
preference data, such as default locations for saved files. The Application Settings feature addresses these
requirements by providing an easy way to store both types of settings on the client computer. After you define
these settings by using either Visual Studio or a code editor, the settings are persisted as XML and automatically
read back into memory at run time.
Display and manipulate data
For step-by-step information about how to use these features, see the following Help topics.

DESC RIP T IO N H EL P TO P IC

Using the BindingSource component How to: Bind Windows Forms Controls with the BindingSource
Component Using the Designer

Working with ADO.NET data sources How to: Sort and Filter ADO.NET Data with the Windows
Forms BindingSource Component

Using the Data Sources window Bind Windows Forms controls to data in Visual Studio

Using application settings How to: Create Application Settings

Deploy apps to client computers


After you have written your application, you must send the application to your users so that they can install and
run it on their own client computers. When you use the ClickOnce technology, you can deploy your applications
from within Visual Studio by using just a few clicks, and provide your users with a URL pointing to your application
on the Web. ClickOnce manages all the elements and dependencies in your application, and ensures that the
application is correctly installed on the client computer.
ClickOnce applications can be configured to run only when the user is connected to the network, or to run both
online and offline. When you specify that an application should support offline operation, ClickOnce adds a link to
your application in the user's Star t menu. The user can then open the application without using the URL.
When you update your application, you publish a new deployment manifest and a new copy of your application to
your Web server. ClickOnce will detect that there is an update available and upgrade the user's installation; no
custom programming is required to update old assemblies.
Deploy ClickOnce apps
For a full introduction to ClickOnce, see ClickOnce Security and Deployment. For step-by-step information about
how to use these features, see the following Help topics,

DESC RIP T IO N H EL P TO P IC

Deploying an application by using ClickOnce How to: Publish a ClickOnce Application using the Publish
Wizard

Walkthrough: Manually Deploying a ClickOnce Application

Updating a ClickOnce deployment How to: Manage Updates for a ClickOnce Application

Managing security with ClickOnce How to: Enable ClickOnce Security Settings

Other controls and features


There are many other features in Windows Forms that make implementing common tasks fast and easy, such as
support for creating dialog boxes, printing, adding Help and documentation, and localizing your application to
multiple languages. Additionally, Windows Forms relies on the robust security system of the .NET Framework. With
this system, you can release more secure applications to your customers.
Implement other controls and features
For step-by-step information about how to use these features, see the following Help topics.

DESC RIP T IO N H EL P TO P IC

Printing the contents of a form How to: Print Graphics in Windows Forms

How to: Print a Multi-Page Text File in Windows Forms

Learn more about Windows Forms security Security in Windows Forms Overview

See also
Getting Started with Windows Forms
Creating a New Windows Form
ToolStrip Control Overview
DataGridView Control Overview
BindingSource Component Overview
Application Settings Overview
ClickOnce Security and Deployment
Creating a New Windows Form
9/1/2020 • 2 minutes to read • Edit Online

This topic contains links to topics that describe how to create your first Windows Forms application. Also, the topics
in this section introduce some of the basic vocabulary and guidelines that you should understand when you start
to create a Windows Forms application. To learn more about Windows Forms applications, the controls you can
use on them, events and handling events, and how to handle input from the user, see the related topic list.

In This Section
Windows Forms Coordinates
Describes client and screen coordinates.
How to: Create a Windows Forms Application from the Command Line
Describes how to create a basic Windows Form and compile it from the command line.

Reference
Form
Describes this class and contains links to all its members.
Control
Describes this class and contains links to all its members.

Related Sections
Handling User Input
Contains links to topics that discuss user input and how to handle it in Windows Forms applications.
Creating Event Handlers in Windows Forms
Contains links to topics that describe how to handle events in Windows Forms applications.
Changing the Appearance of Windows Forms
Contains links to topics that show how to change the appearance of Windows Forms applications.
Windows Forms Controls by Function
Contains links to topics that describe the controls you can use in Windows Forms applications.
How to: Create a Windows Forms application from
the command line
9/1/2020 • 3 minutes to read • Edit Online

The following procedures describe the basic steps that you must complete to create and run a Windows Forms
application from the command line. There is extensive support for these procedures in Visual Studio. Also see
Walkthrough: Hosting a Windows Forms Control in WPF.

Procedure
To create the form
1. In an empty code file, type the following Imports or using statements:

using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

Imports System.ComponentModel
Imports System.Drawing
Imports System.Windows.Forms

2. Declare a class named Form1 that inherits from the Form class:

public class Form1 : Form

Public Class Form1


Inherits Form

3. Create a parameterless constructor for Form1 .


You will add more code to the constructor in a subsequent procedure.

public Form1() {}

Public Sub New()

End Sub

4. Add a Main method to the class.


a. Apply the STAThreadAttribute to the C# Main method to specify your Windows Forms application is
a single-threaded apartment. (The attribute is not necessary in Visual Basic, since Windows forms
applications developed with Visual Basic use a single-threaded apartment model by default.)
b. Call EnableVisualStyles to apply operating system styles to your application.
c. Create an instance of the form and run it.
[STAThread]
public static void Main()
{
Application.EnableVisualStyles();
Application.Run(new Form1());
}

Public Shared Sub Main()


Application.EnableVisualStyles()
Application.Run(New Form1())

End Sub
End Class

To compile and run the application


1. At the .NET Framework command prompt, navigate to the directory you created the Form1 class.
2. Compile the form.
If you are using C#, type: csc form1.cs

-or-

If you are using Visual Basic, type: vbc form1.vb

3. At the command prompt, type: Form1.exe

Adding a control and handling an event


The previous procedure steps demonstrated how to just create a basic Windows Form that compiles and runs. The
next procedure will show you how to create and add a control to the form, and handle an event for the control. For
more information about the controls you can add to Windows Forms, see Windows Forms Controls.
In addition to understanding how to create Windows Forms applications, you should understand event-based
programming and how to handle user input. For more information, see Creating Event Handlers in Windows
Forms, and Handling User Input
To declare a button control and handle its click event
1. Declare a button control named button1 .
2. In the constructor, create the button and set its Size, Location and Text properties.
3. Add the button to the form.
The following code example demonstrates how to declare the button control:

public Button button1;


public Form1()
{
button1 = new Button();
button1.Size = new Size(40, 40);
button1.Location = new Point(30, 30);
button1.Text = "Click me";
this.Controls.Add(button1);
button1.Click += new EventHandler(button1_Click);
}
Public WithEvents button1 As Button

Public Sub New()


button1 = New Button()
button1.Size = New Size(40, 40)
button1.Location = New Point(30, 30)
button1.Text = "Click me"
Me.Controls.Add(button1)

End Sub

4. Create a method to handle the Click event for the button.


5. In the click event handler, display a MessageBox with the message, "Hello World".
The following code example demonstrates how to handle the button control's click event:

private void button1_Click(object sender, EventArgs e)


{
MessageBox.Show("Hello World");
}

Private Sub button1_Click(ByVal sender As Object, _


ByVal e As EventArgs) Handles button1.Click
MessageBox.Show("Hello World")
End Sub

6. Associate the Click event with the method you created.


The following code example demonstrates how to associate the event with the method.

button1.Click += new EventHandler(button1_Click);

Private Sub button1_Click(ByVal sender As Object, _


ByVal e As EventArgs) Handles button1.Click

7. Compile and run the application as described in the previous procedure.

Example
The following code example is the complete example from the previous procedures:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

namespace FormWithButton
{
public class Form1 : Form
{
public Button button1;
public Form1()
{
button1 = new Button();
button1.Size = new Size(40, 40);
button1.Location = new Point(30, 30);
button1.Text = "Click me";
this.Controls.Add(button1);
button1.Click += new EventHandler(button1_Click);
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("Hello World");
}
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new Form1());
}
}
}

Imports System.ComponentModel
Imports System.Drawing
Imports System.Windows.Forms

Public Class Form1


Inherits Form
Public WithEvents button1 As Button

Public Sub New()


button1 = New Button()
button1.Size = New Size(40, 40)
button1.Location = New Point(30, 30)
button1.Text = "Click me"
Me.Controls.Add(button1)

End Sub

Private Sub button1_Click(ByVal sender As Object, _


ByVal e As EventArgs) Handles button1.Click
MessageBox.Show("Hello World")
End Sub

<STAThread()> _
Shared Sub Main()
Application.EnableVisualStyles()
Application.Run(New Form1())

End Sub
End Class

See also
Form
Control
Changing the Appearance of Windows Forms
Enhancing Windows Forms Applications
Getting Started with Windows Forms
Windows Forms Coordinates
9/1/2020 • 2 minutes to read • Edit Online

The coordinate system for a Windows Form is based on device coordinates, and the basic unit of measure when
drawing in Windows Forms is the device unit (typically, the pixel). Points on the screen are described by x- and y-
coordinate pairs, with the x-coordinates increasing to the right and the y-coordinates increasing from top to
bottom. The location of the origin, relative to the screen, will vary depending on whether you are specifying screen
or client coordinates.

Screen Coordinates
A Windows Forms application specifies the position of a window on the screen in screen coordinates. For screen
coordinates, the origin is the upper-left corner of the screen. The full position of a window is often described by a
Rectangle structure containing the screen coordinates of two points that define the upper-left and lower-right
corners of the window.

Client Coordinates
A Windows Forms application specifies the position of points in a form or control using client coordinates. The
origin for client coordinates is the upper-left corner of the client area of the control or form. Client coordinates
ensure that an application can use consistent coordinate values while drawing in a form or control, regardless of
the position of the form or control on the screen.
The dimensions of the client area are also described by a Rectangle structure that contains client coordinates for
the area. In all cases, the upper-left coordinate of the rectangle is included in the client area, while the lower-right
coordinate is excluded. Graphics operations do not include the right and lower edges of a client area. For example
the FillRectangle method will fill up to the right and lower edge of the specified rectangle, but will not include these
edges.

Mapping From One Type of Coordinate to Another


Occasionally, you may need to map from screen coordinates to client coordinates. You can easily accomplish this by
using the PointToClient and PointToScreen methods available in the Control class. For example, the MousePosition
property of Control is reported in screen coordinates, but you may want to convert these to client coordinates.

See also
PointToClient
PointToScreen
Creating Event Handlers in Windows Forms
9/1/2020 • 2 minutes to read • Edit Online

An event handler is a procedure in your code that determines what actions are performed when an event occurs,
such as when the user clicks a button or a message queue receives a message. When an event is raised, the event
handler or handlers that receive the event are executed. Events can be assigned to multiple handlers, and the
methods that handle particular events can be changed dynamically. You can also use the Windows Forms
Designer in Visual Studio to create event handlers.

In This Section
Events Overview
Explains the event model and the role of delegates.
Event Handlers Overview
Describes how to handle events.
How to: Create Event Handlers at Run Time for Windows Forms
Gives directions for responding to system or user events dynamically.
How to: Connect Multiple Events to a Single Event Handler in Windows Forms
Gives directions for assigning the same functionality to multiple controls through events.
Order of Events in Windows Forms
Describes the order in which events are raised in Windows Forms controls.
How to: Create Event Handlers Using the Designer Describes how to use the Windows Forms Designer to create
event handlers.

Related Sections
Events
Provides links to topics on handling and raising events using the .NET Framework.
Troubleshooting Inherited Event Handlers in Visual Basic
Lists common issues that occur with event handlers in inherited components.
Events Overview (Windows Forms)
9/1/2020 • 2 minutes to read • Edit Online

An event is an action which you can respond to, or "handle," in code. Events can be generated by a user action,
such as clicking the mouse or pressing a key; by program code; or by the system.
Event-driven applications execute code in response to an event. Each form and control exposes a predefined set of
events that you can program against. If one of these events occurs and there is code in the associated event
handler, that code is invoked.
The types of events raised by an object vary, but many types are common to most controls. For example, most
objects will handle a Click event. If a user clicks a form, code in the form's Click event handler is executed.

NOTE
Many events occur in conjunction with other events. For example, in the course of the DoubleClick event occurring, the
MouseDown, MouseUp, and Click events occur.

For information about how to raise and consume an event, see Events.

Delegates and Their Role


Delegates are classes commonly used within the .NET Framework to build event-handling mechanisms. Delegates
roughly equate to function pointers, commonly used in Visual C++ and other object-oriented languages. Unlike
function pointers however, delegates are object-oriented, type-safe, and secure. In addition, where a function
pointer contains only a reference to a particular function, a delegate consists of a reference to an object, and
references to one or more methods within the object.
This event model uses delegates to bind events to the methods that are used to handle them. The delegate enables
other classes to register for event notification by specifying a handler method. When the event occurs, the delegate
calls the bound method. For more information about how to define delegates, see Events.
Delegates can be bound to a single method or to multiple methods, referred to as multicasting. When creating a
delegate for an event, you (or the Windows) typically create a multicast event. A rare exception might be an event
that results in a specific procedure (such as displaying a dialog box) that would not logically repeat multiple times
per event. For information about how to create a multicast delegate, see How to combine delegates (Multicast
Delegates).
A multicast delegate maintains an invocation list of the methods it is bound to. The multicast delegate supports a
Combine method to add a method to the invocation list and a Remove method to remove it.
When an event is recorded by the application, the control raises the event by invoking the delegate for that event.
The delegate in turn calls the bound method. In the most common case (a multicast delegate) the delegate calls
each bound method in the invocation list in turn, which provides a one-to-many notification. This strategy means
that the control does not need to maintain a list of target objects for event notification—the delegate handles all
registration and notification.
Delegates also enable multiple events to be bound to the same method, allowing a many-to-one notification. For
example, a button-click event and a menu-command–click event can both invoke the same delegate, which then
calls a single method to handle these separate events the same way.
The binding mechanism used with delegates is dynamic: a delegate can be bound at run time to any method
whose signature matches that of the event handler. With this feature, you can set up or change the bound method
depending on a condition and to dynamically attach an event handler to a control.

See also
Creating Event Handlers in Windows Forms
Event Handlers Overview
Event Handlers Overview (Windows Forms)
9/1/2020 • 2 minutes to read • Edit Online

An event handler is a method that is bound to an event. When the event is raised, the code within the event
handler is executed. Each event handler provides two parameters that allow you to handle the event properly. The
following example shows an event handler for a Button control's Click event.

Private Sub button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles button1.Click

End Sub

private void button1_Click(object sender, System.EventArgs e)


{

private:
void button1_Click(System::Object ^ sender,
System::EventArgs ^ e)
{

The first parameter, sender , provides a reference to the object that raised the event. The second parameter, e , in
the example above, passes an object specific to the event that is being handled. By referencing the object's
properties (and, sometimes, its methods), you can obtain information such as the location of the mouse for mouse
events or data being transferred in drag-and-drop events.
Typically each event produces an event handler with a different event-object type for the second parameter. Some
event handlers, such as those for the MouseDown and MouseUp events, have the same object type for their
second parameter. For these types of events, you can use the same event handler to handle both events.
You can also use the same event handler to handle the same event for different controls. For example, if you have
a group of RadioButton controls on a form, you could create a single event handler for the Click event and have
each control's Click event bound to the single event handler. For more information, see How to: Connect Multiple
Events to a Single Event Handler in Windows Forms.

See also
Creating Event Handlers in Windows Forms
Events Overview
How to: Create Event Handlers at Run Time for
Windows Forms
9/1/2020 • 2 minutes to read • Edit Online

In addition to creating events using the Windows Forms Designer in Visual Studio, you can also create an event
handler at run time. This action allows you to connect event handlers based on conditions in code at run time as
opposed to having them connected when the program initially starts.

Create an event handler at run time


1. Open the form that you want to add an event handler to.
2. Add a method to your form with the method signature for the event that you want to handle.
For example, if you were handling the Click event of a Button control, you would create a method such as
the following:

Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs)


' Add event handler code here.
End Sub

private void button1_Click(object sender, System.EventArgs e)


{
// Add event handler code here.
}

private:
void button1_Click(System::Object ^ sender,
System::EventArgs ^ e)
{
// Add event handler code here.
}

3. Add code to the event handler as appropriate to your application.


4. Determine which form or control you want to create an event handler for.
5. In a method within your form's class, add code that specifies the event handler to handle the event. For
example, the following code specifies the event handler button1_Click handles the Click event of a Button
control:

AddHandler Button1.Click, AddressOf Button1_Click

button1.Click += new EventHandler(button1_Click);

button1->Click += gcnew System::EventHandler(this, &Form1::button1_Click);

The AddHandler method demonstrated in the Visual Basic code above establishes a click event handler for
the button.

See also
Creating Event Handlers in Windows Forms
Event Handlers Overview
Troubleshooting Inherited Event Handlers in Visual Basic
How to: Connect Multiple Events to a Single Event
Handler in Windows Forms
9/1/2020 • 2 minutes to read • Edit Online

In your application design, you may find it necessary to use a single event handler for multiple events or have
multiple events perform the same procedure. For example, it is often a powerful time-saver to have a menu
command raise the same event as a button on your form does if they expose the same functionality. You can do
this by using the Events view of the Properties window in C# or using the Handles keyword and the Class Name
and Method Name drop-down boxes in the Visual Basic Code Editor.
To connect multiple events to a single event handler in Visual Basic
1. Right-click the form and choose View Code .
2. From the Class Name drop-down box, select one of the controls that you want to have the event handler
handle.
3. From the Method Name drop-down box, select one of the events that you want the event handler to
handle.
4. The Code Editor inserts the appropriate event handler and positions the insertion point within the method.
In the example below, it is the Click event for the Button control.

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles


Button1.Click
' Add event-handler code here.
End Sub

5. Append the other events you would like handled to the Handles clause.

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles


Button1.Click, Button2.Click
' Add event-handler code here.
End Sub

6. Add the appropriate code to the event handler.


To connect multiple events to a single event handler in C#
1. Select the control to which you want to connect an event handler.

2. In the Properties window, click the Events button ( ).


3. Click the name of the event that you want to handle.
4. In the value section next to the event name, click the drop-down button to display a list of existing event
handlers that match the method signature of the event you want to handle.
5. Select the appropriate event handler from the list.
Code will be added to the form to bind the event to the existing event handler.

See also
Creating Event Handlers in Windows Forms
Event Handlers Overview
Order of Events in Windows Forms
9/1/2020 • 2 minutes to read • Edit Online

The order in which events are raised in Windows Forms applications is of particular interest to developers
concerned with handling each of these events in turn. When a situation calls for meticulous handling of events,
such as when you are redrawing parts of the form, an awareness of the precise order in which events are raised at
run time is necessary. This topic provides some details on the order of events during several important stages in
the lifetime of applications and controls. For specific details about the order of mouse input events, see Mouse
Events in Windows Forms. For an overview of events in Windows Forms, see Events Overview. For details about
the makeup of event handlers, see Event Handlers Overview.

Application Startup and Shutdown Events


The Form and Control classes expose a set of events related to application startup and shutdown. When a Windows
Forms application starts, the startup events of the main form are raised in the following order:
Control.HandleCreated
Control.BindingContextChanged
Form.Load
Control.VisibleChanged
Form.Activated
Form.Shown
When an application closes, the shutdown events of the main form are raised in the following order:
Form.Closing
Form.FormClosing
Form.Closed
Form.FormClosed
Form.Deactivate
The ApplicationExit event of the Application class is raised after the shutdown events of the main form.

NOTE
Visual Basic 2005 includes additional application events, such as WindowsFormsApplicationBase.Startup and
WindowsFormsApplicationBase.Shutdown.

Focus and Validation Events


When you change the focus by using the keyboard (TAB, SHIFT+TAB, and so on), by calling the Select or
SelectNextControl methods, or by setting the ActiveControl property to the current form, focus events of the
Control class occur in the following order:
Enter
GotFocus
Leave
Validating
Validated
LostFocus
When you change the focus by using the mouse or by calling the Focus method, focus events of the Control class
occur in the following order:
Enter
GotFocus
LostFocus
Leave
Validating
Validated

See also
Creating Event Handlers in Windows Forms
Adjusting the size and scale of Windows Forms
9/1/2020 • 2 minutes to read • Edit Online

This topic provides links to information about resizing Windows Forms.

In This Section
How to: Resize Windows Forms
Provides instructions for specifying the size of Windows Forms.
Automatic Scaling in Windows Forms
Discusses how automatic scaling enables a form and its controls to be displayed appropriately between machines.
High DPI Support in Windows Forms Discusses Windows Forms' support for High DPI and dynamic scaling.

Reference
Size
Describes this class and has links to all of its members.
TableLayoutPanel
Describes this class and has links to all of its members.
FlowLayoutPanel
Describes this class and has links to all of its members.

Related sections
Changing the appearance of Windows Forms
Provides links to topics describing other ways to change the appearance of Windows Forms.
How to: Resize Windows Forms
9/1/2020 • 2 minutes to read • Edit Online

You can specify the size of your Windows Form in several ways. You can change both the height and the width of
the form programmatically by setting a new value for the Size property, or adjust the Height or Width properties
individually. If you're using Visual Studio, you can change the size using the Windows Forms Designer. Also see
How to: Resize Windows Forms Using the Designer.

Resize a form programmatically


Define the size of a form at run time by setting the Size property of the form.
The following code example shows the form size set to 100 × 100 pixels.

Form1.Size = New System.Drawing.Size(100, 100)

Form1.Size = new System.Drawing.Size(100, 100);

Form1->Size = System::Drawing::Size(100, 100);

Change form width and height programmatically


After the Size is defined, change either the form height or width by using the Width or Height properties.
The following code example shows the width of the form set to 300 pixels from the left edge of the form, whereas
the height stays constant.

Form1.Width = 300

Form1.Width = 300;

Form1->Width = 300;

-or-
Change Width or Height by setting the Size property.
However, as the following code example shows, this approach is more cumbersome than just setting Width or
Height properties.

Form1.Size = New Size(300, Form1.Size.Height)

Form1.Size = new Size(300, Form1.Size.Height);


Form1->Size = System::Drawing::Size(300, Form1->Size.Height);

Change form size by increments programmatically


To increment the size of the form, set the Width and Height properties.
The following code example shows the width of the form set to 200 pixels wider than the current setting.

Form1.Width += 200

Form1.Width += 200;

Form1->Width += 200;

Cau t i on

Always use the Height or Width property to change a dimension of a form, unless you are setting both height and
width dimensions at the same time by setting the Size property to a new Size structure. The Size property returns a
Size structure, which is a value type. You cannot assign a new value to the property of a value type. Therefore, the
following code example will not compile.

' NOTE: CODE WILL NOT COMPILE


Dim f As New Form()
f.Size.Width += 100

// NOTE: CODE WILL NOT COMPILE


Form f = new Form();
f.Size.Width += 100;

// NOTE: CODE WILL NOT COMPILE


Form^ f = gcnew Form();
f->Size->X += 100;

See also
Getting Started with Windows Forms
Enhancing Windows Forms Applications
Automatic scaling in Windows Forms
9/1/2020 • 6 minutes to read • Edit Online

Automatic scaling enables a form and its controls, designed on one machine with a certain display resolution or
system font, to be displayed appropriately on another machine with a different display resolution or system font. It
assures that the form and its controls will intelligently resize to be consistent with native windows and other
applications on both the users' and other developers' machines. The support of the .NET Framework for automatic
scaling and visual styles enables .NET Framework applications to maintain a consistent look and feel when
compared to native Windows applications on each user's machine.
For the most part, automatic scaling works as expected in .NET Framework version 2.0 and later. However, font
scheme changes can be problematic. For an example of how to resolve this, see How to: Respond to Font Scheme
Changes in a Windows Forms Application.

Need for automatic scaling


Without automatic scaling, an application designed for one display resolution or font will either appear too small
or too large when that resolution or font is changed. For example, if the application is designed using Tahoma 9
point as a baseline, without adjustment it will appear too small if run on a machine where the system font is
Tahoma 12 point. Text elements, such as titles, menus, text box contents, and so on will render smaller than other
applications. Furthermore, the size of user interface (UI) elements that contain text, such as the title bar, menus, and
many controls are dependent on the font used. In this example, these elements will also appear relatively smaller.
An analogous situation occurs when an application is designed for a certain display resolution. The most common
display resolution is 96 dots per inch (DPI), which equals 100% display scaling, but higher resolution displays
supporting 125%, 150%, 200% (which respectively equal 120, 144 and 192 DPI) and above are becoming more
common. Without adjustment, an application, especially a graphics-based one, designed for one resolution will
appear either too large or too small when run at another resolution.
Automatic scaling seeks to ameliorate these problems by automatically resizing the form and its child controls
according to the relative font size or display resolution. The Windows operating system supports automatic scaling
of dialog boxes using a relative unit of measurement called dialog units. A dialog unit is based on the system font
and its relationship to pixels can be determined though the Win32 SDK function GetDialogBaseUnits . When a user
changes the theme used by Windows, all dialog boxes are automatically adjusted accordingly. In addition, the .NET
Framework supports automatic scaling either according to the default system font or the display resolution.
Optionally, automatic scaling can be disabled in an application.

Original support for automatic scaling


Versions 1.0 and 1.1 of the .NET Framework supported automatic scaling in a straightforward manner that was
dependent on the Windows default font used for the UI, represented by the Win32 SDK value
DEFAULT_GUI_FONT . This font is typically only changed when the display resolution changes. The following
mechanism was used to implement automatic scaling:
1. At design time, the AutoScaleBaseSize property (which is now deprecated) was set to the height and width
of the default system font on the developer's machine.
2. At runtime, the default system font of the user's machine was used to initialize the Font property of the
Form class.
3. Before displaying the form, the ApplyAutoScaling method was called to scale the form. This method
calculated the relative scale sizes from AutoScaleBaseSize and Font then called the Scale method to actually
scale the form and its children.
4. The value of AutoScaleBaseSize was updated so that subsequent calls to ApplyAutoScaling did not
progressively resize the form.
While this mechanism was sufficient for most purposes, it suffered from the following limitations:
Since the AutoScaleBaseSize property represents the baseline font size as integer values, rounding errors
occur that become evident when a form is cycled through multiple resolutions.
Automatic scaling was implemented in only the Form class, not in the ContainerControl class. As a result,
user controls would scale correctly only when the user control was designed at the same resolution as the
form, and it was placed in the form at design time.
Forms and their child controls could only be concurrently designed by multiple developers if their machine
resolutions were the same. Likewise it also made inheritance of a form dependent on the resolution
associated with the parent form.
It is not compatible with the newer layout managers introduced with the .NET Framework version 2.0, such
as FlowLayoutPanel and TableLayoutPanel.
It did not support scaling based directly on the display resolution that is required for compatibility to the
.NET Compact Framework.
Although this mechanism is preserved in the .NET Framework version 2.0 to maintain backward compatibility, it
has been superseded by the more robust scaling mechanism described next. As a consequence, the AutoScale,
ApplyAutoScaling, AutoScaleBaseSize, and certain Scale overloads are marked as obsolete.

NOTE
You can safely delete references to these members when you upgrade your legacy code to the .NET Framework version 2.0.

Current support for automatic scaling


The .NET Framework version 2.0 surmounts previous limitations by introducing the following changes to the
automatic scaling of Windows Forms:
Base support for scaling has been moved to the ContainerControl class so that forms, native composite
controls and user controls all receive uniform scaling support. The new members AutoScaleFactor,
AutoScaleDimensions, AutoScaleMode and PerformAutoScale have been added.
The Control class also has several new members that allow it to participate in scaling and to support mixed
scaling on the same form. Specifically the Scale, ScaleChildren, and GetScaledBounds members support
scaling.
Support for scaling based upon the screen resolution has been added to complement system font support,
as defined by the AutoScaleMode enumeration. This mode is compatible with automatic scaling supported
by the .NET Compact Framework enabling easier application migration.
Compatibility with layout managers such as FlowLayoutPanel and TableLayoutPanel has been added to the
implementation of automatic scaling.
Scaling factors are now represented as floating point values, typically using the SizeF structure, so that
rounding errors have been practically eliminated.
Cau t i on

Arbitrary mixtures of DPI and font scaling modes are not supported. Although you may scale a user control using
one mode (for example, DPI) and place it on a form using another mode (Font) with no issues, but mixing a base
form in one mode and a derived form in another can lead to unexpected results.
Automatic scaling in action
Windows Forms now uses the following logic to automatically scale forms and their contents:
1. At design time, each ContainerControl records the scaling mode and it current resolution in the
AutoScaleMode and AutoScaleDimensions, respectively.
2. At run time, the actual resolution is stored in the CurrentAutoScaleDimensions property. The
AutoScaleFactor property dynamically calculates the ratio between the run-time and design-time scaling
resolution.
3. When the form loads, if the values of CurrentAutoScaleDimensions and AutoScaleDimensions are different,
then the PerformAutoScale method is called to scale the control and its children. This method suspends
layout and calls the Scale method to perform the actual scaling. Afterwards, the value of
AutoScaleDimensions is updated to avoid progressive scaling.
4. PerformAutoScale is also automatically invoked in the following situations:
In response to the OnFontChanged event if the scaling mode is Font.
When the layout of the container control resumes and a change is detected in the
AutoScaleDimensions or AutoScaleMode properties.
As implied above, when a parent ContainerControl is being scaled. Each container control is
responsible for scaling its children using its own scaling factors and not the one from its parent
container.
5. Child controls can modify their scaling behavior through several means:
The ScaleChildren property can be overridden to determine if their child controls should be scaled or
not.
The GetScaledBounds method can be overridden to adjust the bounds that the control is scaled to,
but not the scaling logic.
The ScaleControl method can be overridden to change the scaling logic for the current control.

See also
AutoScaleMode
Scale
PerformAutoScale
AutoScaleDimensions
Rendering Controls with Visual Styles
How to: Improve Performance by Avoiding Automatic Scaling
How to: Respond to Font Scheme Changes in a
Windows Forms Application
9/1/2020 • 3 minutes to read • Edit Online

In the Windows operating systems, a user can change the system-wide font settings to make the default font
appear larger or smaller. Changing these font settings is critical for users who are visually impaired and require
larger type to read the text on their screens. You can adjust your Windows Forms application to react to these
changes by increasing or decreasing the size of the form and all contained text whenever the font scheme changes.
If you want your form to accommodate changes in font sizes dynamically, you can add code to your form.
Typically, the default font used by Windows Forms is the font returned by the Microsoft.Win32 namespace call to
GetStockObject(DEFAULT_GUI_FONT) . The font returned by this call only changes when the screen resolution changes.
As shown in the following procedure, your code must change the default font to IconTitleFont to respond to
changes in font size.
To use the desktop font and respond to font scheme changes
1. Create your form, and add the controls you want to it. For more information, see How to: Create a Windows
Forms Application from the Command Line and Controls to Use on Windows Forms.
2. Add a reference to the Microsoft.Win32 namespace to your code.

using Microsoft.Win32;

Imports Microsoft.Win32

3. Add the following code to the constructor of your form to hook up required event handlers, and to change
the default font in use for the form.

this.Font = SystemFonts.IconTitleFont;
SystemEvents.UserPreferenceChanged += new
UserPreferenceChangedEventHandler(SystemEvents_UserPreferenceChanged);
this.FormClosing += new FormClosingEventHandler(Form1_FormClosing);

Public Sub New()


' This call is required by the Windows Form Designer.
InitializeComponent()

' Add any initialization after the InitializeComponent() call.


AddHandler SystemEvents.UserPreferenceChanged, New UserPreferenceChangedEventHandler(AddressOf
SystemEvents_UserPreferenceChangesEventHandler)
End Sub

4. Implement a handler for the UserPreferenceChanged event that causes the form to scale automatically when
the Window category changes.
void SystemEvents_UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e)
{
if (e.Category == UserPreferenceCategory.Window)
{
this.Font = SystemFonts.IconTitleFont;
}
}

Private Sub SystemEvents_UserPreferenceChangesEventHandler(ByVal sender As Object, ByVal e As


UserPreferenceChangedEventArgs)
If (e.Category = UserPreferenceCategory.Window) Then
Me.Font = SystemFonts.IconTitleFont
End If
End Sub

5. Finally, implement a handler for the FormClosing event that detaches the UserPreferenceChanged event
handler.

IMPORTANT
Failure to include this code will cause your application to leak memory.

void Form1_FormClosing(object sender, FormClosingEventArgs e)


{
SystemEvents.UserPreferenceChanged -= new
UserPreferenceChangedEventHandler(SystemEvents_UserPreferenceChanged);
}

Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As


System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
RemoveHandler SystemEvents.UserPreferenceChanged, New UserPreferenceChangedEventHandler(AddressOf
SystemEvents_UserPreferenceChangesEventHandler)
End Sub

6. Compile and run the code.


To manually change the font scheme in Windows XP
1. While your Windows Forms application is running, right-click the Windows desktop and choose Proper ties
from the shortcut menu.
2. In the Display Proper ties dialog box, click the Appearance tab.
3. From the Font Size drop-down list box, select a new font size.
You'll notice that the form now reacts to run-time changes in the desktop font scheme. When the user
changes between Normal , Large Fonts , and Extra Large Fonts , the form changes font and scales
correctly.

Example
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.Win32;

namespace WinFormsAutoScaling
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();

this.Font = SystemFonts.IconTitleFont;
SystemEvents.UserPreferenceChanged += new
UserPreferenceChangedEventHandler(SystemEvents_UserPreferenceChanged);
this.FormClosing += new FormClosingEventHandler(Form1_FormClosing);
}

void SystemEvents_UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e)


{
if (e.Category == UserPreferenceCategory.Window)
{
this.Font = SystemFonts.IconTitleFont;
}
}

void Form1_FormClosing(object sender, FormClosingEventArgs e)


{
SystemEvents.UserPreferenceChanged -= new
UserPreferenceChangedEventHandler(SystemEvents_UserPreferenceChanged);
}
}
}

Imports Microsoft.Win32

Public Class Form1


Public Sub New()
' This call is required by the Windows Form Designer.
InitializeComponent()

' Add any initialization after the InitializeComponent() call.


AddHandler SystemEvents.UserPreferenceChanged, New UserPreferenceChangedEventHandler(AddressOf
SystemEvents_UserPreferenceChangesEventHandler)
End Sub

Private Sub SystemEvents_UserPreferenceChangesEventHandler(ByVal sender As Object, ByVal e As


UserPreferenceChangedEventArgs)
If (e.Category = UserPreferenceCategory.Window) Then
Me.Font = SystemFonts.IconTitleFont
End If
End Sub

Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As


System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
RemoveHandler SystemEvents.UserPreferenceChanged, New UserPreferenceChangedEventHandler(AddressOf
SystemEvents_UserPreferenceChangesEventHandler)
End Sub
End Class
The constructor in this code example contains a call to InitializeComponent , which is defined when you create a
new Windows Forms project in Visual Studio. Remove this line of code if you are building your application on the
command line.

See also
PerformAutoScale
Automatic Scaling in Windows Forms
High DPI support in Windows Forms
9/1/2020 • 3 minutes to read • Edit Online

Starting with the .NET Framework 4.7, Windows Forms includes enhancements for common high DPI and dynamic
DPI scenarios. These include:
Improvements in the scaling and layout of a number of Windows Forms controls, such as the
MonthCalendar control and the CheckedListBox control.
Single-pass scaling. In the .NET Framework 4.6 and earlier versions, scaling was performed through multiple
passes, which caused some controls to be scaled more than was necessary.
Support for dynamic DPI scenarios in which the user changes the DPI or scale factor after a Windows Forms
application has been launched.
In versions of the .NET Framework starting with the .NET Framework 4.7, enhanced high DPI support is an opt-in
feature. You must configure your application to take advantage of it.

Configuring your Windows Forms app for high DPI support


The new Windows Forms features that support high DPI awareness are available only in applications that target
the .NET Framework 4.7 and are running on Windows operating systems starting with the Windows 10 Creators
Update.
In addition, to configure high DPI support in your Windows Forms application, you must do the following:
Declare compatibility with Windows 10.
To do this, add the following to your manifest file:

<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 compatibility -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>

Enable per-monitor DPI awareness in the app.config file.


Windows Forms introduces a new <System.Windows.Forms.ApplicationConfigurationSection> element to
support new features and customizations added starting with the .NET Framework 4.7. To take advantage of
the new features that support high DPI, add the following to your application configuration file.

<System.Windows.Forms.ApplicationConfigurationSection>
<add key="DpiAwareness" value="PerMonitorV2" />
</System.Windows.Forms.ApplicationConfigurationSection>

IMPORTANT
In previous versions of the .NET Framework, you used the manifest to add high DPI support. This approach is no
longer recommended, since it overrides settings defined on the app.config file.
Call the static EnableVisualStyles method.
This should be the first method call in your application entry point. For example:

static void Main()


{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form2());
}

Opting out of individual high DPI features


Setting the DpiAwareness value to PerMonitorV2 enables all high DPI awareness features supported by .NET
Framework versions starting with the .NET Framework 4.7. Typically, this is adequate for most Windows Forms
applications. However, you may want to opt out of one or more individual features. The most important reason for
doing this is that your existing application code already handles that feature. For example, if your application
handles auto scaling, you might want to disable the auto-resizing feature as follows:

<System.Windows.Forms.ApplicationConfigurationSection>
<add key="DpiAwareness" value="PerMonitorV2" />
<add key="EnableWindowsFormsHighDpiAutoResizing" value="false" />
</System.Windows.Forms.ApplicationConfigurationSection>

For a list of individual keys and their values, see Windows Forms Add Configuration Element.

New DPI change events


Starting with the .NET Framework 4.7, three new events allow you to programmatically handle dynamic DPI
changes:
DpiChangedAfterParent, which is fired when the DPI setting for a control is changed programmatically after a
DPI change event for it's parent control or form has occurred.
DpiChangedBeforeParent, which is fired when the DPI setting for a control is changed programmatically before
a DPI change event for its parent control or form has occurred.
DpiChanged, which is fired when the DPI setting changes on the display device where the form is currently
displayed.

New helper methods and properties


The .NET Framework 4.7 also adds a number of new helper methods and properties that provide information
about DPI scaling and allow you to perform DPI scaling. These include:
LogicalToDeviceUnits, which converts a value from logical to device pixels.
ScaleBitmapLogicalToDevice, which scales a bitmap image to the logical DPI for a device.
DeviceDpi, which returns the DPI for the current device.

Versioning considerations
In addition to running on .NET Framework 4.7 and Windows 10 Creators Update, your application may also run in
an environment in which it isn't compatible with high DPI improvements. In this case, you'll need to develop a
fallback for your application. You can do this to perform custom drawing to handle scaling.
To do this, you also need to determine the operating system on which your app is running. You can do that with
code like the following:

// Create a reference to the OS version of Windows 10 Creators Update.


Version OsMinVersion = new Version(10, 0, 15063, 0);

// Access the platform/version of the current OS.


Console.WriteLine(Environment.OSVersion.Platform.ToString());
Console.WriteLine(Environment.OSVersion.VersionString);

// Compare the current version to the minimum required version.


Console.WriteLine(Environment.OSVersion.Version.CompareTo(OsMinVersion));

Note that your application won't successfully detect Windows 10 if it wasn't listed as a supported operating system
in the application manifest.
You can also check the version of the .NET Framework that the application was built against:

Console.WriteLine(AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName);

See also
Windows Forms Add Configuration Element
Adjusting the Size and Scale of Windows Forms
Changing the Appearance of Windows Forms
9/1/2020 • 2 minutes to read • Edit Online

You can customize the look of your Windows Forms applications in many different ways, such as changing the
border, opacity, shape, style, or setting a background image for your Windows Forms application.

In This Section
How to: Change the Borders of Windows Forms
Shows how to change the border style of a form.

Reference
Form
Describes this class and has links to all of its members.
FormBorderStyle
Describes this enumeration and contains descriptions of all of its members.
VisualStyleRenderer
Describes this class and has links to all of its members.
Image
Describes this class and has links to all of its members.
Region
Describes this class and has links to all of its members.
Color
Describes this class and has links to all of its members.

Related Sections
Adjusting the Size and Scale of Windows Forms
Contains links to topics that show how to change the size and scale of a form.
Graphics and Drawing in Windows Forms
Contains links to topics that describe how to perform custom drawing on Windows Forms.
Controls with Built-In Owner-Drawing Support
List owner-draw support in Windows Forms controls.
How to: Change the Borders of Windows Forms
9/1/2020 • 2 minutes to read • Edit Online

You have several border styles to choose from when you are determining the appearance and behavior of your
Windows Forms. By changing the FormBorderStyle property, you can control the resizing behavior of the form. In
addition, setting the FormBorderStyle affects how the caption bar is displayed as well as what buttons might
appear on it. For more information, see FormBorderStyle.
There is extensive support for this task in Visual Studio.
See also How to: Change the Borders of Windows Forms Using the Designer.
To set the border style of Windows Forms programmatically
Set the FormBorderStyle property to the style you want. The following code example sets the border style of
form DlgBx1 to FixedDialog.

DlgBx1.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog

DlgBx1.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;

DlgBx1->FormBorderStyle =
System::Windows::Forms::FormBorderStyle::FixedDialog;

Also see How to: Create Dialog Boxes at Design Time.


Additionally, if you have chosen a border style for the form that provides optional Minimize and Maximize
buttons, you can specify whether you want either or both of these buttons to be functional. These buttons
are useful when you want to closely control the user experience. The Minimize and Maximize buttons are
enabled by default, and their functionality is manipulated through the Proper ties window.

See also
FormBorderStyle
FixedDialog
Getting Started with Windows Forms
Windows Forms controls
9/1/2020 • 2 minutes to read • Edit Online

As you design and modify the user interface of your Windows Forms applications, you will need to add, align, and
position controls. Controls are objects that are contained within form objects. Each type of control has its own set
of properties, methods, and events that make it suitable for a particular purpose. You can manipulate controls in
the designer and write code to add controls dynamically at run time.

In this section
Putting Controls on Windows Forms
Provides links related to putting controls on forms.
Arranging Controls on Windows Forms
Articles related to arranging controls on forms.
Labeling Individual Windows Forms Controls and Providing Shortcuts to Them
Describes the uses of keyboard shortcuts, text labels on controls, and modifier keys.
Controls to Use on Windows Forms
Lists the controls that work with Windows Forms, and basic things you can accomplish with each control.
Developing Custom Windows Forms Controls with the .NET Framework
Provides background information and samples to help users develop custom Windows Forms controls.
Developing Windows Forms Controls at Design Time
Describes techniques for creating custom controls through design and inheritance.

Related sections
Client Applications
Provides an overview of developing Windows-based applications.
User Input in Windows Forms
9/1/2020 • 2 minutes to read • Edit Online

Windows Forms includes a user input model based on events that are raised while processing related Windows
messages. The topics in this section provide information on mouse and keyboard user input, including code
examples that demonstrate how to perform specific tasks.

In This Section
User Input in a Windows Forms Application
Provides an overview of user input events and the methods that process Windows messages.
Keyboard Input in a Windows Forms Application
Provides information on keyboard message handling, the different types of keys, and the keyboard events.
Mouse Input in a Windows Forms Application
Provides information on the mouse events and other mouse-related features, including mouse cursors and mouse
capture.
How to: Simulate Mouse and Keyboard Events in Code
Demonstrates several different ways to programmatically simulate mouse and keyboard input.
How to: Handle User Input Events in Windows Forms Controls
Presents a code example that handles most user input events and reports information about each event.
User Input Validation in Windows Forms
Describes the methods to validate user input in Windows Forms applications.

Related Sections
Also see Creating Event Handlers in Windows Forms.
User Input in a Windows Forms Application
9/1/2020 • 2 minutes to read • Edit Online

In Windows Forms, user input is sent to applications in the form of Windows messages. A series of overridable
methods process these messages at the application, form, and control level. When these methods receive mouse
and keyboard messages, they raise events that can be handled to get information about the mouse or keyboard
input. In many cases, Windows Forms applications will be able to process all user input simply by handling these
events. In other cases, an application may need to override one of the methods that process messages in order to
intercept a particular message before it is received by the application, form, or control.

Mouse and Keyboard Events


All Windows Forms controls inherit a set of events related to mouse and keyboard input. For example, a control
can handle the KeyPress event to determine the character code of a key that was pressed, or a control can handle
the MouseClick event to determine the location of a mouse click. For more information on the mouse and
keyboard events, see Using Keyboard Events and Mouse Events in Windows Forms.

Methods that Process User Input Messages


Forms and controls have access to the IMessageFilter interface and a set of overridable methods that process
Windows messages at different points in the message queue. These methods all have a Message parameter, which
encapsulates the low-level details of Windows messages. You can implement or override these methods to
examine the message and then either consume the message or pass it on to the next consumer in the message
queue. The following table presents the methods that process all Windows messages in Windows Forms.

M ET H O D N OT ES

PreFilterMessage This method intercepts queued (also known as posted)


Windows messages at the application level.

PreProcessMessage This method intercepts Windows messages at the form and


control level before they have been processed.

WndProc This method processes Windows messages at the form and


control level.

DefWndProc This method performs the default processing of Windows


messages at the form and control level. This provides the
minimal functionality of a window.

OnNotifyMessage This method intercepts messages at the form and control


level, after they have been processed. The
EnableNotifyMessage style bit must be set for this method to
be called.

Keyboard and mouse messages are also processed by an additional set of overridable methods that are specific to
those types of messages. For more information, see How Keyboard Input Works and How Mouse Input Works in
Windows Forms.

See also
User Input in Windows Forms
Keyboard Input in a Windows Forms Application
Mouse Input in a Windows Forms Application
Keyboard Input in a Windows Forms Application
9/1/2020 • 2 minutes to read • Edit Online

Windows Forms includes standard keyboard events that allow you to respond to specific key presses, and also
provides ways for you to intercept, modify, and consume key presses at the application, form, and control level.

In This Section
How Keyboard Input Works
Describes how keyboard messages are processed and transformed into keyboard events.
Using Keyboard Events
Provides information on the types of keyboard events and the information that is received by the keyboard event
handlers.
How to: Modify Keyboard Input to a Standard Control
Presents a code example that shows how to modify key values before they reach a control.
How to: Determine Which Modifier Key Was Pressed
Demonstrates how to find out whether SHIFT, ALT, or CTRL was pressed in addition to another key.
How to: Handle Keyboard Input at the Form Level
Presents a code example that shows how to intercept keys before they reach a control.
How Keyboard Input Works
9/1/2020 • 5 minutes to read • Edit Online

Windows Forms processes keyboard input by raising keyboard events in response to Windows messages. Most
Windows Forms applications process keyboard input exclusively by handling the keyboard events. However, you
need to understand how keyboard messages work so you can implement more advanced keyboard-input
scenarios, such as intercepting keys before they reach a control. This topic describes the types of key data that
Windows Forms recognizes and provides an overview of how keyboard messages are routed. For information
about keyboard events, see Using Keyboard Events.

Types of Keys
Windows Forms identifies keyboard input as virtual-key codes that are represented by the bitwise Keys
enumeration. With the Keys enumeration, you can combine a series of pressed keys to result in a single value.
These values correspond to the values that accompany the WM_KEYDOWN and WM_SYSKEYDOWN Windows
messages. You can detect most physical key presses by handling the KeyDown or KeyUp events. Character keys
are a subset of the Keys enumeration and correspond to the values that accompany the WM_CHAR and
WM_SYSCHAR Windows messages. If the combination of pressed keys results in a character, you can detect the
character by handling the KeyPress event. Alternatively, you can use Keyboard, exposed by Visual Basic
programming interface, to discover which keys were pressed and send keys. For more information, see Accessing
the Keyboard.

Order of Keyboard Events


As listed previously, there are 3 keyboard related events that can occur on a control. The following sequence
shows the general order of the events:
1. The user pushes the "a" key, the key is preprocessed, dispatched, and a KeyDown event occurs.
2. The user holds the "a" key, the key is preprocessed, dispatched, and a KeyPress event occurs.
This event occurs multiple times as the user holds a key.
3. The user releases the "a" key, the key is preprocessed, dispatched and a KeyUp event occurs.

Preprocessing Keys
Like other messages, keyboard messages are processed in the WndProc method of a form or control. However,
before keyboard messages are processed, the PreProcessMessage method calls one or more methods that can be
overridden to handle special character keys and physical keys. You can override these methods to detect and filter
certain keys before the messages are processed by the control. The following table shows the action that is being
performed and the related method that occurs, in the order that the method occurs.
Preprocessing for a KeyDown event
A C T IO N REL AT ED M ET H O D N OT ES
A C T IO N REL AT ED M ET H O D N OT ES

Check for a command key such as an ProcessCmdKey This method processes a command key,
accelerator or menu shortcut. which takes precedence over regular
keys. If this method returns true , the
key message is not dispatched and a
key event does not occur. If it returns
false , IsInputKey is called .

Check for a special key that requires IsInputKey If the method returns true , it means
preprocessing or a normal character the control is a regular character and a
key that should raise a KeyDown event KeyDown event is raised. If false ,
and be dispatched to a control. ProcessDialogKey is called. Note: To
ensure a control gets a key or
combination of keys, you can handle
the PreviewKeyDown event and set
IsInputKey of the
PreviewKeyDownEventArgs to true
for the key or keys you want.

Check for a navigation key (ESC, TAB, ProcessDialogKey This method processes a physical key
Return, or arrow keys). that employs special functionality within
the control, such as switching focus
between the control and its parent. If
the immediate control does not handle
the key, the ProcessDialogKey is called
on the parent control and so on to the
topmost control in the hierarchy. If this
method returns true , preprocessing
is complete and a key event is not
generated. If it returns false , a
KeyDown event occurs.

Preprocessing for a KeyPress Event


A C T IO N REL AT ED M ET H O D N OT ES

Check to see the key is a normal IsInputChar If the character is a normal character,
character that should be processed by this method returns true , the
the control KeyPress event is raised and no further
preprocessing occurs. Otherwise
ProcessDialogChar will be called.

Check to see if the character is a ProcessDialogChar This method, similar to


mnemonic (such as &OK on a button) ProcessDialogKey, will be called up the
control hierarchy. If the control is a
container control, it checks for
mnemonics by calling
ProcessMnemonic on itself and its child
controls. If ProcessDialogChar returns
true , a KeyPress event does not
occur.

Processing Keyboard Messages


After keyboard messages reach the WndProc method of a form or control, they are processed by a set of methods
that can be overridden. Each of these methods returns a Boolean value specifying whether the keyboard message
has been processed and consumed by the control. If one of the methods returns true , then the message is
considered handled, and it is not passed to the control's base or parent for further processing. Otherwise, the
message stays in the message queue and may be processed in another method in the control's base or parent. The
following table presents the methods that process keyboard messages.

M ET H O D N OT ES

ProcessKeyMessage This method processes all keyboard messages that are


received by the WndProc method of the control.

ProcessKeyPreview This method sends the keyboard message to the control's


parent. If ProcessKeyPreview returns true , no key event is
generated, otherwise ProcessKeyEventArgs is called.

ProcessKeyEventArgs This method raises the KeyDown, KeyPress, and KeyUp


events, as appropriate.

Overriding Keyboard Methods


There are many methods available for overriding when a keyboard message is preprocessed and processed;
however, some methods are much better choices than others. Following table shows tasks you might want to
accomplish and the best way to override the keyboard methods. For more information on overriding methods, see
Overriding properties and methods in derived classes.

TA SK M ET H O D

Intercept a navigation key and raise a KeyDown event. For Override IsInputKey. Note: Alternatively, you can handle the
example you want TAB and Return to be handled in a text PreviewKeyDown event and set IsInputKey of the
box. PreviewKeyDownEventArgs to true for the key or keys you
want.

Perform special input or navigation handling on a control. For Override ProcessDialogKey


example, you want the use of arrow keys in your list control
to change the selected item.

Intercept a navigation key and raise a KeyPress event. For Override IsInputChar.
example in a spin-box control you want multiple arrow key
presses to accelerate progression through the items.

Perform special input or navigation handling during a Override ProcessDialogChar


KeyPress event. For example, in a list control holding down
the "r" key skips between items that begin with the letter r.

Perform custom mnemonic handling; for example, you want Override ProcessMnemonic.
to handle mnemonics on owner-drawn buttons contained in a
toolbar.

See also
Keys
WndProc
PreProcessMessage
My.Computer.Keyboard Object
Accessing the Keyboard
Using Keyboard Events
Using Keyboard Events
9/1/2020 • 2 minutes to read • Edit Online

Most Windows Forms programs process keyboard input by handling the keyboard events. This topic provides an
overview of the keyboard events, including details on when to use each event and the data that is supplied for
each event. Also see Event Handlers Overview (Windows Forms) and Events Overview (Windows Forms).

Keyboard Events
Windows Forms provides two events that occur when a user presses a keyboard key and one event when a user
releases a keyboard key:
The KeyDown event occurs once
The KeyPress event, which can occur multiple times when a user holds down the same key.
The KeyUp event occurs once when a user releases a key.
When a user presses a key, Windows Forms determines which event to raise based on whether the keyboard
message specifies a character key or a physical key. For more information about character and physical keys, see
How Keyboard Input Works.
The following table describes the three keyboard events.

K EY B O A RD EVEN T DESC RIP T IO N RESULT S

KeyDown This event is raised when a user presses The handler for KeyDown receives:
a physical key.
A KeyEventArgs parameter,
which provides the KeyCode
property (which specifies a
physical keyboard button).
The Modifiers property (SHIFT,
CTRL, or ALT).
The KeyData property (which
combines the key code and
modifier). The KeyEventArgs
parameter also provides:

The Handled property,


which can be set to
prevent the underlying
control from receiving
the key.
The SuppressKeyPress
property, which can be
used to suppress the
KeyPress and KeyUp
events for that
keystroke.
K EY B O A RD EVEN T DESC RIP T IO N RESULT S

KeyPress This event is raised when the key or KeyPress is raised after KeyDown.
keys pressed result in a character. For
example, a user presses SHIFT and the The handler for KeyPress
lowercase "a" keys, which result in a receives:
capital letter "A" character. A KeyPressEventArgs parameter,
which contains the character
code of the key that was
pressed. This character code is
unique for every combination of
a character key and a modifier
key.

For example, the "A" key will


generate:

The character code 65, if


it is pressed with the
SHIFT key
Or the CAPS LOCK key,
97 if it is pressed by
itself,
And 1, if it is pressed
with the CTRL key.

KeyUp This event is raised when a user The handler for KeyUp receives:
releases a physical key.
A KeyEventArgs parameter:

Which provides the


KeyCode property
(which specifies a
physical keyboard
button).
The Modifiers property
(SHIFT, CTRL, or ALT).
The KeyData property
(which combines the key
code and modifier).

See also
Keyboard Input in a Windows Forms Application
How Keyboard Input Works
Mouse Input in a Windows Forms Application
How to: Modify Keyboard Input to a Standard
Control
9/1/2020 • 7 minutes to read • Edit Online

Windows Forms provides the ability to consume and modify keyboard input. Consuming a key refers to handling a
key within a method or event handler so that other methods and events further down the message queue do not
receive the key value. Modifying a key refers to modifying the value of a key so that methods and event handlers
further down the message queue receive a different key value. This topic shows how to accomplish these tasks.
To consume a key
In a KeyPress event handler, set the Handled property of the KeyPressEventArgs class to true .
-or-
In a KeyDown event handler, set the Handled property of the KeyEventArgs class to true .

NOTE
Setting the Handled property in the KeyDown event handler does not prevent the KeyPress and KeyUp events from
being raised for the current keystroke. Use the SuppressKeyPress property for this purpose.

The following example is an excerpt from a switch statement that examines the KeyChar property of the
KeyPressEventArgs received by a KeyPress event handler. This code consumes the 'A' and 'a' character keys.

// Consume 'A' and 'a'.


case (char)65:
case (char)97:
MessageBox.Show("Control.KeyPress: '" +
e.KeyChar.ToString() + "' consumed.");
e.Handled = true;
break;

' Consume 'A' and 'a'.


Case ChrW(65), ChrW(97)
MessageBox.Show(("Control.KeyPress: '" + _
e.KeyChar.ToString() + "' consumed."))
e.Handled = True

To modify a standard character key


In a KeyPress event handler, set the KeyChar property of the KeyPressEventArgs class to the value of the new
character key.
The following example is an excerpt from a switch statement that modifies 'B' to 'A' and 'b' to 'a'. Note that
the Handled property of the KeyPressEventArgs parameter is set to false , so that the new key value is
propagated to other methods and events in the message queue.
// Detect 'B', modify it to 'A', and forward the key.
case (char)66:
MessageBox.Show("Control.KeyPress: '" +
e.KeyChar.ToString() + "' replaced by 'A'.");
e.KeyChar = (char)65;
e.Handled = false;
break;

// Detect 'b', modify it to 'a', and forward the key.


case (char)98:
MessageBox.Show("Control.KeyPress: '" +
e.KeyChar.ToString() + "' replaced by 'a'.");
e.KeyChar = (char)97;
e.Handled = false;
break;

' Modify 'B' to 'A' and forward the key.


Case ChrW(66)
MessageBox.Show(("Control.KeyPress: '" + _
e.KeyChar.ToString() + "' replaced by 'A'."))
e.KeyChar = ChrW(65)
e.Handled = False

' Modify 'b' to 'a' and forward the key.


Case ChrW(98)
MessageBox.Show(("Control.KeyPress: '" + _
e.KeyChar.ToString() + "' replaced by 'a'."))
e.KeyChar = ChrW(97)
e.Handled = False

To modify a noncharacter key


Override a Control method that processes Windows messages, detect the WM_KEYDOWN or
WM_SYSKEYDOWN message, and set the WParam property of the Message parameter to the Keys value
that represents the new noncharacter key.
The following code example demonstrates how to override the PreProcessMessage method of a control to
detect keys F1 through F9 and modify any F3 key press to F1. For more information on Control methods
that you can override to intercept keyboard messages, see User Input in a Windows Forms Application and
How Keyboard Input Works.
// Detect F1 through F9 during preprocessing and modify F3.
public override bool PreProcessMessage(ref Message m)
{
if (m.Msg == WM_KEYDOWN)
{
Keys keyCode = (Keys)m.WParam & Keys.KeyCode;

// Detect F1 through F9.


switch (keyCode)
{
case Keys.F1:
case Keys.F2:
case Keys.F3:
case Keys.F4:
case Keys.F5:
case Keys.F6:
case Keys.F7:
case Keys.F8:
case Keys.F9:

MessageBox.Show("Control.PreProcessMessage: '" +
keyCode.ToString() + "' pressed.");

// Replace F3 with F1, so that ProcessKeyMessage will


// receive F1 instead of F3.
if (keyCode == Keys.F3)
{
m.WParam = (IntPtr)Keys.F1;
MessageBox.Show("Control.PreProcessMessage: '" +
keyCode.ToString() + "' replaced by F1.");
}
break;
}
}

// Send all other messages to the base method.


return base.PreProcessMessage(ref m);
}
' Detect F1 through F9 during preprocessing and modify F3.
Public Overrides Function PreProcessMessage(ByRef m As Message) _
As Boolean

If m.Msg = WM_KEYDOWN Then


Dim keyCode As Keys = CType(m.WParam, Keys) And Keys.KeyCode

' Detect F1 through F9.


Select Case keyCode
Case Keys.F1, Keys.F2, Keys.F3, Keys.F4, Keys.F5, _
Keys.F6, Keys.F7, Keys.F8, Keys.F9

MessageBox.Show(("Control.PreProcessMessage: '" + _
keyCode.ToString() + "' pressed."))

' Replace F3 with F1, so that ProcessKeyMessage will


' receive F1 instead of F3.
If keyCode = Keys.F3 Then
m.WParam = CType(Keys.F1, IntPtr)
MessageBox.Show(("Control.PreProcessMessage: '" + _
keyCode.ToString() + "' replaced by F1."))
End If
End Select
End If

' Send all other messages to the base method.


Return MyBase.PreProcessMessage(m)
End Function

Example
The following code example is the complete application for the code examples in the previous sections. The
application uses a custom control derived from the TextBox class to consume and modify keyboard input.

using System;
using System.Drawing;
using System.Windows.Forms;

namespace KeyboardInput
{
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand,
Name="FullTrust")]
class Form1 : Form
{
// The following Windows message value is defined in Winuser.h.
private int WM_KEYDOWN = 0x100;
CustomTextBox CustomTextBox1 = new CustomTextBox();

[STAThread]
public static void Main()
{
Application.EnableVisualStyles();
Application.Run(new Form1());
}

public Form1()
{
this.AutoSize = true;
this.Controls.Add(CustomTextBox1);
CustomTextBox1.KeyPress +=
new KeyPressEventHandler(CustomTextBox1_KeyPress);
}

// Consume and modify several character keys.


void CustomTextBox1_KeyPress(object sender, KeyPressEventArgs e)
void CustomTextBox1_KeyPress(object sender, KeyPressEventArgs e)
{
switch (e.KeyChar)
{
// Consume 'A' and 'a'.
case (char)65:
case (char)97:
MessageBox.Show("Control.KeyPress: '" +
e.KeyChar.ToString() + "' consumed.");
e.Handled = true;
break;

// Detect 'B', modify it to 'A', and forward the key.


case (char)66:
MessageBox.Show("Control.KeyPress: '" +
e.KeyChar.ToString() + "' replaced by 'A'.");
e.KeyChar = (char)65;
e.Handled = false;
break;

// Detect 'b', modify it to 'a', and forward the key.


case (char)98:
MessageBox.Show("Control.KeyPress: '" +
e.KeyChar.ToString() + "' replaced by 'a'.");
e.KeyChar = (char)97;
e.Handled = false;
break;
}
}
}
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand,
Name="FullTrust")]
public class CustomTextBox : TextBox
{
// The following Windows message value is defined in Winuser.h.
private int WM_KEYDOWN = 0x100;

public CustomTextBox()
{
this.Size = new Size(100, 100);
this.AutoSize = false;
}

// Detect F1 through F9 during preprocessing and modify F3.


public override bool PreProcessMessage(ref Message m)
{
if (m.Msg == WM_KEYDOWN)
{
Keys keyCode = (Keys)m.WParam & Keys.KeyCode;

// Detect F1 through F9.


switch (keyCode)
{
case Keys.F1:
case Keys.F2:
case Keys.F3:
case Keys.F4:
case Keys.F5:
case Keys.F6:
case Keys.F7:
case Keys.F8:
case Keys.F9:

MessageBox.Show("Control.PreProcessMessage: '" +
keyCode.ToString() + "' pressed.");

// Replace F3 with F1, so that ProcessKeyMessage will


// receive F1 instead of F3.
if (keyCode == Keys.F3)
{
{
m.WParam = (IntPtr)Keys.F1;
MessageBox.Show("Control.PreProcessMessage: '" +
keyCode.ToString() + "' replaced by F1.");
}
break;
}
}

// Send all other messages to the base method.


return base.PreProcessMessage(ref m);
}

// Detect F1 through F9 during processing.


protected override bool ProcessKeyMessage(ref Message m)
{
if (m.Msg == WM_KEYDOWN)
{
Keys keyCode = (Keys)m.WParam & Keys.KeyCode;

// Detect F1 through F9.


switch (keyCode)
{
case Keys.F1:
case Keys.F2:
case Keys.F3:
case Keys.F4:
case Keys.F5:
case Keys.F6:
case Keys.F7:
case Keys.F8:
case Keys.F9:

MessageBox.Show("Control.ProcessKeyMessage: '" +
keyCode.ToString() + "' pressed.");
break;
}
}

// Send all messages to the base method.


return base.ProcessKeyMessage(ref m);
}
}
}

Imports System.Drawing
Imports System.Security
Imports System.Security.Permissions
Imports System.Windows.Forms

Namespace KeyboardInput
<System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.Demand,
Name:="FullTrust")> _
Class Form1
Inherits Form

' The following Windows message value is defined in Winuser.h.


Private WM_KEYDOWN As Integer = &H100
Private WithEvents CustomTextBox1 As New CustomTextBox()

<STAThread()> _
Public Shared Sub Main()
Application.EnableVisualStyles()
Application.Run(New Form1())
End Sub

Public Sub New()


Public Sub New()
Me.AutoSize = True
Me.Controls.Add(CustomTextBox1)
End Sub

' Consume and modify several character keys.


Sub CustomTextBox1_KeyPress(ByVal sender As Object, _
ByVal e As KeyPressEventArgs) Handles CustomTextBox1.KeyPress

Select Case e.KeyChar

' Consume 'A' and 'a'.


Case ChrW(65), ChrW(97)
MessageBox.Show(("Control.KeyPress: '" + _
e.KeyChar.ToString() + "' consumed."))
e.Handled = True

' Modify 'B' to 'A' and forward the key.


Case ChrW(66)
MessageBox.Show(("Control.KeyPress: '" + _
e.KeyChar.ToString() + "' replaced by 'A'."))
e.KeyChar = ChrW(65)
e.Handled = False

' Modify 'b' to 'a' and forward the key.


Case ChrW(98)
MessageBox.Show(("Control.KeyPress: '" + _
e.KeyChar.ToString() + "' replaced by 'a'."))
e.KeyChar = ChrW(97)
e.Handled = False
End Select
End Sub
End Class

<System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.Demand,
Name:="FullTrust")> _
Public Class CustomTextBox
Inherits TextBox

' The following Windows message value is defined in Winuser.h.


Private WM_KEYDOWN As Integer = &H100

Public Sub New()


Me.Size = New Size(100, 100)
Me.AutoSize = False
End Sub

' Detect F1 through F9 during preprocessing and modify F3.


Public Overrides Function PreProcessMessage(ByRef m As Message) _
As Boolean

If m.Msg = WM_KEYDOWN Then


Dim keyCode As Keys = CType(m.WParam, Keys) And Keys.KeyCode

' Detect F1 through F9.


Select Case keyCode
Case Keys.F1, Keys.F2, Keys.F3, Keys.F4, Keys.F5, _
Keys.F6, Keys.F7, Keys.F8, Keys.F9

MessageBox.Show(("Control.PreProcessMessage: '" + _
keyCode.ToString() + "' pressed."))

' Replace F3 with F1, so that ProcessKeyMessage will


' receive F1 instead of F3.
If keyCode = Keys.F3 Then
m.WParam = CType(Keys.F1, IntPtr)
MessageBox.Show(("Control.PreProcessMessage: '" + _
keyCode.ToString() + "' replaced by F1."))
End If
End Select
End Select
End If

' Send all other messages to the base method.


Return MyBase.PreProcessMessage(m)
End Function

' Detect F1 through F9 during processing.


Protected Overrides Function ProcessKeyMessage(ByRef m As Message) _
As Boolean

If m.Msg = WM_KEYDOWN Then


Dim keyCode As Keys = CType(m.WParam, Keys) And Keys.KeyCode

' Detect F1 through F9.


Select Case keyCode
Case Keys.F1, Keys.F2, Keys.F3, Keys.F4, Keys.F5, _
Keys.F6, Keys.F7, Keys.F8, Keys.F9

MessageBox.Show(("Control.ProcessKeyMessage: '" + _
keyCode.ToString() + "' pressed."))
End Select
End If

' Send all messages to the base method.


Return MyBase.ProcessKeyMessage(m)
End Function

End Class
End Namespace

Compiling the Code


This example requires:
References to the System, System.Drawing and System.Windows.Forms assemblies.

See also
Keyboard Input in a Windows Forms Application
User Input in a Windows Forms Application
How Keyboard Input Works
How to: Determine Which Modifier Key Was Pressed
9/1/2020 • 2 minutes to read • Edit Online

When you create an application that accepts the user's keystrokes, you may also want to monitor for modifier keys
such as the SHIFT, ALT, and CTRL keys. When a modifier key is pressed in combination with other keys, or with
mouse clicks, your application can respond appropriately. For example, if the letter S is pressed, this may simply
cause an "s" to appear on the screen, but if the keys CTRL+S are pressed, the current document may be saved. If
you handle the KeyDown event, the Modifiers property of the KeyEventArgs received by the event handler specifies
which modifier keys are pressed. Alternatively, the KeyData property of KeyEventArgs specifies the character that
was pressed as well as any modifier keys combined with a bitwise OR. However, if you are handling the KeyPress
event or a mouse event, the event handler does not receive this information. In this case, you must use the
ModifierKeys property of the Control class. In either case, you must perform a bitwise AND of the appropriate Keys
value and the value you are testing. The Keys enumeration offers variations of each modifier key, so it is important
that you perform the bitwise AND with the correct value. For example, the SHIFT key is represented by Shift,
ShiftKey, RShiftKey and LShiftKey The correct value to test SHIFT as a modifier key is Shift. Similarly, to test for
CTRL and ALT as modifiers you should use the Control and Alt values, respectively.

NOTE
Visual Basic programmers can also access key information through the Keyboard property

To determine which modifier key was pressed


Use the bitwise AND operator with the ModifierKeys property and a value of the Keys enumeration to
determine whether a particular modifier key is pressed. The following code example shows how to
determine whether the SHIFT key is pressed within a KeyPress event handler.

private:
void textBox1_KeyPress(Object^ sender, KeyPressEventArgs^ e)
{
if ((Control::ModifierKeys & Keys::Shift) == Keys::Shift)
{
MessageBox::Show("Pressed " + Keys::Shift.ToString());
}
}

public void TextBox1_KeyPress(object sender, KeyPressEventArgs e)


{
if ((Control.ModifierKeys & Keys.Shift) == Keys.Shift)
{
MessageBox.Show("Pressed " + Keys.Shift);
}
}

Public Sub TextBox1_KeyPress(ByVal sender As Object, _


ByVal e As KeyPressEventArgs) Handles TextBox1.KeyPress

If ((Control.ModifierKeys And Keys.Shift) = Keys.Shift) Then


MsgBox("Pressed " + Keys.Shift.ToString())
End If
End Sub
See also
Keys
ModifierKeys
Keyboard Input in a Windows Forms Application
How to: Determine If CapsLock is On in Visual Basic
How to: Handle Keyboard Input at the Form Level
9/1/2020 • 6 minutes to read • Edit Online

Windows Forms provides the ability to handle keyboard messages at the form level, before the messages reach a
control. This topic shows how to accomplish this task.
To handle a keyboard message at the form level
Handle the KeyPress or KeyDown event of the startup form, and set the KeyPreview property of the form to
true so that keyboard messages are received by the form before they reach any controls on the form. The
following code example handles the KeyPress event by detecting all of the number keys and consuming '1',
'4', and '7'.

// Detect all numeric characters at the form level and consume 1,


// 4, and 7. Note that Form.KeyPreview must be set to true for this
// event handler to be called.
private:
void Form1_KeyPress(Object^ sender, KeyPressEventArgs^ e)
{
if ((e->KeyChar >= '0') && (e->KeyChar <= '9'))
{
MessageBox::Show("Form.KeyPress: '" +
e->KeyChar.ToString() + "' pressed.");

switch (e->KeyChar)
{
case '1':
case '4':
case '7':
MessageBox::Show("Form.KeyPress: '" +
e->KeyChar.ToString() + "' consumed.");
e->Handled = true;
break;
}
}
}
// Detect all numeric characters at the form level and consume 1,
// 4, and 7. Note that Form.KeyPreview must be set to true for this
// event handler to be called.
void Form1_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar >= 48 && e.KeyChar <= 57)
{
MessageBox.Show("Form.KeyPress: '" +
e.KeyChar.ToString() + "' pressed.");

switch (e.KeyChar)
{
case (char)49:
case (char)52:
case (char)55:
MessageBox.Show("Form.KeyPress: '" +
e.KeyChar.ToString() + "' consumed.");
e.Handled = true;
break;
}
}
}

' Detect all numeric characters at the form level and consume 1,
' 4, and 7. Note that Form.KeyPreview must be set to true for this
' event handler to be called.
Sub Form1_KeyPress(ByVal sender As Object, _
ByVal e As KeyPressEventArgs) Handles Me.KeyPress

If e.KeyChar >= ChrW(48) And e.KeyChar <= ChrW(57) Then


MessageBox.Show(("Form.KeyPress: '" + _
e.KeyChar.ToString() + "' pressed."))

Select Case e.KeyChar


Case ChrW(49), ChrW(52), ChrW(55)
MessageBox.Show(("Form.KeyPress: '" + _
e.KeyChar.ToString() + "' consumed."))
e.Handled = True
End Select
End If
End Sub

Example
The following code example is the entire application for the above example. The application includes a TextBox
along with several other controls that allow you to move focus from the TextBox. The KeyPress event of the main
Form consumes '1', '4', and '7', and the KeyPress event of the TextBox consumes '2', '5', and '8' while displaying the
remaining keys. Compare the MessageBox output when you press a number key while the TextBox has focus with
the MessageBox output when you press a number key while focus is on one of the other controls.

#using <System.Drawing.dll>
#using <System.Windows.Forms.dll>
#using <System.dll>

using namespace System;


using namespace System::Drawing;
using namespace System::Windows::Forms;
using namespace System::Security::Permissions;

namespace KeyboardInputForm
{
public ref class Form1 sealed: public Form, public IMessageFilter
{
// The following Windows message value is defined in Winuser.h.
private:
static const int WM_KEYDOWN = 0x100;
private:
TextBox^ inputTextBox;
public:
Form1()
{
inputTextBox = gcnew TextBox();
this->AutoSize = true;
Application::AddMessageFilter(this);
FlowLayoutPanel^ panel = gcnew FlowLayoutPanel();
panel->AutoSize = true;
panel->FlowDirection = FlowDirection::TopDown;
panel->Controls->Add(gcnew Button());
panel->Controls->Add(gcnew RadioButton());
panel->Controls->Add(inputTextBox);
this->Controls->Add(panel);
this->KeyPreview = true;
this->KeyPress +=
gcnew KeyPressEventHandler(this, &Form1::Form1_KeyPress);
inputTextBox->KeyPress +=
gcnew KeyPressEventHandler(this,
&Form1::inputTextBox_KeyPress);
}

// Detect all numeric characters at the


// application level and consume 0.
[SecurityPermission(SecurityAction::LinkDemand,
Flags=SecurityPermissionFlag::UnmanagedCode)]
virtual bool PreFilterMessage(Message% m)
{
// Detect key down messages.
if (m.Msg == WM_KEYDOWN)
{
Keys keyCode = (Keys)((int)m.WParam) & Keys::KeyCode;
// Determine whether the keystroke is a number from the top of
// the keyboard, or a number from the keypad.
if (((keyCode >= Keys::D0) && (keyCode <= Keys::D9))
||((keyCode >= Keys::NumPad0)
&& (keyCode <= Keys::NumPad9)))
{
MessageBox::Show(
"IMessageFilter.PreFilterMessage: '" +
keyCode.ToString() + "' pressed.");

if ((keyCode == Keys::D0) || (keyCode == Keys::NumPad0))


{
MessageBox::Show(
"IMessageFilter.PreFilterMessage: '" +
keyCode.ToString() + "' consumed.");
return true;
}
}
}

// Forward all other messages.


return false;
}

// Detect all numeric characters at the form level and consume 1,


// 4, and 7. Note that Form.KeyPreview must be set to true for this
// event handler to be called.
private:
void Form1_KeyPress(Object^ sender, KeyPressEventArgs^ e)
{
if ((e->KeyChar >= '0') && (e->KeyChar <= '9'))
{
{
MessageBox::Show("Form.KeyPress: '" +
e->KeyChar.ToString() + "' pressed.");

switch (e->KeyChar)
{
case '1':
case '4':
case '7':
MessageBox::Show("Form.KeyPress: '" +
e->KeyChar.ToString() + "' consumed.");
e->Handled = true;
break;
}
}
}

// Detect all numeric characters at the TextBox level and consume


// 2, 5, and 8.
private:
void inputTextBox_KeyPress(Object^ sender, KeyPressEventArgs^ e)
{
if ((e->KeyChar >= '0') && (e->KeyChar <= '9'))
{
MessageBox::Show("Control.KeyPress: '" +
e->KeyChar.ToString() + "' pressed.");

switch (e->KeyChar)
{
case '2':
case '5':
case '8':
MessageBox::Show("Control.KeyPress: '" +
e->KeyChar.ToString() + "' consumed.");
e->Handled = true;
break;
}
}
}
};
}

[STAThread]
int main()
{
Application::EnableVisualStyles();
Application::Run(gcnew KeyboardInputForm::Form1());
}

using System;
using System.Drawing;
using System.Windows.Forms;

namespace KeyboardInputForm
{
class Form1 : Form
{
TextBox TextBox1 = new TextBox();

[STAThread]
public static void Main()
{
Application.EnableVisualStyles();
Application.Run(new Form1());
}

public Form1()
{
this.AutoSize = true;

FlowLayoutPanel panel = new FlowLayoutPanel();


panel.AutoSize = true;
panel.FlowDirection = FlowDirection.TopDown;
panel.Controls.Add(TextBox1);
this.Controls.Add(panel);

this.KeyPreview = true;
this.KeyPress +=
new KeyPressEventHandler(Form1_KeyPress);
TextBox1.KeyPress +=
new KeyPressEventHandler(TextBox1_KeyPress);
}

// Detect all numeric characters at the form level and consume 1,


// 4, and 7. Note that Form.KeyPreview must be set to true for this
// event handler to be called.
void Form1_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar >= 48 && e.KeyChar <= 57)
{
MessageBox.Show("Form.KeyPress: '" +
e.KeyChar.ToString() + "' pressed.");

switch (e.KeyChar)
{
case (char)49:
case (char)52:
case (char)55:
MessageBox.Show("Form.KeyPress: '" +
e.KeyChar.ToString() + "' consumed.");
e.Handled = true;
break;
}
}
}

// Detect all numeric characters at the TextBox level and consume


// 2, 5, and 8.
void TextBox1_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar >= 48 && e.KeyChar <= 57)
{
MessageBox.Show("Control.KeyPress: '" +
e.KeyChar.ToString() + "' pressed.");

switch (e.KeyChar)
{
case (char)50:
case (char)53:
case (char)56:
MessageBox.Show("Control.KeyPress: '" +
e.KeyChar.ToString() + "' consumed.");
e.Handled = true;
break;
}
}
}
}
}

Imports System.Drawing
Imports System.Windows.Forms

Namespace KeyboardInputForm
Class Form1
Inherits Form

Private WithEvents TextBox1 As New TextBox()

<STAThread()> _
Public Shared Sub Main()
Application.EnableVisualStyles()
Application.Run(New Form1())
End Sub

Public Sub New()


Me.AutoSize = True

Dim panel As New FlowLayoutPanel()


panel.AutoSize = True
panel.FlowDirection = FlowDirection.TopDown
panel.Controls.Add(TextBox1)
Me.Controls.Add(panel)

Me.KeyPreview = True
End Sub

' Detect all numeric characters at the form level and consume 1,
' 4, and 7. Note that Form.KeyPreview must be set to true for this
' event handler to be called.
Sub Form1_KeyPress(ByVal sender As Object, _
ByVal e As KeyPressEventArgs) Handles Me.KeyPress

If e.KeyChar >= ChrW(48) And e.KeyChar <= ChrW(57) Then


MessageBox.Show(("Form.KeyPress: '" + _
e.KeyChar.ToString() + "' pressed."))

Select Case e.KeyChar


Case ChrW(49), ChrW(52), ChrW(55)
MessageBox.Show(("Form.KeyPress: '" + _
e.KeyChar.ToString() + "' consumed."))
e.Handled = True
End Select
End If
End Sub

' Detect all numeric characters at the TextBox level and consume
' 2, 5, and 8.
Sub TextBox1_KeyPress(ByVal sender As Object, _
ByVal e As KeyPressEventArgs) Handles TextBox1.KeyPress

If e.KeyChar >= ChrW(48) And e.KeyChar <= ChrW(57) Then


MessageBox.Show(("Control.KeyPress: '" + _
e.KeyChar.ToString() + "' pressed."))

Select Case e.KeyChar


Case ChrW(50), ChrW(53), ChrW(56)
MessageBox.Show(("Control.KeyPress: '" + _
e.KeyChar.ToString() + "' consumed."))
e.Handled = True
End Select
End If
End Sub

End Class
End Namespace

Compiling the Code


This example requires:
References to the System, System.Drawing and System.Windows.Forms assemblies.

See also
Keyboard Input in a Windows Forms Application
Mouse Input in a Windows Forms Application
9/1/2020 • 2 minutes to read • Edit Online

Windows Forms includes a variety of mouse events and additional support for customized mouse cursors, mouse
capture, and drag-and-drop behavior.

In This Section
How Mouse Input Works in Windows Forms
Provides information about the mouse events and how to get current information and system settings for the
mouse.
Mouse Events in Windows Forms
Provides information about the order in which the mouse events occur and how the mouse events are raised
within specific controls.
How to: Distinguish Between Clicks and Double-Clicks
Demonstrates how to use single and double clicks to initiate incompatible actions.
Mouse Pointers in Windows Forms
Describes how to change the mouse cursor.
Mouse Capture in Windows Forms
Describes how a control can capture the mouse.
Drag-and-Drop Functionality in Windows Forms
Describes how to implement drag-and-drop behavior.

Related Sections
Accessing the Mouse
Lists topics for accessing the mouse using Visual Basic.
How Mouse Input Works in Windows Forms
9/1/2020 • 4 minutes to read • Edit Online

Receiving and handling mouse input is an important part of every Windows application. You can handle mouse
events to perform an action in your application, or use mouse location information to perform hit testing or other
actions. In addition, you can change the way the controls in your application handle mouse input. This topic
describes these mouse events in detail, and how to obtain and change system settings for the mouse. For more
information about the data provided with the mouse events and the order in which the mouse click events are
raised, see Mouse Events in Windows Forms.

Mouse Location and Hit-Testing


When the user moves the mouse, the operating system moves the mouse pointer. The mouse pointer contains a
single pixel, called the hot spot, which the operating system tracks and recognizes as the position of the pointer.
When the user moves the mouse or presses a mouse button, the Control that contains the HotSpot raises the
appropriate mouse event. You can obtain the current mouse position with the Location property of the
MouseEventArgs when handling a mouse event or by using the Position property of the Cursor class. You can
subsequently use mouse location information to perform hit-testing, and then perform an action based on the
location of the mouse. Hit-testing capability is built in to several controls in Windows Forms such as the ListView,
TreeView, MonthCalendar and DataGridView controls. Used with the appropriate mouse event, MouseHover for
example, hit-testing is very useful for determining when your application should perform a specific action.

Mouse Events
The primary way to respond to mouse input is to handle mouse events. The following table shows the mouse
events and describes when they are raised.

M O USE EVEN T DESC RIP T IO N

Click This event occurs when the mouse button is released, typically
before the MouseUp event. The handler for this event receives
an argument of type EventArgs. Handle this event when you
only need to determine when a click occurs.

MouseClick This event occurs when the user clicks the control with the
mouse. The handler for this event receives an argument of
type MouseEventArgs. Handle this event when you need to
get information about the mouse when a click occurs.

DoubleClick This event occurs when the control is double-clicked. The


handler for this event receives an argument of type
EventArgs. Handle this event when you only need to
determine when a double-click occurs.

MouseDoubleClick This event occurs when the user double-clicks the control with
the mouse. The handler for this event receives an argument of
type MouseEventArgs. Handle this event when you need to
get information about the mouse when a double-click occurs.
M O USE EVEN T DESC RIP T IO N

MouseDown This event occurs when the mouse pointer is over the control
and the user presses a mouse button. The handler for this
event receives an argument of type MouseEventArgs.

MouseEnter This event occurs when the mouse pointer enters the border
or client area of the control, depending on the type of control.
The handler for this event receives an argument of type
EventArgs.

MouseHover This event occurs when the mouse pointer stops and rests
over the control. The handler for this event receives an
argument of type EventArgs.

MouseLeave This event occurs when the mouse pointer leaves the border
or client area of the control, depending on the type of the
control. The handler for this event receives an argument of
type EventArgs.

MouseMove This event occurs when the mouse pointer moves while it is
over a control. The handler for this event receives an
argument of type MouseEventArgs.

MouseUp This event occurs when the mouse pointer is over the control
and the user releases a mouse button. The handler for this
event receives an argument of type MouseEventArgs.

MouseWheel This event occurs when the user rotates the mouse wheel
while the control has focus. The handler for this event receives
an argument of type MouseEventArgs. You can use the Delta
property of MouseEventArgs to determine how far the mouse
has scrolled.

Changing Mouse Input and Detecting System Settings


You can detect and change the way a control handles mouse input by deriving from the control and using the
GetStyle and SetStyle methods. The SetStyle method takes a bitwise combination of ControlStyles values to
determine whether the control will have standard click or double-click behavior or if the control will handle its own
mouse processing. In addition, the SystemInformation class includes properties that describe the capabilities of the
mouse and specify how the mouse interacts with the operating system. The following table summarizes these
properties.

P RO P ERT Y DESC RIP T IO N

DoubleClickSize Gets the dimensions, in pixels, of the area in which the user
must click twice for the operating system to consider the two
clicks a double-click.

DoubleClickTime Gets the maximum number of milliseconds that can elapse


between a first click and a second click for the operating
system to consider the mouse action a double-click.

MouseButtons Gets the number of buttons on the mouse.


P RO P ERT Y DESC RIP T IO N

MouseButtonsSwapped Gets a value indicating whether the functions of the left and
right mouse buttons have been swapped.

MouseHoverSize Gets the dimensions, in pixels, of the rectangle within which


the mouse pointer has to stay for the mouse hover time
before a mouse hover message is generated.

MouseHoverTime Gets the time, in milliseconds, that the mouse pointer has to
stay in the hover rectangle before a mouse hover message is
generated.

MousePresent Gets a value indicating whether a mouse is installed.

MouseSpeed Gets a value indicating the current mouse speed, from 1 to


20.

MouseWheelPresent Gets a value indicating whether a mouse with a mouse wheel


is installed.

MouseWheelScrollDelta Gets the amount of the delta value of the increment of a


single mouse wheel rotation.

MouseWheelScrollLines Gets the number of lines to scroll when the mouse wheel is
rotated.

See also
Mouse Input in a Windows Forms Application
Mouse Capture in Windows Forms
Mouse Pointers in Windows Forms
Mouse Events in Windows Forms
9/1/2020 • 4 minutes to read • Edit Online

When you handle mouse input, you usually want to know the location of the mouse pointer and the state of the
mouse buttons. This topic provides details on how to get this information from mouse events, and explains the
order in which mouse click events are raised in Windows Forms controls. For a list and description of all of the
mouse events, see How Mouse Input Works in Windows Forms. Also see Event Handlers Overview (Windows
Forms) and Events Overview (Windows Forms).

Mouse Information
A MouseEventArgs is sent to the handlers of mouse events related to clicking a mouse button and tracking mouse
movements. MouseEventArgs provides information about the current state of the mouse, including the location of
the mouse pointer in client coordinates, which mouse buttons are pressed, and whether the mouse wheel has
scrolled. Several mouse events, such as those that simply notify when the mouse pointer has entered or left the
bounds of a control, send an EventArgs to the event handler with no further information.
If you want to know the current state of the mouse buttons or the location of the mouse pointer, and you want to
avoid handling a mouse event, you can also use the MouseButtons and MousePosition properties of the Control
class. MouseButtons returns information about which mouse buttons are currently pressed. The MousePosition
returns the screen coordinates of the mouse pointer and is equivalent to the value returned by Position.

Converting Between Screen and Client Coordinates


Because some mouse location information is in client coordinates and some is in screen coordinates, you may
need to convert a point from one coordinate system to the other. You can do this easily by using the PointToClient
and PointToScreen methods available on the Control class.

Standard Click Event Behavior


If you want to handle mouse click events in the proper order, you need to know the order in which click events are
raised in Windows Forms controls. All Windows Forms controls raise click events in the same order when a mouse
button is pressed and released (regardless of which mouse button), except where noted in the following list for
individual controls. The following list shows the order of events raised for a single mouse-button click:
1. MouseDown event.
2. Click event.
3. MouseClick event.
4. MouseUp event.
The following is the order of events raised for a double mouse-button click:
1. MouseDown event.
2. Click event.
3. MouseClick event.
4. MouseUp event.
5. MouseDown event.
6. DoubleClick event. (This can vary, depending on whether the control in question has the
StandardDoubleClick style bit set to true . For more information about how to set a ControlStyles bit, see
the SetStyle method.)
7. MouseDoubleClick event.
8. MouseUp event.
For a code example that demonstrates the order of the mouse click events, see How to: Handle User Input Events
in Windows Forms Controls.
Individual Controls
The following controls do not conform to the standard mouse click event behavior:
Button
CheckBox
ComboBox
RadioButton

NOTE
For the ComboBox control, the event behavior detailed later occurs if the user clicks on the edit field, the button, or
on an item within the list.

Left click: Click, MouseClick


Right click: No click events raised
Left double-click: Click, MouseClick; Click, MouseClick
Right double-click: No click events raised
TextBox, RichTextBox, ListBox, MaskedTextBox, and CheckedListBox controls

NOTE
The event behavior detailed later occurs when the user clicks anywhere within these controls.

Left click: Click, MouseClick


Right click: No click events raised
Left double-click: Click, MouseClick, DoubleClick, MouseDoubleClick
Right double-click: No click events raised
ListView control

NOTE
The event behavior detailed later occurs only when the user clicks on the items in the ListView control. No events are
raised for clicks anywhere else on the control. In addition to the events described later, there are the BeforeLabelEdit
and AfterLabelEdit events, which may be of interest to you if you want to use validation with the ListView control.

Left click: Click, MouseClick


Right click: Click, MouseClick
Left double-click: Click, MouseClick; DoubleClick, MouseDoubleClick
Right double-click: Click, MouseClick; DoubleClick, MouseDoubleClick
TreeView control

NOTE
The event behavior detailed later occurs only when the user clicks on the items themselves or to the right of the
items in the TreeView control. No events are raised for clicks anywhere else on the control. In addition to those
described later, there are the BeforeCheck, BeforeSelect, BeforeLabelEdit, AfterSelect, AfterCheck, and AfterLabelEdit
events, which may be of interest to you if you want to use validation with the TreeView control.

Left click: Click, MouseClick


Right click: Click, MouseClick
Left double-click: Click, MouseClick; DoubleClick, MouseDoubleClick
Right double-click: Click, MouseClick; DoubleClick, MouseDoubleClick
Painting behavior of toggle controls
Toggle controls, such as the controls deriving from the ButtonBase class, have the following distinctive painting
behavior in combination with mouse click events:
1. The user presses the mouse button.
2. The control paints in the pressed state.
3. The MouseDown event is raised.
4. The user releases the mouse button.
5. The control paints in the raised state.
6. The Click event is raised.
7. The MouseClick event is raised.
8. The MouseUp event is raised.

NOTE
If the user moves the pointer out of the toggle control while the mouse button is down (such as moving the mouse
off the Button control while it is pressed), the toggle control will paint in the raised state and only the MouseUp
event occurs. The Click or MouseClick events will not occur in this situation.

See also
Mouse Input in a Windows Forms Application
How to: Distinguish Between Clicks and Double-
Clicks
9/1/2020 • 9 minutes to read • Edit Online

Typically, a single click initiates a user interface (UI) action and a double-click extends the action. For example, one
click usually selects an item, and a double-click edits the selected item. However, the Windows Forms click events
do not easily accommodate a scenario where a click and a double-click perform incompatible actions, because an
action tied to the Click or MouseClick event is performed before the action tied to the DoubleClick or
MouseDoubleClick event. This topic demonstrates two solutions to this problem. One solution is to handle the
double-click event and roll back the actions in the handling of the click event. In rare situations you may need to
simulate click and double-click behavior by handling the MouseDown event and by using the DoubleClickTime and
DoubleClickSize properties of the SystemInformation class. You measure the time between clicks and if a second
click occurs before the value of DoubleClickTime is reached and the click is within a rectangle defined by
DoubleClickSize, perform the double-click action; otherwise, perform the click action.
To roll back a click action
Ensure that the control you are working with has standard double-click behavior. If not, enable the control
with the SetStyle method. Handle the double-click event and roll back the click action as well as the double-
click action. The following code example demonstrates a how to create a custom button with double-click
enabled, as well as how to roll back the click action in the double-click event handling code.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace MouseRollBackSingleClick
{
public class Form1 : Form
{
private DoubleClickButton button1;
private FormBorderStyle initialStyle;

public Form1()
{
initialStyle = this.FormBorderStyle;
this.ClientSize = new System.Drawing.Size(292, 266);
button1 = new DoubleClickButton();
button1.Location = new Point (40,40);
button1.Click += new EventHandler(button1_Click);
button1.AutoSize = true;
this.AllowDrop = true;
button1.Text = "Click or Double Click";
button1.DoubleClick += new EventHandler(button1_DoubleClick);
this.Controls.Add(button1);
}

// Handle the double click event.


void button1_DoubleClick(object sender, EventArgs e)
{
// Change the border style back to the initial style.
this.FormBorderStyle = initialStyle;
MessageBox.Show("Rolled back single click change.");
}

// Handle the click event.


void button1_Click(object sender, EventArgs e)
{
this.FormBorderStyle = FormBorderStyle.FixedToolWindow;
}

[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new Form1());
}
}
public class DoubleClickButton : Button
{
public DoubleClickButton() : base()
{
// Set the style so a double click event occurs.
SetStyle(ControlStyles.StandardClick |
ControlStyles.StandardDoubleClick, true);
}
}
}
Imports System.ComponentModel
Imports System.Drawing
Imports System.Text
Imports System.Windows.Forms

Public Class Form1


Inherits Form
Private WithEvents button1 As DoubleClickButton
Private initialStyle As FormBorderStyle

Public Sub New()


Me.SuspendLayout()
initialStyle = Me.FormBorderStyle
Me.ClientSize = New System.Drawing.Size(292, 266)
button1 = New DoubleClickButton()
button1.Location = New Point(40, 40)
button1.AutoSize = True
button1.Text = "Click or Double Click"
Me.Controls.Add(button1)
Me.Name = "Form1"
Me.ResumeLayout(False)
Me.PerformLayout()

End Sub

' Handle the double click event.


Private Sub button1_DoubleClick(ByVal sender As Object, ByVal e As EventArgs) _
Handles button1.DoubleClick

' Change the border style back to the initial style.


Me.FormBorderStyle = initialStyle
MessageBox.Show("Rolled back single click change.")

End Sub

' Handle the click event.


Private Sub button1_Click(ByVal sender As Object, ByVal e As EventArgs) _
Handles button1.Click

Me.FormBorderStyle = FormBorderStyle.FixedToolWindow

End Sub

<STAThread()> _
Shared Sub Main()
Application.EnableVisualStyles()
Application.Run(New Form1())

End Sub
End Class

Public Class DoubleClickButton


Inherits Button

Public Sub New()


' Set the style so a double click event occurs.
SetStyle(ControlStyles.StandardClick Or ControlStyles.StandardDoubleClick, True)

End Sub
End Class
To distinguish between clicks in the MouseDown event
Handle the MouseDown event and determine the location and time span between clicks using the
appropriate SystemInformation properties and a Timer component. Perform the appropriate action
depending on whether a click or double-click takes place. The following code example demonstrates how
this can be done.

#using <System.Drawing.dll>
#using <System.Windows.Forms.dll>
#using <System.dll>

using namespace System;


using namespace System::Drawing;
using namespace System::Windows::Forms;

namespace SingleVersusDoubleClick
{
public ref class Form1 : public Form
{
private:
Rectangle hitTestRectangle;
private:
Rectangle doubleClickRectangle;
private:
TextBox^ outputBox;
private:
Timer^ doubleClickTimer;
private:
ProgressBar^ doubleClickBar;
private:
Label^ hitTestLabel;
private:
Label^ timerLabel;
private:
bool isFirstClick;
private:
bool isDoubleClick;
private:
int milliseconds;

public:
Form1()
{
hitTestRectangle = Rectangle();
hitTestRectangle.Location = Point(30, 20);
hitTestRectangle.Size = System::Drawing::Size(100, 40);

doubleClickRectangle = Rectangle();

outputBox = gcnew TextBox();


outputBox->Location = Point(30, 120);
outputBox->Size = System::Drawing::Size(200, 100);
outputBox->AutoSize = false;
outputBox->Multiline = true;

doubleClickTimer = gcnew Timer();


doubleClickTimer->Interval = 100;
doubleClickTimer->Tick +=
gcnew EventHandler(this, &Form1::doubleClickTimer_Tick);

doubleClickBar = gcnew ProgressBar();


doubleClickBar->Location = Point(30, 85);
doubleClickBar->Minimum = 0;
doubleClickBar->Maximum = SystemInformation::DoubleClickTime;

hitTestLabel = gcnew Label();


hitTestLabel->Location = Point(30, 5);
hitTestLabel->Size = System::Drawing::Size(100, 15);
hitTestLabel->Size = System::Drawing::Size(100, 15);
hitTestLabel->Text = "Hit test rectangle:";

timerLabel = gcnew Label();


timerLabel->Location = Point(30, 70);
timerLabel->Size = System::Drawing::Size(100, 15);
timerLabel->Text = "Double click timer:";

isFirstClick = true;

this->Paint += gcnew PaintEventHandler(this, &Form1::Form1_Paint);


this->MouseDown +=
gcnew MouseEventHandler(this, &Form1::Form1_MouseDown);
this->Controls->
AddRange(gcnew array<Control^> { doubleClickBar, outputBox,
hitTestLabel, timerLabel });
}

// Detect a valid single click or double click.


private:
void Form1_MouseDown(Object^ sender, MouseEventArgs^ e)
{
// Verify that the mouse click is in the main hit
// test rectangle.
if (!hitTestRectangle.Contains(e->Location))
{
return;
}

// This is the first mouse click.


if (isFirstClick)
{
isFirstClick = false;

// Determine the location and size of the double click


// rectangle area to draw around the cursor point.
doubleClickRectangle = Rectangle(
e->X - (SystemInformation::DoubleClickSize.Width / 2),
e->Y - (SystemInformation::DoubleClickSize.Height / 2),
SystemInformation::DoubleClickSize.Width,
SystemInformation::DoubleClickSize.Height);
Invalidate();

// Start the double click timer.


doubleClickTimer->Start();
}

// This is the second mouse click.


else
{
// Verify that the mouse click is within the double click
// rectangle and is within the system-defined double
// click period.
if (doubleClickRectangle.Contains(e->Location) &&
milliseconds < SystemInformation::DoubleClickTime)
{
isDoubleClick = true;
}
}
}

private:
void doubleClickTimer_Tick(Object^ sender, EventArgs^ e)
{
milliseconds += 100;
doubleClickBar->Increment(100);

// The timer has reached the double click time limit.


if (milliseconds >= SystemInformation::DoubleClickTime)
{
{
doubleClickTimer->Stop();

if (isDoubleClick)
{
outputBox->AppendText("Perform double click action");
outputBox->AppendText(Environment::NewLine);
}
else
{
outputBox->AppendText("Perform single click action");
outputBox->AppendText(Environment::NewLine);
}

// Allow the MouseDown event handler to process clicks again.


isFirstClick = true;
isDoubleClick = false;
milliseconds = 0;
doubleClickBar->Value = 0;
}
}

// Paint the hit test and double click rectangles.


private:
void Form1_Paint(Object^ sender, PaintEventArgs^ e)
{
// Draw the border of the main hit test rectangle.
e->Graphics->DrawRectangle(Pens::Black, hitTestRectangle);

// Fill in the double click rectangle.


e->Graphics->FillRectangle(Brushes::Blue, doubleClickRectangle);
}
};
}

[STAThread]
int main()
{
Application::EnableVisualStyles();
Application::Run(gcnew SingleVersusDoubleClick::Form1);
}

using System;
using System.Drawing;
using System.Windows.Forms;

namespace SingleVersusDoubleClick
{
class Form1 : Form
{
private Rectangle hitTestRectangle = new Rectangle();
private Rectangle doubleClickRectangle = new Rectangle();
private TextBox textBox1 = new TextBox();
private Timer doubleClickTimer = new Timer();
private ProgressBar doubleClickBar = new ProgressBar();
private Label label1 = new Label();
private Label label2 = new Label();
private bool isFirstClick = true;
private bool isDoubleClick = false;
private int milliseconds = 0;

[STAThread]
public static void Main()
{
Application.EnableVisualStyles();
Application.Run(new Form1());
}
}

public Form1()
{
label1.Location = new Point(30, 5);
label1.Size = new Size(100, 15);
label1.Text = "Hit test rectangle:";

label2.Location = new Point(30, 70);


label2.Size = new Size(100, 15);
label2.Text = "Double click timer:";

hitTestRectangle.Location = new Point(30, 20);


hitTestRectangle.Size = new Size(100, 40);

doubleClickTimer.Interval = 100;
doubleClickTimer.Tick +=
new EventHandler(doubleClickTimer_Tick);

doubleClickBar.Location = new Point(30, 85);


doubleClickBar.Minimum = 0;
doubleClickBar.Maximum = SystemInformation.DoubleClickTime;

textBox1.Location = new Point(30, 120);


textBox1.Size = new Size(200, 100);
textBox1.AutoSize = false;
textBox1.Multiline = true;

this.Paint += new PaintEventHandler(Form1_Paint);


this.MouseDown += new MouseEventHandler(Form1_MouseDown);
this.Controls.AddRange(new Control[] { doubleClickBar, textBox1,
label1, label2 });
}

// Detect a valid single click or double click.


void Form1_MouseDown(object sender, MouseEventArgs e)
{
// Verify that the mouse click is in the main hit
// test rectangle.
if (!hitTestRectangle.Contains(e.Location))
{
return;
}

// This is the first mouse click.


if (isFirstClick)
{
isFirstClick = false;

// Determine the location and size of the double click


// rectangle area to draw around the cursor point.
doubleClickRectangle = new Rectangle(
e.X - (SystemInformation.DoubleClickSize.Width / 2),
e.Y - (SystemInformation.DoubleClickSize.Height / 2),
SystemInformation.DoubleClickSize.Width,
SystemInformation.DoubleClickSize.Height);
Invalidate();

// Start the double click timer.


doubleClickTimer.Start();
}

// This is the second mouse click.


else
{
// Verify that the mouse click is within the double click
// rectangle and is within the system-defined double
// click period.
if (doubleClickRectangle.Contains(e.Location) &&
milliseconds < SystemInformation.DoubleClickTime)
{
isDoubleClick = true;
}
}
}

void doubleClickTimer_Tick(object sender, EventArgs e)


{
milliseconds += 100;
doubleClickBar.Increment(100);

// The timer has reached the double click time limit.


if (milliseconds >= SystemInformation.DoubleClickTime)
{
doubleClickTimer.Stop();

if (isDoubleClick)
{
textBox1.AppendText("Perform double click action");
textBox1.AppendText(Environment.NewLine);
}
else
{
textBox1.AppendText("Perform single click action");
textBox1.AppendText(Environment.NewLine);
}

// Allow the MouseDown event handler to process clicks again.


isFirstClick = true;
isDoubleClick = false;
milliseconds = 0;
doubleClickBar.Value = 0;
}
}

// Paint the hit test and double click rectangles.


void Form1_Paint(object sender, PaintEventArgs e)
{
// Draw the border of the main hit test rectangle.
e.Graphics.DrawRectangle(Pens.Black, hitTestRectangle);

// Fill in the double click rectangle.


e.Graphics.FillRectangle(Brushes.Blue, doubleClickRectangle);
}
}
}

Imports System.Drawing
Imports System.Windows.Forms

Namespace SingleVersusDoubleClick

Class Form1
Inherits Form
Private hitTestRectangle As New Rectangle()
Private doubleClickRectangle As New Rectangle()
Private textBox1 As New TextBox()
Private WithEvents doubleClickTimer As New Timer()
Private doubleClickBar As New ProgressBar()
Private label1 As New Label()
Private label2 As New Label()
Private isFirstClick As Boolean = True
Private isDoubleClick As Boolean = False
Private milliseconds As Integer = 0

<STAThread()> _
Public Shared Sub Main()
Public Shared Sub Main()
Application.EnableVisualStyles()
Application.Run(New Form1())
End Sub

Public Sub New()


label1.Location = New Point(30, 5)
label1.Size = New Size(100, 15)
label1.Text = "Hit test rectangle:"

label2.Location = New Point(30, 70)


label2.Size = New Size(100, 15)
label2.Text = "Double click timer:"

hitTestRectangle.Location = New Point(30, 20)


hitTestRectangle.Size = New Size(100, 40)
doubleClickTimer.Interval = 100

doubleClickBar.Location = New Point(30, 85)


doubleClickBar.Minimum = 0
doubleClickBar.Maximum = SystemInformation.DoubleClickTime

textBox1.Location = New Point(30, 120)


textBox1.Size = New Size(200, 100)
textBox1.AutoSize = False
textBox1.Multiline = True

Me.Controls.Add(doubleClickBar)
Me.Controls.Add(textBox1)
Me.Controls.Add(label1)
Me.Controls.Add(label2)
End Sub

' Detect a valid single click or double click.


Sub Form1_MouseDown(ByVal sender As Object, _
ByVal e As MouseEventArgs) Handles Me.MouseDown

' Verify that the mouse click is in the main hit


' test rectangle.
If Not hitTestRectangle.Contains(e.Location) Then
Return
End If

' This is the first mouse click.


If isFirstClick = True Then
isFirstClick = False

' Determine the location and size of the double click


' rectangle to draw around the cursor point.
doubleClickRectangle = New Rectangle( _
e.X - (SystemInformation.DoubleClickSize.Width / 2), _
e.Y - (SystemInformation.DoubleClickSize.Height / 2), _
SystemInformation.DoubleClickSize.Width, _
SystemInformation.DoubleClickSize.Height)
Invalidate()

' Start the double click timer.


doubleClickTimer.Start()

' This is the second mouse click.


Else
' Verify that the mouse click is within the double click
' rectangle and is within the system-defined double
' click period.
If doubleClickRectangle.Contains(e.Location) And _
milliseconds < SystemInformation.DoubleClickTime Then
isDoubleClick = True
End If
End If
End Sub

Sub doubleClickTimer_Tick(ByVal sender As Object, _


ByVal e As EventArgs) Handles doubleClickTimer.Tick

milliseconds += 100
doubleClickBar.Increment(100)

' The timer has reached the double click time limit.
If milliseconds >= SystemInformation.DoubleClickTime Then
doubleClickTimer.Stop()

If isDoubleClick Then
textBox1.AppendText("Perform double click action")
textBox1.AppendText(Environment.NewLine)
Else
textBox1.AppendText("Perform single click action")
textBox1.AppendText(Environment.NewLine)
End If

' Allow the MouseDown event handler to process clicks again.


isFirstClick = True
isDoubleClick = False
milliseconds = 0
doubleClickBar.Value = 0
End If
End Sub

' Paint the hit test and double click rectangles.


Sub Form1_Paint(ByVal sender As Object, _
ByVal e As PaintEventArgs) Handles Me.Paint

' Draw the border of the main hit test rectangle.


e.Graphics.DrawRectangle(Pens.Black, hitTestRectangle)

' Fill in the double click rectangle.


e.Graphics.FillRectangle(Brushes.Blue, doubleClickRectangle)
End Sub
End Class
End Namespace

Compiling the Code


These examples require:
References to the System, System.Drawing, and System.Windows.Forms assemblies.

See also
Mouse Input in a Windows Forms Application
Mouse Pointers in Windows Forms
9/1/2020 • 2 minutes to read • Edit Online

The mouse pointer, which is sometimes referred to as the cursor, is a bitmap that specifies a focus point on the
screen for user input with the mouse. This topic provides an overview of the mouse pointer in Windows Forms and
describes some of the ways to modify and control the mouse pointer.

Accessing the Mouse Pointer


The mouse pointer is represented by the Cursor class, and each Control has a Control.Cursor property that
specifies the pointer for that control. The Cursor class contains properties that describe the pointer, such as the
Position and HotSpot properties, and methods that can modify the appearance of the pointer, such as the Show,
Hide, and DrawStretched methods.

Controlling the Mouse Pointer


Sometimes you may want to limit the area in which the mouse pointer can be used or change the position the
mouse. You can get or set the current location of the mouse using the Position property of the Cursor. In addition,
you can limit the area the mouse pointer can be used be setting the Clip property. The clip area, by default, is the
entire screen.

Changing the Mouse Pointer


Changing the mouse pointer is an important way of providing feedback to the user. For example, the mouse
pointer can be modified in the handlers of the MouseEnter and MouseLeave events to tell the user that
computations are occurring and to limit user interaction in the control. Sometimes, the mouse pointer will change
because of system events, such as when your application is involved in a drag-and-drop operation.
The primary way to change the mouse pointer is by setting the Control.Cursor or DefaultCursor property of a
control to a new Cursor. For examples of changing the mouse pointer, see the code example in the Cursor class. In
addition, the Cursors class exposes a set of Cursor objects for many different types of pointers, such as a pointer
that resembles a hand. To display the wait pointer, which resembles an hourglass, whenever the mouse pointer is
on the control, use the UseWaitCursor property of the Control class.

See also
Cursor
Mouse Input in a Windows Forms Application
Drag-and-Drop Functionality in Windows Forms
Mouse Capture in Windows Forms
9/1/2020 • 2 minutes to read • Edit Online

Mouse capture refers to when a control takes command of all mouse input. When a control has captured the
mouse, it receives mouse input whether or not the pointer is within its borders.

Setting Mouse Capture


In Windows Forms the mouse is captured by the control when the user presses a mouse button on a control, and
the mouse is released by the control when the user releases the mouse button.
The Capture property of the Control class specifies whether a control has captured the mouse. To determine when
a control loses mouse capture, handle the MouseCaptureChanged event.
Only the foreground window can capture the mouse. When a background window attempts to capture the mouse,
the window receives messages only for mouse events that occur when the mouse pointer is within the visible
portion of the window. Also, even if the foreground window has captured the mouse, the user can still click another
window, bringing it to the foreground. When the mouse is captured, shortcut keys do not work.

See also
Mouse Input in a Windows Forms Application
Drag-and-Drop Functionality in Windows Forms
9/1/2020 • 2 minutes to read • Edit Online

Windows Forms includes a set of methods, events, and classes that implement drag-and-drop behavior. This topic
provides an overview of the drag-and-drop support in Windows Forms. Also see Drag-and-Drop Operations and
Clipboard Support.

Performing Drag-and-Drop Operations


To perform a drag-and-drop operation, use the DoDragDrop method of the Control class. For more information
about how a drag-and-drop operation is performed, see DoDragDrop. To get the rectangle that the mouse pointer
must be dragged over before a drag-and-drop operation begins, use the DragSize property of the
SystemInformation class.

Events Related to Drag-and-Drop Operations


There are two categories of events in a drag and drop operation: events that occur on the current target of the
drag-and-drop operation, and events that occur on the source of the drag and drop operation.
Events on the Current Target
The following table shows the events that occur on the current target of a drag-and-drop operation.

M O USE EVEN T DESC RIP T IO N

DragEnter This event occurs when an object is dragged into the control's
bounds. The handler for this event receives an argument of
type DragEventArgs.

DragOver This event occurs when an object is dragged while the mouse
pointer is within the control's bounds. The handler for this
event receives an argument of type DragEventArgs.

DragDrop This event occurs when a drag-and-drop operation is


completed. The handler for this event receives an argument of
type DragEventArgs.

DragLeave This event occurs when an object is dragged out of the


control's bounds. The handler for this event receives an
argument of type EventArgs.

The DragEventArgs class provides the location of the mouse pointer, the current state of the mouse buttons and
modifier keys of the keyboard, the data being dragged, and DragDropEffects values that specify the operations
allowed by the source of the drag event and the target drop effect for the operation.
Events on the Source
The following table shows the events that occur on the source of the drag-and-drop operation.

M O USE EVEN T DESC RIP T IO N


M O USE EVEN T DESC RIP T IO N

GiveFeedback This event occurs during a drag operation. It provides an


opportunity to give a visual cue to the user that the drag-
and-drop operation is occurring, such as changing the mouse
pointer. The handler for this event receives an argument of
type GiveFeedbackEventArgs.

QueryContinueDrag This event is raised during a drag-and-drop operation and


enables the drag source to determine whether the drag-and-
drop operation should be canceled. The handler for this event
receives an argument of type QueryContinueDragEventArgs.

The QueryContinueDragEventArgs class provides the current state of the mouse buttons and modifier keys of the
keyboard, a value specifying whether the ESC key was pressed, and a DragAction value that can be set to specify
whether the drag-and-drop operation should continue.

See also
Mouse Input in a Windows Forms Application
How to: Simulate Mouse and Keyboard Events in
Code
9/1/2020 • 9 minutes to read • Edit Online

Windows Forms provides several options for programmatically simulating mouse and keyboard input. This topic
provides an overview of these options.

Simulating Mouse Input


The best way to simulate mouse events is to call the On EventName method that raises the mouse event you want
to simulate. This option is usually possible only within custom controls and forms, because the methods that raise
events are protected and cannot be accessed outside the control or form. For example, the following steps illustrate
how to simulate clicking the right mouse button in code.
To programmatically click the right mouse button
1. Create a MouseEventArgs whose Button property is set to the MouseButtons.Right value.
2. Call the OnMouseClick method with this MouseEventArgs as the argument.
For more information on custom controls, see Developing Windows Forms Controls at Design Time.
There are other ways to simulate mouse input. For example, you can programmatically set a control property that
represents a state that is typically set through mouse input (such as the Checked property of the CheckBox control),
or you can directly call the delegate that is attached to the event you want to simulate.

Simulating Keyboard Input


Although you can simulate keyboard input by using the strategies discussed above for mouse input, Windows
Forms also provides the SendKeys class for sending keystrokes to the active application.
Cau t i on

If your application is intended for international use with a variety of keyboards, the use of SendKeys.Send could
yield unpredictable results and should be avoided.
NOTE
The SendKeys class has been updated for the .NET Framework 3.0 to enable its use in applications that run on Windows
Vista. The enhanced security of Windows Vista (known as User Account Control or UAC) prevents the previous
implementation from working as expected.
The SendKeys class is susceptible to timing issues, which some developers have had to work around. The updated
implementation is still susceptible to timing issues, but is slightly faster and may require changes to the workarounds. The
SendKeys class tries to use the previous implementation first, and if that fails, uses the new implementation. As a result, the
SendKeys class may behave differently on different operating systems. Additionally, when the SendKeys class uses the new
implementation, the SendWait method will not wait for messages to be processed when they are sent to another process.
If your application relies on consistent behavior regardless of the operating system, you can force the SendKeys class to use
the new implementation by adding the following application setting to your app.config file.

<appSettings>
<add key="SendKeys" value="SendInput"/>
</appSettings>

To force the SendKeys class to use the previous implementation, use the value "JournalHook" instead.

To send a keystroke to the same application


1. Call the Send or SendWait method of the SendKeys class. The specified keystrokes will be received by the
active control of the application. The following code example uses Send to simulate pressing the ENTER key
when the user double-clicks the surface of the form. This example assumes a Form with a single Button
control that has a tab index of 0.

// Send a key to the button when the user double-clicks anywhere


// on the form.
private:
void Form1_DoubleClick(Object^ sender, EventArgs^ e)
{
// Send the enter key to the button, which triggers the click
// event for the button. This works because the tab stop of
// the button is 0.
SendKeys::Send("{ENTER}");
}

// Send a key to the button when the user double-clicks anywhere


// on the form.
private void Form1_DoubleClick(object sender, EventArgs e)
{
// Send the enter key to the button, which raises the click
// event for the button. This works because the tab stop of
// the button is 0.
SendKeys.Send("{ENTER}");
}
' Send a key to the button when the user double-clicks anywhere
' on the form.
Private Sub Form1_DoubleClick(ByVal sender As Object, _
ByVal e As EventArgs) Handles Me.DoubleClick

' Send the enter key to the button, which raises the click
' event for the button. This works because the tab stop of
' the button is 0.
SendKeys.Send("{ENTER}")
End Sub

To send a keystroke to a different application


1. Activate the application window that will receive the keystrokes, and then call the Send or SendWait method.
Because there is no managed method to activate another application, you must use native Windows
methods to force focus on other applications. The following code example uses platform invoke to call the
FindWindow and SetForegroundWindow methods to activate the Calculator application window, and then calls
SendWait to issue a series of calculations to the Calculator application.

NOTE
The correct parameters of the FindWindow call that locates the Calculator application vary based on your version of
Windows. The following code finds the Calculator application on Windows 7. On Windows Vista, change the first
parameter to "SciCalc". You can use the Spy++ tool, included with Visual Studio, to determine the correct parameters.

// Get a handle to an application window.


public:
[DllImport("USER32.DLL", CharSet = CharSet::Unicode)]
static IntPtr FindWindow(String^ lpClassName, String^ lpWindowName);
public:
// Activate an application window.
[DllImport("USER32.DLL")]
static bool SetForegroundWindow(IntPtr hWnd);

// Send a series of key presses to the Calculator application.


private:
void button1_Click(Object^ sender, EventArgs^ e)
{
// Get a handle to the Calculator application. The window class
// and window name were obtained using the Spy++ tool.
IntPtr calculatorHandle = FindWindow("CalcFrame", "Calculator");

// Verify that Calculator is a running process.


if (calculatorHandle == IntPtr::Zero)
{
MessageBox::Show("Calculator is not running.");
return;
}

// Make Calculator the foreground application and send it


// a set of calculations.
SetForegroundWindow(calculatorHandle);
SendKeys::SendWait("111");
SendKeys::SendWait("*");
SendKeys::SendWait("11");
SendKeys::SendWait("=");
}
// Get a handle to an application window.
[DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(string lpClassName,
string lpWindowName);

// Activate an application window.


[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);

// Send a series of key presses to the Calculator application.


private void button1_Click(object sender, EventArgs e)
{
// Get a handle to the Calculator application. The window class
// and window name were obtained using the Spy++ tool.
IntPtr calculatorHandle = FindWindow("CalcFrame","Calculator");

// Verify that Calculator is a running process.


if (calculatorHandle == IntPtr.Zero)
{
MessageBox.Show("Calculator is not running.");
return;
}

// Make Calculator the foreground application and send it


// a set of calculations.
SetForegroundWindow(calculatorHandle);
SendKeys.SendWait("111");
SendKeys.SendWait("*");
SendKeys.SendWait("11");
SendKeys.SendWait("=");
}

' Get a handle to an application window.


Declare Auto Function FindWindow Lib "USER32.DLL" ( _
ByVal lpClassName As String, _
ByVal lpWindowName As String) As IntPtr

' Activate an application window.


Declare Auto Function SetForegroundWindow Lib "USER32.DLL" _
(ByVal hWnd As IntPtr) As Boolean

' Send a series of key presses to the Calculator application.


Private Sub button1_Click(ByVal sender As Object, _
ByVal e As EventArgs) Handles button1.Click

' Get a handle to the Calculator application. The window class


' and window name were obtained using the Spy++ tool.
Dim calculatorHandle As IntPtr = FindWindow("CalcFrame", "Calculator")

' Verify that Calculator is a running process.


If calculatorHandle = IntPtr.Zero Then
MsgBox("Calculator is not running.")
Return
End If

' Make Calculator the foreground application and send it


' a set of calculations.
SetForegroundWindow(calculatorHandle)
SendKeys.SendWait("111")
SendKeys.SendWait("*")
SendKeys.SendWait("11")
SendKeys.SendWait("=")
End Sub
Example
The following code example is the complete application for the previous code examples.

#using <System.Drawing.dll>
#using <System.Windows.Forms.dll>
#using <System.dll>

using namespace System;


using namespace System::Runtime::InteropServices;
using namespace System::Drawing;
using namespace System::Windows::Forms;

namespace SimulateKeyPress
{

public ref class Form1 : public Form


{
public:
Form1()
{
Button^ button1 = gcnew Button();
button1->Location = Point(10, 10);
button1->TabIndex = 0;
button1->Text = "Click to automate Calculator";
button1->AutoSize = true;
button1->Click += gcnew EventHandler(this, &Form1::button1_Click);

this->DoubleClick += gcnew EventHandler(this,


&Form1::Form1_DoubleClick);
this->Controls->Add(button1);
}

// Get a handle to an application window.


public:
[DllImport("USER32.DLL", CharSet = CharSet::Unicode)]
static IntPtr FindWindow(String^ lpClassName, String^ lpWindowName);
public:
// Activate an application window.
[DllImport("USER32.DLL")]
static bool SetForegroundWindow(IntPtr hWnd);

// Send a series of key presses to the Calculator application.


private:
void button1_Click(Object^ sender, EventArgs^ e)
{
// Get a handle to the Calculator application. The window class
// and window name were obtained using the Spy++ tool.
IntPtr calculatorHandle = FindWindow("CalcFrame", "Calculator");

// Verify that Calculator is a running process.


if (calculatorHandle == IntPtr::Zero)
{
MessageBox::Show("Calculator is not running.");
return;
}

// Make Calculator the foreground application and send it


// a set of calculations.
SetForegroundWindow(calculatorHandle);
SendKeys::SendWait("111");
SendKeys::SendWait("*");
SendKeys::SendWait("11");
SendKeys::SendWait("=");
}

// Send a key to the button when the user double-clicks anywhere


// on the form.
// on the form.
private:
void Form1_DoubleClick(Object^ sender, EventArgs^ e)
{
// Send the enter key to the button, which triggers the click
// event for the button. This works because the tab stop of
// the button is 0.
SendKeys::Send("{ENTER}");
}
};
}

[STAThread]
int main()
{
Application::EnableVisualStyles();
Application::Run(gcnew SimulateKeyPress::Form1());
}

using System;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Windows.Forms;

namespace SimulateKeyPress
{
class Form1 : Form
{
private Button button1 = new Button();

[STAThread]
public static void Main()
{
Application.EnableVisualStyles();
Application.Run(new Form1());
}

public Form1()
{
button1.Location = new Point(10, 10);
button1.TabIndex = 0;
button1.Text = "Click to automate Calculator";
button1.AutoSize = true;
button1.Click += new EventHandler(button1_Click);

this.DoubleClick += new EventHandler(Form1_DoubleClick);


this.Controls.Add(button1);
}

// Get a handle to an application window.


[DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(string lpClassName,
string lpWindowName);

// Activate an application window.


[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);

// Send a series of key presses to the Calculator application.


private void button1_Click(object sender, EventArgs e)
{
// Get a handle to the Calculator application. The window class
// and window name were obtained using the Spy++ tool.
IntPtr calculatorHandle = FindWindow("CalcFrame","Calculator");

// Verify that Calculator is a running process.


if (calculatorHandle == IntPtr.Zero)
{
{
MessageBox.Show("Calculator is not running.");
return;
}

// Make Calculator the foreground application and send it


// a set of calculations.
SetForegroundWindow(calculatorHandle);
SendKeys.SendWait("111");
SendKeys.SendWait("*");
SendKeys.SendWait("11");
SendKeys.SendWait("=");
}

// Send a key to the button when the user double-clicks anywhere


// on the form.
private void Form1_DoubleClick(object sender, EventArgs e)
{
// Send the enter key to the button, which raises the click
// event for the button. This works because the tab stop of
// the button is 0.
SendKeys.Send("{ENTER}");
}
}
}

Imports System.Runtime.InteropServices
Imports System.Drawing
Imports System.Windows.Forms

Namespace SimulateKeyPress

Class Form1
Inherits Form
Private WithEvents button1 As New Button()

<STAThread()> _
Public Shared Sub Main()
Application.EnableVisualStyles()
Application.Run(New Form1())
End Sub

Public Sub New()


button1.Location = New Point(10, 10)
button1.TabIndex = 0
button1.Text = "Click to automate Calculator"
button1.AutoSize = True
Me.Controls.Add(button1)
End Sub

' Get a handle to an application window.


Declare Auto Function FindWindow Lib "USER32.DLL" ( _
ByVal lpClassName As String, _
ByVal lpWindowName As String) As IntPtr

' Activate an application window.


Declare Auto Function SetForegroundWindow Lib "USER32.DLL" _
(ByVal hWnd As IntPtr) As Boolean

' Send a series of key presses to the Calculator application.


Private Sub button1_Click(ByVal sender As Object, _
ByVal e As EventArgs) Handles button1.Click

' Get a handle to the Calculator application. The window class


' and window name were obtained using the Spy++ tool.
Dim calculatorHandle As IntPtr = FindWindow("CalcFrame", "Calculator")

' Verify that Calculator is a running process.


' Verify that Calculator is a running process.
If calculatorHandle = IntPtr.Zero Then
MsgBox("Calculator is not running.")
Return
End If

' Make Calculator the foreground application and send it


' a set of calculations.
SetForegroundWindow(calculatorHandle)
SendKeys.SendWait("111")
SendKeys.SendWait("*")
SendKeys.SendWait("11")
SendKeys.SendWait("=")
End Sub

' Send a key to the button when the user double-clicks anywhere
' on the form.
Private Sub Form1_DoubleClick(ByVal sender As Object, _
ByVal e As EventArgs) Handles Me.DoubleClick

' Send the enter key to the button, which raises the click
' event for the button. This works because the tab stop of
' the button is 0.
SendKeys.Send("{ENTER}")
End Sub

End Class
End Namespace

Compiling the Code


This example requires:
References to the System, System.Drawing and System.Windows.Forms assemblies.

See also
User Input in Windows Forms
How to: Handle User Input Events in Windows Forms
Controls
9/1/2020 • 26 minutes to read • Edit Online

This example demonstrates how to handle most keyboard, mouse, focus, and validation events that can occur in a
Windows Forms control. The text box named TextBoxInput receives the events when it has focus, and information
about each event is written in the text box named TextBoxOutput in the order in which the events are raised. The
application also includes a set of check boxes that can be used to filter which events to report.

Example
#using <System.Drawing.dll>
#using <System.Windows.Forms.dll>
#using <System.dll>

using namespace System;


using namespace System::Drawing;
using namespace System::ComponentModel;
using namespace System::Windows::Forms;

namespace UserInputWalkthrough
{
public ref class Form1 : public Form
{
Label^ lblEvent;
Label^ lblInput;

TextBox^ TextBoxOutput;
TextBox^ TextBoxInput;
GroupBox^ GroupBoxEvents;
Button^ ButtonClear;
LinkLabel^ LinkLabelDrag;

CheckBox^ CheckBoxToggleAll;
CheckBox^ CheckBoxMouse;
CheckBox^ CheckBoxMouseEnter;
CheckBox^ CheckBoxMouseMove;
CheckBox^ CheckBoxMousePoints;
CheckBox^ CheckBoxMouseDrag;
CheckBox^ CheckBoxMouseDragOver;
CheckBox^ CheckBoxKeyboard;
CheckBox^ CheckBoxKeyUpDown;
CheckBox^ CheckBoxFocus;
CheckBox^ CheckBoxValidation;

public:
Form1() : Form()
{
this->Load += gcnew EventHandler(this, &Form1::Form1_Load);

lblEvent = gcnew Label();


lblInput = gcnew Label();

TextBoxOutput = gcnew TextBox();


TextBoxInput = gcnew TextBox();
GroupBoxEvents = gcnew GroupBox();
ButtonClear = gcnew Button();
LinkLabelDrag = gcnew LinkLabel();
CheckBoxToggleAll = gcnew CheckBox();
CheckBoxMouse = gcnew CheckBox();
CheckBoxMouseEnter = gcnew CheckBox();
CheckBoxMouseMove = gcnew CheckBox();
CheckBoxMousePoints = gcnew CheckBox();
CheckBoxMouseDrag = gcnew CheckBox();
CheckBoxMouseDragOver = gcnew CheckBox();
CheckBoxKeyboard = gcnew CheckBox();
CheckBoxKeyUpDown = gcnew CheckBox();
CheckBoxFocus = gcnew CheckBox();
CheckBoxValidation = gcnew CheckBox();
}

private:
void Form1_Load(Object^ sender, EventArgs^ e)
{
this->GroupBoxEvents->SuspendLayout();
this->SuspendLayout();

lblEvent->Location = Point(232, 12);


lblEvent->Size = System::Drawing::Size(98, 14);
lblEvent->AutoSize = true;
lblEvent->Text = "Generated Events:";

lblInput->Location = Point(13, 12);


lblInput->Size = System::Drawing::Size(95, 14);
lblInput->AutoSize = true;
lblInput->Text = "User Input Target:";

TextBoxInput->Location = Point(13, 34);


TextBoxInput->Size = System::Drawing::Size(200, 200);
TextBoxInput->AllowDrop = true;
TextBoxInput->AutoSize = false;
TextBoxInput->Cursor = Cursors::Cross;
TextBoxInput->Multiline = true;
TextBoxInput->TabIndex = 1;

LinkLabelDrag->AllowDrop = true;
LinkLabelDrag->AutoSize = true;
LinkLabelDrag->Location = Point(13, 240);
LinkLabelDrag->Size = System::Drawing::Size(175, 14);
LinkLabelDrag->TabIndex = 2;
LinkLabelDrag->TabStop = true;
LinkLabelDrag->Text = "Click here to use as a drag source";
LinkLabelDrag->Links->Add(gcnew LinkLabel::Link(0,
LinkLabelDrag->Text->Length));

GroupBoxEvents->Location = Point(13, 281);


GroupBoxEvents->Size = System::Drawing::Size(200, 302);
GroupBoxEvents->Text = "Event Filter:";
GroupBoxEvents->TabStop = true;
GroupBoxEvents->TabIndex = 3;
GroupBoxEvents->Controls->Add(CheckBoxMouseEnter);
GroupBoxEvents->Controls->Add(CheckBoxToggleAll);
GroupBoxEvents->Controls->Add(CheckBoxMousePoints);
GroupBoxEvents->Controls->Add(CheckBoxKeyUpDown);
GroupBoxEvents->Controls->Add(CheckBoxMouseDragOver);
GroupBoxEvents->Controls->Add(CheckBoxMouseDrag);
GroupBoxEvents->Controls->Add(CheckBoxValidation);
GroupBoxEvents->Controls->Add(CheckBoxMouseMove);
GroupBoxEvents->Controls->Add(CheckBoxFocus);
GroupBoxEvents->Controls->Add(CheckBoxKeyboard);
GroupBoxEvents->Controls->Add(CheckBoxMouse);

CheckBoxToggleAll->AutoSize = true;
CheckBoxToggleAll->Location = Point(7, 20);
CheckBoxToggleAll->Size = System::Drawing::Size(122, 17);
CheckBoxToggleAll->TabIndex = 4;
CheckBoxToggleAll->Text = "Toggle All Events";
CheckBoxToggleAll->Text = "Toggle All Events";

CheckBoxMouse->AutoSize = true;
CheckBoxMouse->Location = Point(7, 45);
CheckBoxMouse->Size = System::Drawing::Size(137, 17);
CheckBoxMouse->TabIndex = 5;
CheckBoxMouse->Text = "Mouse and Click Events";

CheckBoxMouseEnter->AutoSize = true;
CheckBoxMouseEnter->Location = Point(26, 69);
CheckBoxMouseEnter->Margin =
System::Windows::Forms::Padding(3, 3, 3, 1);
CheckBoxMouseEnter->Size = System::Drawing::Size(151, 17);
CheckBoxMouseEnter->TabIndex = 6;
CheckBoxMouseEnter->Text = "Mouse Enter/Hover/Leave";

CheckBoxMouseMove->AutoSize = true;
CheckBoxMouseMove->Location = Point(26, 89);
CheckBoxMouseMove->Margin =
System::Windows::Forms::Padding(3, 2, 3, 3);
CheckBoxMouseMove->Size = System::Drawing::Size(120, 17);
CheckBoxMouseMove->TabIndex = 7;
CheckBoxMouseMove->Text = "Mouse Move Events";

CheckBoxMousePoints->AutoSize = true;
CheckBoxMousePoints->Location = Point(26, 112);
CheckBoxMousePoints->Margin =
System::Windows::Forms::Padding(3, 3, 3, 1);
CheckBoxMousePoints->Size = System::Drawing::Size(141, 17);
CheckBoxMousePoints->TabIndex = 8;
CheckBoxMousePoints->Text = "Draw Mouse Points";

CheckBoxMouseDrag->AutoSize = true;
CheckBoxMouseDrag->Location = Point(26, 135);
CheckBoxMouseDrag->Margin =
System::Windows::Forms::Padding(3, 1, 3, 3);
CheckBoxMouseDrag->Size = System::Drawing::Size(151, 17);
CheckBoxMouseDrag->TabIndex = 9;
CheckBoxMouseDrag->Text = "Mouse Drag && Drop Events";

CheckBoxMouseDragOver->AutoSize = true;
CheckBoxMouseDragOver->Location = Point(44, 159);
CheckBoxMouseDragOver->Size = System::Drawing::Size(142, 17);
CheckBoxMouseDragOver->TabIndex = 10;
CheckBoxMouseDragOver->Text = "Mouse Drag Over Events";

CheckBoxKeyboard->AutoSize = true;
CheckBoxKeyboard->Location = Point(8, 184);
CheckBoxKeyboard->Size = System::Drawing::Size(103, 17);
CheckBoxKeyboard->TabIndex = 11;
CheckBoxKeyboard->Text = "Keyboard Events";

CheckBoxKeyUpDown->AutoSize = true;
CheckBoxKeyUpDown->Location = Point(26, 207);
CheckBoxKeyUpDown->Margin =
System::Windows::Forms::Padding(3, 3, 3, 1);
CheckBoxKeyUpDown->Size = System::Drawing::Size(133, 17);
CheckBoxKeyUpDown->TabIndex = 12;
CheckBoxKeyUpDown->Text = "Key Up && Down Events";

CheckBoxFocus->AutoSize = true;
CheckBoxFocus->Location = Point(8, 233);
CheckBoxFocus->Margin =
System::Windows::Forms::Padding(3, 2, 3, 3);
CheckBoxFocus->Size = System::Drawing::Size(146, 17);
CheckBoxFocus->TabIndex = 13;
CheckBoxFocus->Text = "Focus && Activation Events";

CheckBoxValidation->AutoSize = true;
CheckBoxValidation->Location = Point(8, 257);
CheckBoxValidation->Location = Point(8, 257);
CheckBoxValidation->Size = System::Drawing::Size(104, 17);
CheckBoxValidation->TabIndex = 14;
CheckBoxValidation->Text = "Validation Events";

TextBoxOutput->Location = Point(232, 34);


TextBoxOutput->Size = System::Drawing::Size(308, 510);
TextBoxOutput->Multiline = true;
TextBoxOutput->CausesValidation = false;
TextBoxOutput->ReadOnly = true;
TextBoxOutput->ScrollBars = ScrollBars::Vertical;
TextBoxOutput->TabIndex = 15;
TextBoxOutput->WordWrap = false;

ButtonClear->Location = Point(232, 560);


ButtonClear->Size = System::Drawing::Size(308, 23);
ButtonClear->TabIndex = 16;
ButtonClear->Text = "Clear Event List";

this->ClientSize = System::Drawing::Size(552, 595);


this->Controls->Add(LinkLabelDrag);
this->Controls->Add(ButtonClear);
this->Controls->Add(GroupBoxEvents);
this->Controls->Add(lblEvent);
this->Controls->Add(lblInput);
this->Controls->Add(TextBoxInput);
this->Controls->Add(TextBoxOutput);
this->Text = "User Input Events";

ButtonClear->Click +=
gcnew EventHandler(this, &Form1::ButtonClear_Click);
TextBoxInput->KeyDown +=
gcnew KeyEventHandler(this, &Form1::TextBoxInput_KeyDown);
TextBoxInput->KeyPress +=
gcnew KeyPressEventHandler(this,
&Form1::TextBoxInput_KeyPress);
TextBoxInput->KeyUp +=
gcnew KeyEventHandler(this, &Form1::TextBoxInput_KeyUp);
TextBoxInput->Click +=
gcnew EventHandler(this, &Form1::TextBoxInput_Click);
TextBoxInput->DoubleClick +=
gcnew EventHandler(this, &Form1::TextBoxInput_DoubleClick);
TextBoxInput->MouseClick +=
gcnew MouseEventHandler(this, &Form1::TextBoxInput_MouseClick);
TextBoxInput->MouseDoubleClick +=
gcnew MouseEventHandler(this,
&Form1::TextBoxInput_MouseDoubleClick);
TextBoxInput->MouseDown +=
gcnew MouseEventHandler(this, &Form1::TextBoxInput_MouseDown);
TextBoxInput->MouseUp +=
gcnew MouseEventHandler(this, &Form1::TextBoxInput_MouseUp);
TextBoxInput->MouseEnter +=
gcnew EventHandler(this, &Form1::TextBoxInput_MouseEnter);
TextBoxInput->MouseHover +=
gcnew EventHandler(this, &Form1::TextBoxInput_MouseHover);
TextBoxInput->MouseLeave +=
gcnew EventHandler(this, &Form1::TextBoxInput_MouseLeave);
TextBoxInput->MouseWheel +=
gcnew MouseEventHandler(this, &Form1::TextBoxInput_MouseWheel);
TextBoxInput->MouseMove +=
gcnew MouseEventHandler(this, &Form1::TextBoxInput_MouseMove);
TextBoxInput->MouseCaptureChanged +=
gcnew EventHandler(this,
&Form1::TextBoxInput_MouseCaptureChanged);
TextBoxInput->DragEnter +=
gcnew DragEventHandler(this, &Form1::TextBoxInput_DragEnter);
TextBoxInput->DragDrop +=
gcnew DragEventHandler(this, &Form1::TextBoxInput_DragDrop);
TextBoxInput->DragOver +=
gcnew DragEventHandler(this, &Form1::TextBoxInput_DragOver);
gcnew DragEventHandler(this, &Form1::TextBoxInput_DragOver);
TextBoxInput->DragLeave +=
gcnew EventHandler(this, &Form1::TextBoxInput_DragLeave);
TextBoxInput->Enter +=
gcnew EventHandler(this, &Form1::TextBoxInput_Enter);
TextBoxInput->Leave +=
gcnew EventHandler(this, &Form1::TextBoxInput_Leave);
TextBoxInput->GotFocus +=
gcnew EventHandler(this, &Form1::TextBoxInput_GotFocus);
TextBoxInput->LostFocus +=
gcnew EventHandler(this, &Form1::TextBoxInput_LostFocus);
TextBoxInput->Validated +=
gcnew EventHandler(this, &Form1::TextBoxInput_Validated);
TextBoxInput->Validating +=
gcnew CancelEventHandler(this,
&Form1::TextBoxInput_Validating);

LinkLabelDrag->MouseDown +=
gcnew MouseEventHandler(this, &Form1::LinkLabelDrag_MouseDown);
LinkLabelDrag->GiveFeedback +=
gcnew GiveFeedbackEventHandler(this,
&Form1::LinkLabelDrag_GiveFeedback);

CheckBoxToggleAll->CheckedChanged +=
gcnew EventHandler(this,
&Form1::CheckBoxToggleAll_CheckedChanged);
CheckBoxMouse->CheckedChanged +=
gcnew EventHandler(this, &Form1::CheckBoxMouse_CheckedChanged);
CheckBoxMouseDrag->CheckedChanged +=
gcnew EventHandler(this,
&Form1::CheckBoxMouseDrag_CheckedChanged);
CheckBoxMouseEnter->CheckedChanged +=
gcnew EventHandler(this,
&Form1::CheckBoxMouseMove_CheckedChanged);
CheckBoxMouseMove->CheckedChanged +=
gcnew EventHandler(this,
&Form1::CheckBoxMouseMove_CheckedChanged);
CheckBoxKeyboard->CheckedChanged +=
gcnew EventHandler(this,
&Form1::CheckBoxKeyboard_CheckedChanged);

this->GroupBoxEvents->ResumeLayout(false);
this->GroupBoxEvents->PerformLayout();
this->ResumeLayout(false);
this->PerformLayout();
CheckAllChildCheckBoxes(this, true);
}

// Recursively search the form for all contained checkboxes and


// initially check them
private:
void CheckAllChildCheckBoxes(Control^ parent, bool value)
{
CheckBox^ box;
for each (Control^ currentControl in parent->Controls)
{
if (dynamic_cast<CheckBox^>(currentControl))
{
box = (CheckBox^)currentControl;
box->Checked = value;
}

// Recurse if control contains other controls


if (currentControl->Controls->Count > 0)
{
CheckAllChildCheckBoxes(currentControl, value);
}
}
}
// All-purpose method for displaying a line of text in one of the
// text boxes.
private:
void DisplayLine(String^ line)
{
TextBoxOutput->AppendText(line);
TextBoxOutput->AppendText(Environment::NewLine);
}

// Click event handler for the button that clears the text box.
private:
void ButtonClear_Click(Object^ sender, EventArgs^ e)
{
TextBoxOutput->Invalidate();
TextBoxOutput->Clear();
}

private:
void TextBoxInput_KeyDown(Object^ sender, KeyEventArgs^ e)
{
if (CheckBoxKeyUpDown->Checked)
{
DisplayLine("KeyDown: " + e->KeyData.ToString());
}
}

private:
void TextBoxInput_KeyUp(Object^ sender, KeyEventArgs^ e)
{
if (CheckBoxKeyUpDown->Checked)
{
DisplayLine("KeyUp: " + e->KeyData.ToString());
}
}

private:
void TextBoxInput_KeyPress(Object^ sender,
KeyPressEventArgs^ e)
{
if (CheckBoxKeyboard->Checked)
{
if (Char::IsWhiteSpace(e->KeyChar))
{
DisplayLine("KeyPress: WS");
}
else
{
DisplayLine("KeyPress: " + e->KeyChar.ToString());
}
}
}

private:
void TextBoxInput_Click(Object^ sender, EventArgs^ e)
{
if (CheckBoxMouse->Checked)
{
DisplayLine("Click event");
}
}

private:
void TextBoxInput_DoubleClick(Object^ sender, EventArgs^ e)
{
if (CheckBoxMouse->Checked)
{
DisplayLine("DoubleClick event");
}
}

private:
void TextBoxInput_MouseClick(Object^ sender, MouseEventArgs^ e)
{
if (CheckBoxMouse->Checked)
{
DisplayLine("MouseClick: " + e->Button.ToString() +
" " + e->Location.ToString());
}
}

private:
void TextBoxInput_MouseDoubleClick(Object^ sender,
MouseEventArgs^ e)
{
if (CheckBoxMouse->Checked)
{
DisplayLine("MouseDoubleClick: " + e->Button.ToString() +
" " + e->Location.ToString());
}
}

private:
void TextBoxInput_MouseDown(Object^ sender,
MouseEventArgs^ e)
{
if (CheckBoxMouse->Checked)
{
DisplayLine("MouseDown: " + e->Button.ToString() +
" " + e->Location.ToString());
}
}

private:
void TextBoxInput_MouseUp(Object^ sender,
MouseEventArgs^ e)
{
if (CheckBoxMouse->Checked)
{
DisplayLine("MouseUp: " + e->Button.ToString() +
" " + e->Location.ToString());
}

// The TextBox control was designed to change focus only on


// the primary click, so force focus to avoid user confusion.
if (!TextBoxInput->Focused)
{
TextBoxInput->Focus();
}
}

private:
void TextBoxInput_MouseEnter(Object^ sender, EventArgs^ e)
{
if (CheckBoxMouseEnter->Checked)
{
DisplayLine("MouseEnter event");
}
}

private:
void TextBoxInput_MouseHover(Object^ sender, EventArgs^ e)
{
if (CheckBoxMouseEnter->Checked)
{
DisplayLine("MouseHover event");
}
}
}

private:
void TextBoxInput_MouseLeave(Object^ sender, EventArgs^ e)
{
if (CheckBoxMouseEnter->Checked)
{
DisplayLine("MouseLeave event");
}
}

private:
void TextBoxInput_MouseWheel(Object^ sender,
MouseEventArgs^ e)
{
if (CheckBoxMouse->Checked)
{
DisplayLine("MouseWheel: " + e->Delta.ToString() +
" detents at " + e->Location.ToString());
}
}

private:
void TextBoxInput_MouseMove(Object^ sender,
MouseEventArgs^ e)
{
if (CheckBoxMouseMove->Checked)
{
DisplayLine("MouseMove: " + e->Button.ToString() + " " +
e->Location.ToString());
}

if (CheckBoxMousePoints->Checked)
{
Graphics^ g = TextBoxInput->CreateGraphics();
g->FillRectangle(Brushes::Black, e->Location.X,
e->Location.Y, 1, 1);
delete g;
}
}

private:
void TextBoxInput_MouseCaptureChanged(Object^ sender,
EventArgs^ e)
{
if (CheckBoxMouseDrag->Checked)
{
DisplayLine("MouseCaptureChanged event");
}
}

private:
void TextBoxInput_DragEnter(Object^ sender, DragEventArgs^ e)
{
if (CheckBoxMouseDrag->Checked)
{
Point^ pt = gcnew Point(e->X, e->Y);
DisplayLine("DragEnter: " +
CovertKeyStateToString(e->KeyState)
+ " at " + pt->ToString());
}
}

private:
void TextBoxInput_DragDrop(Object^ sender, DragEventArgs^ e)
{
if (CheckBoxMouseDrag->Checked)
{
Point^ pt = gcnew Point(e->X, e->Y);
DisplayLine("DragDrop: " +
DisplayLine("DragDrop: " +
CovertKeyStateToString(e->KeyState)
+ " at " + pt->ToString());
}
}

private:
void TextBoxInput_DragOver(Object^ sender, DragEventArgs^ e)
{
if (CheckBoxMouseDragOver->Checked)
{
Point^ pt = gcnew Point(e->X, e->Y);
DisplayLine("DragOver: " +
CovertKeyStateToString(e->KeyState)
+ " at " + pt->ToString());
}

// Allow if drop data is of type string.


if (!e->Data->GetDataPresent(String::typeid))
{
e->Effect = DragDropEffects::None;
}
else
{
e->Effect = DragDropEffects::Copy;
}
}

private:
void TextBoxInput_DragLeave(Object^ sender,
EventArgs^ e)
{
if (CheckBoxMouseDrag->Checked)
{
DisplayLine("DragLeave event");
}
}

private:
static String^ CovertKeyStateToString(int keyState)
{
String^ keyString = "None";

// Which button was pressed?


if ((keyState & 1) == 1)
{
keyString = "Left";
}
else if ((keyState & 2) == 2)
{
keyString = "Right";
}
else if ((keyState & 16) == 16)
{
keyString = "Middle";
}

// Are one or more modifier keys also pressed?


if ((keyState & 4) == 4)
{
keyString += "+SHIFT";
}

if ((keyState & 8) == 8)
{
keyString += "+CTRL";
}

if ((keyState & 32) == 32)


{
{
keyString += "+ALT";
}

return keyString;
}

private:
void TextBoxInput_Enter(Object^ sender, EventArgs^ e)
{
if (CheckBoxFocus->Checked)
{
DisplayLine("Enter event");
}
}

private:
void TextBoxInput_Leave(Object^ sender, EventArgs^ e)
{
if (CheckBoxFocus->Checked)
{
DisplayLine("Leave event");
}
}

private:
void TextBoxInput_GotFocus(Object^ sender, EventArgs^ e)
{
if (CheckBoxFocus->Checked)
{
DisplayLine("GotFocus event");
}
}

private:
void TextBoxInput_LostFocus(Object^ sender, EventArgs^ e)
{
if (CheckBoxFocus->Checked)
{
DisplayLine("LostFocus event");
}
}

private:
void TextBoxInput_Validated(Object^ sender, EventArgs^ e)
{
if (CheckBoxValidation->Checked)
{
DisplayLine("Validated event");
}
}

private:
void TextBoxInput_Validating(
Object^ sender, CancelEventArgs^ e)
{
if (CheckBoxValidation->Checked)
{
DisplayLine("Validating event");
}
}

private:
void CheckBoxToggleAll_CheckedChanged(
Object^ sender, EventArgs^ e)
{
if (dynamic_cast<CheckBox^>(sender))
{
CheckAllChildCheckBoxes(this, ((CheckBox^)sender)->Checked);
}
}
}

private:
void CheckBoxMouse_CheckedChanged(
Object^ sender, EventArgs^ e)
{
ConfigureCheckBoxSettings();
}

private:
void CheckBoxMouseDrag_CheckedChanged(
Object^ sender, EventArgs^ e)
{
ConfigureCheckBoxSettings();
}

private:
void CheckBoxKeyboard_CheckedChanged(
Object^ sender, EventArgs^ e)
{
ConfigureCheckBoxSettings();
}

private:
void CheckBoxMouseMove_CheckedChanged(
Object^ sender, EventArgs^ e)
{
ConfigureCheckBoxSettings();
}

// Reconcile dependencies between the check box


// selection choices.
private:
void ConfigureCheckBoxSettings()
{
// CheckBoxMouse is a top-level check box.
if (!CheckBoxMouse->Checked)
{
CheckBoxMouseEnter->Enabled = false;
CheckBoxMouseMove->Enabled = false;
CheckBoxMouseDrag->Enabled = false;
CheckBoxMouseDragOver->Enabled = false;
CheckBoxMousePoints->Enabled = false;
}
else
{
CheckBoxMouseEnter->Enabled = true;
CheckBoxMouseMove->Enabled = true;
CheckBoxMouseDrag->Enabled = true;
CheckBoxMousePoints->Enabled = true;

// Enable children depending on the state of the parent.


if (!CheckBoxMouseDrag->Checked)
{
CheckBoxMouseDragOver->Enabled = false;
}
else
{
CheckBoxMouseDragOver->Enabled = true;
}
}

if (!CheckBoxKeyboard->Checked)
{
CheckBoxKeyUpDown->Enabled = false;
}
else
{
CheckBoxKeyUpDown->Enabled = true;
CheckBoxKeyUpDown->Enabled = true;
}
}

private:
void LinkLabelDrag_MouseDown(Object^ sender, MouseEventArgs^ e)
{
String^ data = "Sample Data";
LinkLabelDrag->DoDragDrop(data, DragDropEffects::All);
}

private:
void LinkLabelDrag_GiveFeedback(Object^ sender,
GiveFeedbackEventArgs^ e)
{
if ((e->Effect & DragDropEffects::Copy) ==
DragDropEffects::Copy)
{
LinkLabelDrag->Cursor = Cursors::HSplit;
}
else
{
LinkLabelDrag->Cursor = Cursors::Default;
}
}
};
}

[STAThread]
int main()
{
Application::EnableVisualStyles();
Application::Run(gcnew UserInputWalkthrough::Form1());
}

using System;
using System.Drawing;
using System.ComponentModel;
using System.Windows.Forms;

namespace UserInputWalkthrough
{
public class Form1 : Form
{
Label Label1 = new Label();
Label Label2 = new Label();
TextBox TextBoxOutput = new TextBox();
TextBox TextBoxInput = new TextBox();
GroupBox GroupBoxEvents = new GroupBox();
Button ButtonClear = new Button();
LinkLabel LinkLabelDrag = new LinkLabel();

CheckBox CheckBoxToggleAll = new CheckBox();


CheckBox CheckBoxMouse = new CheckBox();
CheckBox CheckBoxMouseEnter = new CheckBox();
CheckBox CheckBoxMouseMove = new CheckBox();
CheckBox CheckBoxMousePoints = new CheckBox();
CheckBox CheckBoxMouseDrag = new CheckBox();
CheckBox CheckBoxMouseDragOver = new CheckBox();
CheckBox CheckBoxKeyboard = new CheckBox();
CheckBox CheckBoxKeyUpDown = new CheckBox();
CheckBox CheckBoxFocus = new CheckBox();
CheckBox CheckBoxValidation = new CheckBox();

[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.EnableVisualStyles();
Application.Run(new Form1());
}

public Form1()
: base()
{
this.Load += new EventHandler(Form1_Load);
}

private void Form1_Load(object sender, EventArgs e)


{
this.GroupBoxEvents.SuspendLayout();
this.SuspendLayout();

Label1.Location = new Point(232, 12);


Label1.Size = new Size(98, 14);
Label1.AutoSize = true;
Label1.Text = "Generated Events:";

Label2.Location = new Point(13, 12);


Label2.Size = new Size(95, 14);
Label2.AutoSize = true;
Label2.Text = "User Input Target:";

TextBoxInput.Location = new Point(13, 34);


TextBoxInput.Size = new Size(200, 200);
TextBoxInput.AllowDrop = true;
TextBoxInput.AutoSize = false;
TextBoxInput.Cursor = Cursors.Cross;
TextBoxInput.Multiline = true;
TextBoxInput.TabIndex = 1;

LinkLabelDrag.AllowDrop = true;
LinkLabelDrag.AutoSize = true;
LinkLabelDrag.Location = new Point(13, 240);
LinkLabelDrag.Size = new Size(175, 14);
LinkLabelDrag.TabIndex = 2;
LinkLabelDrag.TabStop = true;
LinkLabelDrag.Text = "Click here to use as a drag source";
LinkLabelDrag.Links.Add(new LinkLabel.Link(0,
LinkLabelDrag.Text.Length));

GroupBoxEvents.Location = new Point(13, 281);


GroupBoxEvents.Size = new Size(200, 302);
GroupBoxEvents.Text = "Event Filter:";
GroupBoxEvents.TabStop = true;
GroupBoxEvents.TabIndex = 3;
GroupBoxEvents.Controls.Add(CheckBoxMouseEnter);
GroupBoxEvents.Controls.Add(CheckBoxToggleAll);
GroupBoxEvents.Controls.Add(CheckBoxMousePoints);
GroupBoxEvents.Controls.Add(CheckBoxKeyUpDown);
GroupBoxEvents.Controls.Add(CheckBoxMouseDragOver);
GroupBoxEvents.Controls.Add(CheckBoxMouseDrag);
GroupBoxEvents.Controls.Add(CheckBoxValidation);
GroupBoxEvents.Controls.Add(CheckBoxMouseMove);
GroupBoxEvents.Controls.Add(CheckBoxFocus);
GroupBoxEvents.Controls.Add(CheckBoxKeyboard);
GroupBoxEvents.Controls.Add(CheckBoxMouse);

CheckBoxToggleAll.AutoSize = true;
CheckBoxToggleAll.Location = new Point(7, 20);
CheckBoxToggleAll.Size = new Size(122, 17);
CheckBoxToggleAll.TabIndex = 4;
CheckBoxToggleAll.Text = "Toggle All Events";

CheckBoxMouse.AutoSize = true;
CheckBoxMouse.Location = new Point(7, 45);
CheckBoxMouse.Size = new Size(137, 17);
CheckBoxMouse.TabIndex = 5;
CheckBoxMouse.TabIndex = 5;
CheckBoxMouse.Text = "Mouse and Click Events";

CheckBoxMouseEnter.AutoSize = true;
CheckBoxMouseEnter.Location = new Point(26, 69);
CheckBoxMouseEnter.Margin = new Padding(3, 3, 3, 1);
CheckBoxMouseEnter.Size = new System.Drawing.Size(151, 17);
CheckBoxMouseEnter.TabIndex = 6;
CheckBoxMouseEnter.Text = "Mouse Enter/Hover/Leave";

CheckBoxMouseMove.AutoSize = true;
CheckBoxMouseMove.Location = new Point(26, 89);
CheckBoxMouseMove.Margin = new Padding(3, 2, 3, 3);
CheckBoxMouseMove.Size = new Size(120, 17);
CheckBoxMouseMove.TabIndex = 7;
CheckBoxMouseMove.Text = "Mouse Move Events";

CheckBoxMousePoints.AutoSize = true;
CheckBoxMousePoints.Location = new Point(26, 112);
CheckBoxMousePoints.Margin = new Padding(3, 3, 3, 1);
CheckBoxMousePoints.Size = new Size(141, 17);
CheckBoxMousePoints.TabIndex = 8;
CheckBoxMousePoints.Text = "Draw Mouse Points";

CheckBoxMouseDrag.AutoSize = true;
CheckBoxMouseDrag.Location = new Point(26, 135);
CheckBoxMouseDrag.Margin = new Padding(3, 1, 3, 3);
CheckBoxMouseDrag.Size = new Size(151, 17);
CheckBoxMouseDrag.TabIndex = 9;
CheckBoxMouseDrag.Text = "Mouse Drag && Drop Events";

CheckBoxMouseDragOver.AutoSize = true;
CheckBoxMouseDragOver.Location = new Point(44, 159);
CheckBoxMouseDragOver.Size = new Size(142, 17);
CheckBoxMouseDragOver.TabIndex = 10;
CheckBoxMouseDragOver.Text = "Mouse Drag Over Events";

CheckBoxKeyboard.AutoSize = true;
CheckBoxKeyboard.Location = new Point(8, 184);
CheckBoxKeyboard.Size = new Size(103, 17);
CheckBoxKeyboard.TabIndex = 11;
CheckBoxKeyboard.Text = "Keyboard Events";

CheckBoxKeyUpDown.AutoSize = true;
CheckBoxKeyUpDown.Location = new Point(26, 207);
CheckBoxKeyUpDown.Margin = new Padding(3, 3, 3, 1);
CheckBoxKeyUpDown.Size = new Size(133, 17);
CheckBoxKeyUpDown.TabIndex = 12;
CheckBoxKeyUpDown.Text = "Key Up && Down Events";

CheckBoxFocus.AutoSize = true;
CheckBoxFocus.Location = new Point(8, 233);
CheckBoxFocus.Margin = new Padding(3, 2, 3, 3);
CheckBoxFocus.Size = new Size(146, 17);
CheckBoxFocus.TabIndex = 13;
CheckBoxFocus.Text = "Focus && Activation Events";

CheckBoxValidation.AutoSize = true;
CheckBoxValidation.Location = new Point(8, 257);
CheckBoxValidation.Size = new Size(104, 17);
CheckBoxValidation.TabIndex = 14;
CheckBoxValidation.Text = "Validation Events";

TextBoxOutput.Location = new Point(232, 34);


TextBoxOutput.Size = new Size(308, 510);
TextBoxOutput.Multiline = true;
TextBoxOutput.CausesValidation = false;
TextBoxOutput.ReadOnly = true;
TextBoxOutput.ScrollBars = ScrollBars.Vertical;
TextBoxOutput.TabIndex = 15;
TextBoxOutput.WordWrap = false;

ButtonClear.Location = new Point(232, 560);


ButtonClear.Size = new Size(308, 23);
ButtonClear.TabIndex = 16;
ButtonClear.Text = "Clear Event List";

this.ClientSize = new Size(552, 595);


this.Controls.Add(LinkLabelDrag);
this.Controls.Add(ButtonClear);
this.Controls.Add(GroupBoxEvents);
this.Controls.Add(Label1);
this.Controls.Add(Label2);
this.Controls.Add(TextBoxInput);
this.Controls.Add(TextBoxOutput);
this.Text = "User Input Events";

ButtonClear.Click +=
new EventHandler(ButtonClear_Click);
TextBoxInput.KeyDown +=
new KeyEventHandler(TextBoxInput_KeyDown);
TextBoxInput.KeyPress +=
new KeyPressEventHandler(TextBoxInput_KeyPress);
TextBoxInput.KeyUp +=
new KeyEventHandler(TextBoxInput_KeyUp);
TextBoxInput.Click +=
new EventHandler(TextBoxInput_Click);
TextBoxInput.DoubleClick +=
new EventHandler(TextBoxInput_DoubleClick);
TextBoxInput.MouseClick +=
new MouseEventHandler(TextBoxInput_MouseClick);
TextBoxInput.MouseDoubleClick +=
new MouseEventHandler(TextBoxInput_MouseDoubleClick);
TextBoxInput.MouseDown +=
new MouseEventHandler(TextBoxInput_MouseDown);
TextBoxInput.MouseUp +=
new MouseEventHandler(TextBoxInput_MouseUp);
TextBoxInput.MouseEnter +=
new EventHandler(TextBoxInput_MouseEnter);
TextBoxInput.MouseHover +=
new EventHandler(TextBoxInput_MouseHover);
TextBoxInput.MouseLeave +=
new EventHandler(TextBoxInput_MouseLeave);
TextBoxInput.MouseWheel +=
new MouseEventHandler(TextBoxInput_MouseWheel);
TextBoxInput.MouseMove +=
new MouseEventHandler(TextBoxInput_MouseMove);
TextBoxInput.MouseCaptureChanged +=
new EventHandler(TextBoxInput_MouseCaptureChanged);
TextBoxInput.DragEnter +=
new DragEventHandler(TextBoxInput_DragEnter);
TextBoxInput.DragDrop +=
new DragEventHandler(TextBoxInput_DragDrop);
TextBoxInput.DragOver +=
new DragEventHandler(TextBoxInput_DragOver);
TextBoxInput.DragLeave +=
new EventHandler(TextBoxInput_DragLeave);
TextBoxInput.Enter +=
new EventHandler(TextBoxInput_Enter);
TextBoxInput.Leave +=
new EventHandler(TextBoxInput_Leave);
TextBoxInput.GotFocus +=
new EventHandler(TextBoxInput_GotFocus);
TextBoxInput.LostFocus +=
new EventHandler(TextBoxInput_LostFocus);
TextBoxInput.Validated +=
new EventHandler(TextBoxInput_Validated);
TextBoxInput.Validating +=
TextBoxInput.Validating +=
new CancelEventHandler(TextBoxInput_Validating);

LinkLabelDrag.MouseDown +=
new MouseEventHandler(LinkLabelDrag_MouseDown);
LinkLabelDrag.GiveFeedback +=
new GiveFeedbackEventHandler(LinkLabelDrag_GiveFeedback);

CheckBoxToggleAll.CheckedChanged +=
new EventHandler(CheckBoxToggleAll_CheckedChanged);
CheckBoxMouse.CheckedChanged +=
new EventHandler(CheckBoxMouse_CheckedChanged);
CheckBoxMouseDrag.CheckedChanged +=
new EventHandler(CheckBoxMouseDrag_CheckedChanged);
CheckBoxMouseEnter.CheckedChanged +=
new EventHandler(CheckBoxMouseMove_CheckedChanged);
CheckBoxMouseMove.CheckedChanged +=
new EventHandler(CheckBoxMouseMove_CheckedChanged);
CheckBoxKeyboard.CheckedChanged +=
new EventHandler(CheckBoxKeyboard_CheckedChanged);

this.GroupBoxEvents.ResumeLayout(false);
this.GroupBoxEvents.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
CheckAllChildCheckBoxes(this, true);
}

// Recursively search the form for all contained checkboxes and


// initially check them
private void CheckAllChildCheckBoxes(Control parent, bool value)
{
CheckBox box;
foreach (Control currentControl in parent.Controls)
{
if (currentControl is CheckBox)
{
box = (CheckBox)currentControl;
box.Checked = value;
}

// Recurse if control contains other controls


if (currentControl.Controls.Count > 0)
{
CheckAllChildCheckBoxes(currentControl, value);
}
}
}

// All-purpose method for displaying a line of text in one of the


// text boxes.
private void DisplayLine(string line)
{
TextBoxOutput.AppendText(line);
TextBoxOutput.AppendText(Environment.NewLine);
}

// Click event handler for the button that clears the text box.
private void ButtonClear_Click(object sender, EventArgs e)
{
TextBoxOutput.Invalidate();
TextBoxOutput.Clear();
}

private void TextBoxInput_KeyDown(object sender, KeyEventArgs e)


{
if (CheckBoxKeyUpDown.Checked)
{
DisplayLine("KeyDown: " + e.KeyData.ToString());
}
}
}

private void TextBoxInput_KeyUp(object sender, KeyEventArgs e)


{
if (CheckBoxKeyUpDown.Checked)
{
DisplayLine("KeyUp: " + e.KeyData.ToString());
}
}

private void TextBoxInput_KeyPress(object sender,


KeyPressEventArgs e)
{
if (CheckBoxKeyboard.Checked)
{
if (Char.IsWhiteSpace(e.KeyChar))
{
DisplayLine("KeyPress: WS");
}
else
{
DisplayLine("KeyPress: " + e.KeyChar.ToString());
}
}
}

private void TextBoxInput_Click(object sender, EventArgs e)


{
if (CheckBoxMouse.Checked)
{
DisplayLine("Click event");
}
}

private void TextBoxInput_DoubleClick(object sender, EventArgs e)


{
if (CheckBoxMouse.Checked)
{
DisplayLine("DoubleClick event");
}
}

private void TextBoxInput_MouseClick(object sender, MouseEventArgs e)


{
if (CheckBoxMouse.Checked)
{
DisplayLine("MouseClick: " + e.Button.ToString() +
" " + e.Location.ToString());
}
}

private void TextBoxInput_MouseDoubleClick(object sender,


MouseEventArgs e)
{
if (CheckBoxMouse.Checked)
{
DisplayLine("MouseDoubleClick: " + e.Button.ToString() +
" " + e.Location.ToString());
}
}

private void TextBoxInput_MouseDown(object sender,


MouseEventArgs e)
{
if (CheckBoxMouse.Checked)
{
DisplayLine("MouseDown: " + e.Button.ToString() +
" " + e.Location.ToString());
}
}
}

private void TextBoxInput_MouseUp(object sender,


MouseEventArgs e)
{
if (CheckBoxMouse.Checked)
{
DisplayLine("MouseUp: " + e.Button.ToString() +
" " + e.Location.ToString());
}

// The TextBox control was designed to change focus only on


// the primary click, so force focus to avoid user confusion.
if (!TextBoxInput.Focused)
{
TextBoxInput.Focus();
}
}

private void TextBoxInput_MouseEnter(object sender, EventArgs e)


{
if (CheckBoxMouseEnter.Checked)
{
DisplayLine("MouseEnter event");
}
}

private void TextBoxInput_MouseHover(object sender, EventArgs e)


{
if (CheckBoxMouseEnter.Checked)
{
DisplayLine("MouseHover event");
}
}

private void TextBoxInput_MouseLeave(object sender, EventArgs e)


{
if (CheckBoxMouseEnter.Checked)
{
DisplayLine("MouseLeave event");
}
}

private void TextBoxInput_MouseWheel(object sender,


MouseEventArgs e)
{
if (CheckBoxMouse.Checked)
{
DisplayLine("MouseWheel: " + e.Delta.ToString() +
" detents at " + e.Location.ToString());
}
}

private void TextBoxInput_MouseMove(object sender,


MouseEventArgs e)
{
if (CheckBoxMouseMove.Checked)
{
DisplayLine("MouseMove: " + e.Button.ToString() + " " +
e.Location.ToString());
}

if (CheckBoxMousePoints.Checked)
{
Graphics g = TextBoxInput.CreateGraphics();
g.FillRectangle(Brushes.Black, e.Location.X,
e.Location.Y, 1, 1);
g.Dispose();
}
}
}

private void TextBoxInput_MouseCaptureChanged(object sender,


EventArgs e)
{
if (CheckBoxMouseDrag.Checked)
{
DisplayLine("MouseCaptureChanged event");
}
}

private void TextBoxInput_DragEnter(object sender,


DragEventArgs e)
{
if (CheckBoxMouseDrag.Checked)
{
Point pt = new Point(e.X, e.Y);
DisplayLine("DragEnter: " +
CovertKeyStateToString(e.KeyState)
+ " at " + pt.ToString());
}
}

private void TextBoxInput_DragDrop(object sender,


DragEventArgs e)
{
if (CheckBoxMouseDrag.Checked)
{
Point pt = new Point(e.X, e.Y);
DisplayLine("DragDrop: " +
CovertKeyStateToString(e.KeyState)
+ " at " + pt.ToString());
}
}

private void TextBoxInput_DragOver(object sender,


DragEventArgs e)
{
if (CheckBoxMouseDragOver.Checked)
{
Point pt = new Point(e.X, e.Y);
DisplayLine("DragOver: " +
CovertKeyStateToString(e.KeyState)
+ " at " + pt.ToString());
}

// Allow if drop data is of type string.


if (!e.Data.GetDataPresent(typeof(String)))
{
e.Effect = DragDropEffects.None;
}
else
{
e.Effect = DragDropEffects.Copy;
}
}

private void TextBoxInput_DragLeave(object sender,


EventArgs e)
{
if (CheckBoxMouseDrag.Checked)
{
DisplayLine("DragLeave event");
}
}

private string CovertKeyStateToString(int keyState)


{
string keyString = "None";
string keyString = "None";

// Which button was pressed?


if ((keyState & 1) == 1)
{
keyString = "Left";
}
else if ((keyState & 2) == 2)
{
keyString = "Right";
}
else if ((keyState & 16) == 16)
{
keyString = "Middle";
}

// Are one or more modifier keys also pressed?


if ((keyState & 4) == 4)
{
keyString += "+SHIFT";
}

if ((keyState & 8) == 8)
{
keyString += "+CTRL";
}

if ((keyState & 32) == 32)


{
keyString += "+ALT";
}

return keyString;
}

private void TextBoxInput_Enter(object sender, EventArgs e)


{
if (CheckBoxFocus.Checked)
{
DisplayLine("Enter event");
}
}

private void TextBoxInput_Leave(object sender, EventArgs e)


{
if (CheckBoxFocus.Checked)
{
DisplayLine("Leave event");
}
}

private void TextBoxInput_GotFocus(object sender, EventArgs e)


{
if (CheckBoxFocus.Checked)
{
DisplayLine("GotFocus event");
}
}

private void TextBoxInput_LostFocus(object sender, EventArgs e)


{
if (CheckBoxFocus.Checked)
{
DisplayLine("LostFocus event");
}
}

private void TextBoxInput_Validated(object sender, EventArgs e)


{
if (CheckBoxValidation.Checked)
{
DisplayLine("Validated event");
}
}

private void TextBoxInput_Validating(


object sender, CancelEventArgs e)
{
if (CheckBoxValidation.Checked)
{
DisplayLine("Validating event");
}
}

private void CheckBoxToggleAll_CheckedChanged(


object sender, EventArgs e)
{
if (sender is CheckBox)
{
CheckAllChildCheckBoxes(this, ((CheckBox)sender).Checked);
}
}

private void CheckBoxMouse_CheckedChanged(


object sender, EventArgs e)
{
ConfigureCheckBoxSettings();
}

private void CheckBoxMouseDrag_CheckedChanged(


object sender, EventArgs e)
{
ConfigureCheckBoxSettings();
}

private void CheckBoxKeyboard_CheckedChanged(


object sender, EventArgs e)
{
ConfigureCheckBoxSettings();
}

private void CheckBoxMouseMove_CheckedChanged(


object sender, EventArgs e)
{
ConfigureCheckBoxSettings();
}

// Reconcile dependencies between the check box


// selection choices.
private void ConfigureCheckBoxSettings()
{
// CheckBoxMouse is a top-level check box.
if (!CheckBoxMouse.Checked)
{
CheckBoxMouseEnter.Enabled = false;
CheckBoxMouseMove.Enabled = false;
CheckBoxMouseDrag.Enabled = false;
CheckBoxMouseDragOver.Enabled = false;
CheckBoxMousePoints.Enabled = false;
}
else
{
CheckBoxMouseEnter.Enabled = true;
CheckBoxMouseMove.Enabled = true;
CheckBoxMouseDrag.Enabled = true;
CheckBoxMousePoints.Enabled = true;

// Enable children depending on the state of the parent.


if (!CheckBoxMouseDrag.Checked)
{
CheckBoxMouseDragOver.Enabled = false;
}
else
{
CheckBoxMouseDragOver.Enabled = true;
}
}

if (!CheckBoxKeyboard.Checked)
{
CheckBoxKeyUpDown.Enabled = false;
}
else
{
CheckBoxKeyUpDown.Enabled = true;
}
}

private void LinkLabelDrag_MouseDown(object sender,


MouseEventArgs e)
{
string data = "Sample Data";
LinkLabelDrag.DoDragDrop(data, DragDropEffects.All);
}

private void LinkLabelDrag_GiveFeedback(object sender,


GiveFeedbackEventArgs e)
{
if ((e.Effect & DragDropEffects.Copy) ==
DragDropEffects.Copy)
{
LinkLabelDrag.Cursor = Cursors.HSplit;
}
else
{
LinkLabelDrag.Cursor = Cursors.Default;
}
}
}
}

Imports System.Drawing
Imports System.ComponentModel
Imports System.Windows.Forms

Namespace UserInputWalkthrough

Public Class Form1


Inherits Form

Dim Label1 As New Label


Dim Label2 As New Label
Dim TextBoxOutput As New TextBox
Dim WithEvents TextBoxInput As New TextBox
Dim WithEvents GroupBoxEvents As New GroupBox
Dim WithEvents ButtonClear As New Button
Dim WithEvents LinkLabelDrag As New LinkLabel

Dim WithEvents CheckBoxToggleAll As New CheckBox


Dim WithEvents CheckBoxMouse As New CheckBox
Dim WithEvents CheckBoxMouseEnter As New CheckBox
Dim WithEvents CheckBoxMouseMove As New CheckBox
Dim WithEvents CheckBoxMousePoints As New CheckBox
Dim WithEvents CheckBoxMouseDrag As New CheckBox
Dim WithEvents CheckBoxMouseDragOver As New CheckBox
Dim WithEvents CheckBoxKeyboard As New CheckBox
Dim WithEvents CheckBoxKeyUpDown As New CheckBox
Dim WithEvents CheckBoxFocus As New CheckBox
Dim WithEvents CheckBoxValidation As New CheckBox

<STAThread()> _
Shared Sub Main()
Application.EnableVisualStyles()
Application.Run(New Form1())
End Sub

Public Sub New()


MyBase.New()
End Sub

Private Sub Form1_Load(ByVal sender As Object, _


ByVal e As System.EventArgs) Handles MyBase.Load

Me.GroupBoxEvents.SuspendLayout()
Me.SuspendLayout()

Label1.Location = New Point(232, 12)


Label1.Size = New Size(98, 14)
Label1.AutoSize = True
Label1.Text = "Generated Events:"

Label2.Location = New Point(13, 12)


Label2.Size = New Size(95, 14)
Label2.AutoSize = True
Label2.Text = "User Input Target:"

TextBoxInput.Location = New Point(13, 34)


TextBoxInput.Size = New Size(200, 200)
TextBoxInput.AllowDrop = True
TextBoxInput.AutoSize = False
TextBoxInput.Cursor = Cursors.Cross
TextBoxInput.Multiline = True
TextBoxInput.TabIndex = 1

LinkLabelDrag.AllowDrop = True
LinkLabelDrag.AutoSize = True
LinkLabelDrag.Location = New Point(13, 240)
LinkLabelDrag.Size = New Size(175, 14)
LinkLabelDrag.TabIndex = 2
LinkLabelDrag.TabStop = True
LinkLabelDrag.Text = "Click here to use as a drag source"
LinkLabelDrag.Links.Add(New LinkLabel.Link(0, _
LinkLabelDrag.Text.Length))

GroupBoxEvents.Location = New Point(13, 281)


GroupBoxEvents.Size = New Size(200, 302)
GroupBoxEvents.TabIndex = 3
GroupBoxEvents.TabStop = False
GroupBoxEvents.Text = "Event Filter:"
GroupBoxEvents.Controls.Add(CheckBoxMouseEnter)
GroupBoxEvents.Controls.Add(CheckBoxToggleAll)
GroupBoxEvents.Controls.Add(CheckBoxMousePoints)
GroupBoxEvents.Controls.Add(CheckBoxKeyUpDown)
GroupBoxEvents.Controls.Add(CheckBoxMouseDragOver)
GroupBoxEvents.Controls.Add(CheckBoxMouseDrag)
GroupBoxEvents.Controls.Add(CheckBoxValidation)
GroupBoxEvents.Controls.Add(CheckBoxMouseMove)
GroupBoxEvents.Controls.Add(CheckBoxFocus)
GroupBoxEvents.Controls.Add(CheckBoxKeyboard)
GroupBoxEvents.Controls.Add(CheckBoxMouse)

CheckBoxToggleAll.AutoSize = True
CheckBoxToggleAll.Location = New Point(7, 20)
CheckBoxToggleAll.Size = New Size(122, 17)
CheckBoxToggleAll.Size = New Size(122, 17)
CheckBoxToggleAll.TabIndex = 4
CheckBoxToggleAll.Text = "Toggle All Events"

CheckBoxMouse.AutoSize = True
CheckBoxMouse.Location = New Point(7, 45)
CheckBoxMouse.Size = New Size(137, 17)
CheckBoxMouse.TabIndex = 5
CheckBoxMouse.Text = "Mouse and Click Events"

CheckBoxMouseEnter.AutoSize = True
CheckBoxMouseEnter.Location = New Point(26, 69)
CheckBoxMouseEnter.Margin = New Padding(3, 3, 3, 1)
CheckBoxMouseEnter.Size = New System.Drawing.Size(151, 17)
CheckBoxMouseEnter.TabIndex = 6
CheckBoxMouseEnter.Text = "Mouse Enter/Hover/Leave"

CheckBoxMouseMove.AutoSize = True
CheckBoxMouseMove.Location = New Point(26, 89)
CheckBoxMouseMove.Margin = New Padding(3, 2, 3, 3)
CheckBoxMouseMove.Size = New Size(120, 17)
CheckBoxMouseMove.TabIndex = 7
CheckBoxMouseMove.Text = "Mouse Move Events"

CheckBoxMousePoints.AutoSize = True
CheckBoxMousePoints.Location = New Point(26, 112)
CheckBoxMousePoints.Margin = New Padding(3, 3, 3, 1)
CheckBoxMousePoints.Size = New Size(141, 17)
CheckBoxMousePoints.TabIndex = 8
CheckBoxMousePoints.Text = "Draw Mouse Points"

CheckBoxMouseDrag.AutoSize = True
CheckBoxMouseDrag.Location = New Point(26, 135)
CheckBoxMouseDrag.Margin = New Padding(3, 1, 3, 3)
CheckBoxMouseDrag.Size = New Size(151, 17)
CheckBoxMouseDrag.TabIndex = 9
CheckBoxMouseDrag.Text = "Mouse Drag && Drop Events"

CheckBoxMouseDragOver.AutoSize = True
CheckBoxMouseDragOver.Location = New Point(44, 159)
CheckBoxMouseDragOver.Size = New Size(142, 17)
CheckBoxMouseDragOver.TabIndex = 10
CheckBoxMouseDragOver.Text = "Mouse Drag Over Events"

CheckBoxKeyboard.AutoSize = True
CheckBoxKeyboard.Location = New Point(8, 184)
CheckBoxKeyboard.Size = New Size(103, 17)
CheckBoxKeyboard.TabIndex = 11
CheckBoxKeyboard.Text = "Keyboard Events"

CheckBoxKeyUpDown.AutoSize = True
CheckBoxKeyUpDown.Location = New Point(26, 207)
CheckBoxKeyUpDown.Margin = New Padding(3, 3, 3, 1)
CheckBoxKeyUpDown.Size = New Size(133, 17)
CheckBoxKeyUpDown.TabIndex = 12
CheckBoxKeyUpDown.Text = "Key Up && Down Events"

CheckBoxFocus.AutoSize = True
CheckBoxFocus.Location = New Point(8, 233)
CheckBoxFocus.Margin = New Padding(3, 2, 3, 3)
CheckBoxFocus.Size = New Size(146, 17)
CheckBoxFocus.TabIndex = 13
CheckBoxFocus.Text = "Focus && Activation Events"

CheckBoxValidation.AutoSize = True
CheckBoxValidation.Location = New Point(8, 257)
CheckBoxValidation.Size = New Size(104, 17)
CheckBoxValidation.TabIndex = 14
CheckBoxValidation.Text = "Validation Events"
TextBoxOutput.Location = New Point(232, 34)
TextBoxOutput.Size = New Size(308, 510)
TextBoxOutput.Multiline = True
TextBoxOutput.CausesValidation = False
TextBoxOutput.ReadOnly = True
TextBoxOutput.ScrollBars = ScrollBars.Vertical
TextBoxOutput.TabIndex = 15
TextBoxOutput.WordWrap = False

ButtonClear.Location = New Point(232, 560)


ButtonClear.Size = New Size(308, 23)
ButtonClear.TabIndex = 16
ButtonClear.Text = "Clear Event List"

Me.ClientSize = New Size(552, 595)


Me.Controls.Add(LinkLabelDrag)
Me.Controls.Add(ButtonClear)
Me.Controls.Add(GroupBoxEvents)
Me.Controls.Add(Label1)
Me.Controls.Add(Label2)
Me.Controls.Add(TextBoxInput)
Me.Controls.Add(TextBoxOutput)
Me.Text = "User Input Events"

Me.GroupBoxEvents.ResumeLayout(False)
Me.GroupBoxEvents.PerformLayout()
Me.ResumeLayout(False)
Me.PerformLayout()
CheckAllChildCheckBoxes(Me, True)
End Sub

' Recursively search the form for all contained checkboxes and
' initially check them
Private Sub CheckAllChildCheckBoxes(ByRef parent As Control, _
ByVal value As Boolean)

Dim currentControl As Control


Dim box As CheckBox
For Each currentControl In parent.Controls
box = TryCast(currentControl, CheckBox)
If box IsNot Nothing Then
box.Checked = value
End If

'Recurse if control contains other controls


If currentControl.Controls.Count > 0 Then
CheckAllChildCheckBoxes(currentControl, value)
End If
Next
End Sub

' All-purpose method for displaying a line of text in one of the


' text boxes.
Private Sub DisplayLine(ByRef line As String)

TextBoxOutput.AppendText(line)
TextBoxOutput.AppendText(ControlChars.NewLine)
End Sub

' Click event handler for the button that clears the text box.
Private Sub ButtonClear_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles ButtonClear.Click

TextBoxOutput.Invalidate()
TextBoxOutput.Clear()
End Sub

Private Sub TextBoxInput_KeyDown(ByVal sender As Object, _


ByVal e As KeyEventArgs) Handles TextBoxInput.KeyDown
ByVal e As KeyEventArgs) Handles TextBoxInput.KeyDown

If CheckBoxKeyUpDown.Checked = True Then


DisplayLine("KeyDown: " + e.KeyData.ToString())
End If
End Sub

Private Sub TextBoxInput_KeyUp(ByVal sender As Object, _


ByVal e As KeyEventArgs) Handles TextBoxInput.KeyUp

If CheckBoxKeyUpDown.Checked = True Then


DisplayLine("KeyUp: " + e.KeyData.ToString())
End If
End Sub

Private Sub TextBoxInput_KeyPress(ByVal sender As Object, _


ByVal e As KeyPressEventArgs) Handles TextBoxInput.KeyPress

If CheckBoxKeyboard.Checked = True Then

If Char.IsWhiteSpace(e.KeyChar) Then
Dim keyVal As Integer
keyVal = AscW(e.KeyChar)
DisplayLine("KeyPress: WS-" + keyVal.ToString())
Else
DisplayLine("KeyPress: " + e.KeyChar.ToString())
End If
End If
End Sub

Private Sub TextBoxInput_Click(ByVal sender As Object, _


ByVal e As EventArgs) Handles TextBoxInput.Click

If CheckBoxMouse.Checked = True Then


DisplayLine("Click event")
End If
End Sub

Private Sub TextBoxInput_DoubleClick(ByVal sender As Object, _


ByVal e As EventArgs) Handles TextBoxInput.DoubleClick

If CheckBoxMouse.Checked = True Then


DisplayLine("DoubleClick event")
End If
End Sub

Private Sub TextBoxInput_MouseClick(ByVal sender As Object, _


ByVal e As MouseEventArgs) Handles TextBoxInput.MouseClick

If CheckBoxMouse.Checked = True Then


DisplayLine("MouseClick: " + e.Button.ToString() + _
" " + e.Location.ToString())
End If
End Sub

Private Sub TextBoxInput_MouseDoubleClick(ByVal sender As Object, _


ByVal e As MouseEventArgs) Handles TextBoxInput.MouseDoubleClick

If CheckBoxMouse.Checked = True Then


DisplayLine("MouseDoubleClick: " + e.Button.ToString() + _
" " + e.Location.ToString())
End If
End Sub

Private Sub TextBoxInput_MouseDown(ByVal sender As System.Object, _


ByVal e As MouseEventArgs) Handles TextBoxInput.MouseDown

If CheckBoxMouse.Checked = True Then


DisplayLine("MouseDown: " + e.Button.ToString() + _
" " + e.Location.ToString())
" " + e.Location.ToString())
End If
End Sub

Private Sub TextBoxInput_MouseUp(ByVal sender As Object, _


ByVal e As MouseEventArgs) Handles TextBoxInput.MouseUp

If CheckBoxMouse.Checked = True Then


DisplayLine("MouseUp: " + e.Button.ToString() + _
" " + e.Location.ToString())
End If

' The TextBox control was designed to change focus only on


' the primary click, so force focus to avoid user confusion.
If TextBoxInput.Focused = False Then
TextBoxInput.Focus()
End If
End Sub

Private Sub TextBoxInput_MouseEnter(ByVal sender As Object, _


ByVal e As EventArgs) Handles TextBoxInput.MouseEnter

If CheckBoxMouseEnter.Checked = True Then


DisplayLine("MouseEnter event")
End If
End Sub

Private Sub TextBoxInput_MouseHover(ByVal sender As Object, _


ByVal e As EventArgs) Handles TextBoxInput.MouseHover

If CheckBoxMouseEnter.Checked = True Then


DisplayLine("MouseHover event")
End If
End Sub

Private Sub TextBoxInput_MouseLeave(ByVal sender As Object, _


ByVal e As EventArgs) Handles TextBoxInput.MouseLeave

If CheckBoxMouseEnter.Checked = True Then


DisplayLine("MouseLeave event")
End If
End Sub

Private Sub TextBoxInput_MouseWheel(ByVal sender As Object, _


ByVal e As MouseEventArgs) Handles TextBoxInput.MouseWheel

If CheckBoxMouse.Checked = True Then


DisplayLine("MouseWheel: " + e.Delta.ToString() + _
" detents at " + e.Location.ToString())
End If
End Sub

Private Sub TextBoxInput_MouseMove(ByVal sender As Object, _


ByVal e As MouseEventArgs) Handles TextBoxInput.MouseMove

If CheckBoxMouseMove.Checked = True Then


DisplayLine("MouseMove: " + e.Button.ToString() + " " + _
e.Location.ToString())
End If

If CheckBoxMousePoints.Checked = True Then


Dim g As Graphics = TextBoxInput.CreateGraphics()
g.FillRectangle(Brushes.Black, e.Location.X, _
e.Location.Y, 1, 1)
g.Dispose()
End If
End Sub

Private Sub TextBoxInput_MouseCaptureChanged( _


ByVal sender As Object, ByVal e As EventArgs) _
ByVal sender As Object, ByVal e As EventArgs) _
Handles TextBoxInput.MouseCaptureChanged

If CheckBoxMouseDrag.Checked = True Then


DisplayLine("MouseCaptureChanged event")
End If
End Sub

Private Sub TextBoxInput_DragEnter(ByVal sender As Object, _


ByVal e As DragEventArgs) Handles TextBoxInput.DragEnter

Dim pt As Point
If CheckBoxMouseDrag.Checked = True Then
pt = New Point(e.X, e.Y)
DisplayLine("DragEnter: " + _
CovertKeyStateToString(e.KeyState) _
+ " at " + pt.ToString())
End If
End Sub

Private Sub TextBoxInput_DragDrop(ByVal sender As Object, _


ByVal e As DragEventArgs) Handles TextBoxInput.DragDrop

If CheckBoxMouseDrag.Checked = True Then


Dim pt As Point
pt = New Point(e.X, e.Y)

DisplayLine("DragDrop: " + _
CovertKeyStateToString(e.KeyState) _
+ " at " + pt.ToString())
End If
End Sub

Private Sub TextBoxInput_DragOver(ByVal sender As Object, _


ByVal e As DragEventArgs) Handles TextBoxInput.DragOver

If CheckBoxMouseDragOver.Checked = True Then


Dim pt As Point
pt = New Point(e.X, e.Y)
DisplayLine("DragOver: " + _
CovertKeyStateToString(e.KeyState) _
+ " at " + pt.ToString())
End If

' Allow if drop data is of type string.


If Not (e.Data.GetDataPresent(GetType(String))) Then
e.Effect = DragDropEffects.None
Else
e.Effect = DragDropEffects.Copy
End If
End Sub

Private Sub TextBoxInput_DragLeave(ByVal sender As Object, _


ByVal e As EventArgs) Handles TextBoxInput.DragLeave

If CheckBoxMouseDrag.Checked = True Then


DisplayLine("DragLeave event")
End If
End Sub

Private Function CovertKeyStateToString(ByVal keyState As Integer) _


As String

Dim keyString As String = "None"

' Which button was pressed?


If ((keyState And 1) = 1) Then
keyString = "Left"
ElseIf ((keyState And 2) = 2) Then
keyString = "Right"
ElseIf ((keyState And 16) = 16) Then
keyString = "Middle"
End If

' Are one or more modifier keys also pressed?


If ((keyState And 4) = 4) Then
keyString += "+SHIFT"
End If
If ((keyState And 8) = 8) Then
keyString += "+CTRL"
End If
If ((keyState And 32) = 32) Then
keyString += "+ALT"
End If

Return keyString
End Function

Private Sub TextBoxInput_Enter(ByVal sender As Object, _


ByVal e As EventArgs) Handles TextBoxInput.Enter

If CheckBoxFocus.Checked = True Then


DisplayLine("Enter event")
End If
End Sub

Private Sub TextBoxInput_Leave(ByVal sender As Object, _


ByVal e As EventArgs) Handles TextBoxInput.Leave

If CheckBoxFocus.Checked = True Then


DisplayLine("Leave event")
End If
End Sub

Private Sub TextBoxInput_GotFocus(ByVal sender As Object, _


ByVal e As EventArgs) Handles TextBoxInput.GotFocus

If CheckBoxFocus.Checked = True Then


DisplayLine("GotFocus event")
End If
End Sub

Private Sub TextBoxInput_LostFocus(ByVal sender As Object, _


ByVal e As EventArgs) Handles TextBoxInput.LostFocus

If CheckBoxFocus.Checked = True Then


DisplayLine("LostFocus event")
End If
End Sub

Private Sub TextBoxInput_Validated(ByVal sender As Object, _


ByVal e As EventArgs) Handles TextBoxInput.Validated

If CheckBoxValidation.Checked = True Then


DisplayLine("Validated event")
End If
End Sub

Private Sub TextBoxInput_Validating(ByVal sender As Object, _


ByVal e As CancelEventArgs) _
Handles TextBoxInput.Validating

If CheckBoxValidation.Checked = True Then


DisplayLine("Validating event")
End If
End Sub

Private Sub CheckBoxToggleAll_CheckedChanged( _


ByVal sender As Object, ByVal e As EventArgs) _
Handles CheckBoxToggleAll.CheckedChanged

Dim cb As CheckBox
cb = TryCast(sender, CheckBox)
If cb IsNot Nothing Then
CheckAllChildCheckBoxes(Me, cb.Checked)
End If
End Sub

Private Sub CheckBoxMouse_CheckedChanged( _


ByVal sender As Object, ByVal e As EventArgs) _
Handles CheckBoxMouse.CheckedChanged

ConfigureCheckBoxSettings()
End Sub

Private Sub CheckBoxMouseDrag_CheckedChanged( _


ByVal sender As Object, ByVal e As EventArgs) _
Handles CheckBoxMouseDrag.CheckedChanged

ConfigureCheckBoxSettings()
End Sub

Private Sub CheckBoxKeyboard_CheckedChanged( _


ByVal sender As Object, ByVal e As EventArgs) _
Handles CheckBoxKeyboard.CheckedChanged

ConfigureCheckBoxSettings()
End Sub

Private Sub CheckBoxMouseMove_CheckedChanged( _


ByVal sender As Object, ByVal e As EventArgs) _
Handles CheckBoxMouseEnter.CheckedChanged, _
CheckBoxMouseMove.CheckedChanged

ConfigureCheckBoxSettings()
End Sub

' Reconcile dependencies between the check box


' selection choices.
Private Sub ConfigureCheckBoxSettings()

' CheckBoxMouse is a top-level check box.


If CheckBoxMouse.Checked = False Then
CheckBoxMouseEnter.Enabled = False
CheckBoxMouseMove.Enabled = False
CheckBoxMouseDrag.Enabled = False
CheckBoxMouseDragOver.Enabled = False
CheckBoxMousePoints.Enabled = False
Else
CheckBoxMouseEnter.Enabled = True
CheckBoxMouseMove.Enabled = True
CheckBoxMouseDrag.Enabled = True
CheckBoxMousePoints.Enabled = True

' Enable children depending on the state of the parent.


If CheckBoxMouseDrag.Checked = False Then
CheckBoxMouseDragOver.Enabled = False
Else
CheckBoxMouseDragOver.Enabled = True
End If
End If

If CheckBoxKeyboard.Checked = False Then


CheckBoxKeyUpDown.Enabled = False
Else
CheckBoxKeyUpDown.Enabled = True
End If
End If
End Sub

Private Sub LinkLabelDrag_MouseDown(ByVal sender As Object, _


ByVal e As MouseEventArgs) Handles LinkLabelDrag.MouseDown

Dim data As String = "Sample Data"


LinkLabelDrag.DoDragDrop(data, DragDropEffects.All)
End Sub

Private Sub LinkLabelDrag_GiveFeedback(ByVal sender As Object, _


ByVal e As GiveFeedbackEventArgs) _
Handles LinkLabelDrag.GiveFeedback

If ((e.Effect And DragDropEffects.Copy) = _


DragDropEffects.Copy) Then
LinkLabelDrag.Cursor = Cursors.HSplit
Else
LinkLabelDrag.Cursor = Cursors.Default
End If
End Sub

End Class
End Namespace

Compiling the Code


This example requires:
References to the System, System.Drawing and System.Windows.Forms assemblies.

See also
User Input in Windows Forms
User Input Validation in Windows Forms
9/1/2020 • 6 minutes to read • Edit Online

When users enter data into your application, you may want to verify that the data is valid before your application
uses it. You may require that certain text fields not be zero-length, that a field be formatted as a telephone number
or other type of well-formed data, or that a string not contain any unsafe characters that could be used to
compromise the security of a database. Windows Forms provides several ways for you to validate input in your
application.

Validation with the MaskedTextBox Control


If you need to require users to enter data in a well-defined format, such as a telephone number or a part number,
you can accomplish this quickly and with minimal code by using the MaskedTextBox control. A mask is a string
made up of characters from a masking language that specifies which characters can be entered at any given
position in the text box. The control displays a set of prompts to the user. If the user types an incorrect entry, for
example, the user types a letter when a digit is required, the control will automatically reject the input.
The masking language that is used by MaskedTextBox is very flexible. It allows you to specify required characters,
optional characters, literal characters, such as hyphens and parentheses, currency characters, and date separators.
The control also works well when bound to a data source. The Format event on a data binding can be used to
reformat incoming data to comply with the mask, and the Parse event can be used to reformat outgoing data to
comply with the specifications of the data field.
For more information, see MaskedTextBox Control.

Event-Driven Validation
If you want full programmatic control over validation, or need to perform complex validation checks, you should
use the validation events built into most Windows Forms controls. Each control that accepts free-form user input
has a Validating event that will occur whenever the control requires data validation. In the Validating event-
handling method, you can validate user input in several ways. For example, if you have a text box that must contain
a postal code, you can perform the validation in the following ways:
If the postal code must belong to a specific group of zip codes, you can perform a string comparison on the
input to validate the data entered by the user. For example, if the postal code must be in the set {10001,
10002, 10003}, then you can use a string comparison to validate the data.
If the postal code must be in a specific form you can use regular expressions to validate the data entered by
the user. For example, to validate the form ##### or #####-#### , you can use the regular expression
^(\d{5})(-\d{4})?$ . To validate the form A#A #A# , you can use the regular expression
[A-Z]\d[A-Z] \d[A-Z]\d . For more information about regular expressions, see .NET Framework Regular
Expressions and Regular Expression Examples.
If the postal code must be a valid United States Zip code, you could call a Zip code Web service to validate
the data entered by the user.
The Validating event is supplied an object of type CancelEventArgs. If you determine that the control's data is not
valid, you can cancel the Validating event by setting this object's Cancel property to true . If you do not set the
Cancel property, Windows Forms will assume that validation succeeded for that control, and raise the Validated
event.
For a code example that validates an email address in a TextBox, see Validating.
Data Binding and Event-Driven Validation
Validation is very useful when you have bound your controls to a data source, such as a database table. By using
validation, you can make sure that your control's data satisfies the format required by the data source, and that it
does not contain any special characters such as quotation marks and back slashes that might be unsafe.
When you use data binding, the data in your control is synchronized with the data source during execution of the
Validating event. If you cancel the Validating event, the data will not be synchronized with the data source.

IMPORTANT
If you have custom validation that takes place after the Validating event, it will not affect the data binding. For example, if
you have code in a Validated event that attempts to cancel the data binding, the data binding will still occur. In this case, to
perform validation in the Validated event, change the control's Data Source Update Mode property (under
(Databindings) \(Advanced) ) from OnValidation to Never , and add Control .DataBindings[" <YOURFIELD>
"].WriteValue() to your validation code.

Implicit and Explicit Validation


So when does a control's data get validated? This is up to you, the developer. You can use either implicit or explicit
validation, depending on the needs of your application.
Implicit Validation
The implicit validation approach validates data as the user enters it. You can validate the data as the data is entered
in a control by reading the keys as they are pressed, or more commonly whenever the user takes the input focus
away from one control and moves to the next. This approach is useful when you want to give the user immediate
feedback about the data as they are working.
If you want to use implicit validation for a control, you must set that control's AutoValidate property to
EnablePreventFocusChange or EnableAllowFocusChange. If you cancel the Validating event, the behavior of the
control will be determined by what value that you assigned to AutoValidate. If you assigned
EnablePreventFocusChange, canceling the event will cause the Validated event not to occur. Input focus will remain
on the current control until the user changes the data to a valid input. If you assigned EnableAllowFocusChange,
the Validated event will not occur when you cancel the event, but focus will still change to the next control.
Assigning Disable to the AutoValidate property prevents implicit validation altogether. To validate your controls,
you will have to use explicit validation.
Explicit Validation
The explicit validation approach validates data at one time. You can validate the data in response to a user action,
such as clicking a Save button or a Next link. When the user action occurs, you can trigger explicit validation in one
of the following ways:
Call Validate to validate the last control to have lost focus.
Call ValidateChildren to validate all child controls in a form or container control.
Call a custom method to validate the data in the controls manually.
Default Implicit Validation Behavior for Windows Forms Controls
Different Windows Forms controls have different defaults for their AutoValidate property. The following table
shows the most common controls and their defaults.

C O N T RO L DEFA ULT VA L IDAT IO N B EH AVIO R

ContainerControl Inherit

Form EnableAllowFocusChange
C O N T RO L DEFA ULT VA L IDAT IO N B EH AVIO R

PropertyGrid Property not exposed in Visual Studio

ToolStripContainer Property not exposed in Visual Studio

SplitContainer Inherit

UserControl EnableAllowFocusChange

Closing the Form and Overriding Validation


When a control maintains focus because the data it contains is invalid, it is impossible to close the parent form in
one of the usual ways:
By clicking the Close button.
By selecting Close in the System menu.
By calling the Close method programmatically.
However, in some cases, you might want to let the user close the form regardless of whether the values in the
controls are valid. You can override validation and close a form that still contains invalid data by creating a handler
for the form's FormClosing event. In the event, set the Cancel property to false . This forces the form to close. For
more information and an example, see Form.FormClosing.

NOTE
If you force the form to close in this manner, any data in the form's controls that has not already been saved is lost. In
addition, modal forms do not validate the contents of controls when they are closed. You can still use control validation to
lock focus to a control, but you do not have to be concerned about the behavior associated with closing the form.

See also
Control.Validating
Form.FormClosing
System.Windows.Forms.FormClosingEventArgs
MaskedTextBox Control
Regular Expression Examples
Dialog Boxes in Windows Forms
9/1/2020 • 2 minutes to read • Edit Online

Dialog boxes are used to interact with the user and retrieve information. In simple terms, a dialog box is a form
with its FormBorderStyle enumeration property set to FixedDialog . You can construct your own custom dialog
boxes by using the Windows Forms Designer in Visual Studio. Add controls such as Label , Textbox , and Button
to customize dialog boxes to your specific needs. The .NET Framework also includes predefined dialog boxes, such
as File Open and message boxes, which you can adapt for your own applications. For more information, see
Dialog-Box Controls and Components.

In This Section
How to: Display Dialog Boxes for Windows Forms
Gives directions for showing dialog boxes.

Related Sections
Dialog-Box Controls and Components
Lists the predefined dialog box controls.
Changing the Appearance of Windows Forms
Contains links to topics that describe how to change the appearance of Windows Forms applications.
TabControl Control Overview
Explains how you incorporate the tab control into a dialog box.
How to: Display Dialog Boxes for Windows Forms
9/1/2020 • 2 minutes to read • Edit Online

You display a dialog box in the same way you display any other form in an application. The startup form loads
automatically when the application is run. To make a second form or dialog box appear in the application, write
code to load and display it. Similarly, to make the form or dialog box disappear, write code to unload or hide it.
To display a dialog box
1. Navigate to the event handler with which you want to open the dialog box. This can happen when a menu
command is selected, when a button is clicked, or when any other event occurs.
2. In the event handler, add code to open the dialog box. In this example, a button-click event is used to show
the dialog box:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles


Button1.Click
Dim dlg1 as new Form()
dlg1.ShowDialog()
End Sub

private void button1_Click(object sender, System.EventArgs e)


{
Form dlg1 = new Form();
dlg1.ShowDialog();
}

private:
void button1_Click(System::Object ^ sender,
System::EventArgs ^ e)
{
Form ^ dlg1 = gcnew Form();
dlg1->ShowDialog();
}
Windows Forms Data Binding
9/1/2020 • 2 minutes to read • Edit Online

Data binding in Windows Forms gives you the means to display and make changes to information from a data
source in controls on the form. You can bind to both traditional data sources as well as almost any structure that
contains data.

In This Section
Data Binding and Windows Forms
Provides an overview of data binding in Windows Forms.
Data Sources Supported by Windows Forms
Describes the data sources that can be used with Windows Forms.
Interfaces Related to Data Binding
Describes several of the interfaces used with Windows Forms data binding.
How to: Navigate Data in Windows Forms
Shows how to navigate through items in a data source.
Change Notification in Windows Forms Data Binding
Describes different types of change notification for Windows Forms data binding.
How to: Implement the INotifyPropertyChanged Interface
Shows how to implement the INotifyPropertyChanged interface. The interface communicates to a bound control
the property changes on a business object
How to: Apply the PropertyNameChanged Pattern
Shows how to apply the PropertyNameChanged pattern to properties of a Windows Forms user control.
How to: Implement the ITypedList Interface
Shows how to enable discovery of the schema for a bindable list by implementing the ITypedList interface.
How to: Implement the IListSource Interface
Shows how to implement the IListSource interface to create a bindable class does not implement IList, but
provides a list from another location.
How to: Ensure Multiple Controls Bound to the Same Data Source Remain Synchronized
Shows how to handle the BindingComplete event to ensure all controls bound to a data source remain
synchronized.
How to: Ensure the Selected Row in a Child Table Remains at the Correct Position
Shows how to ensure the selected row of a child table does not change, when a change is made to a field of the
parent table.
Also see Interfaces Related to Data Binding, How to: Navigate Data in Windows Forms, and How to: Create a
Simple-Bound Control on a Windows Form.

Reference
System.Windows.Forms.Binding
Describes the class that represents the binding between a bindable component and a data source.
System.Windows.Forms.BindingSource
Describes the class that encapsulates a data source for binding to controls.

Related Sections
BindingSource Component
Contains a list of topics that demonstrate how to use the BindingSource component.
DataGridView Control
Provides a list of topics that demonstrate how to use a bindable datagrid control.
Also see Accessing Data in Visual Studio.
Data Binding and Windows Forms
9/1/2020 • 4 minutes to read • Edit Online

In Windows Forms, you can bind to not just traditional data sources, but also to almost any structure that contains
data. You can bind to an array of values that you calculate at run time, read from a file, or derive from the values of
other controls.
In addition, you can bind any property of any control to the data source. In traditional data binding, you typically
bind the display property—for example, the Text property of a TextBox control—to the data source. With the .NET
Framework, you also have the option of setting other properties through binding as well. You might use binding
to perform the following tasks:
Setting the graphic of an image control.
Setting the background color of one or more controls.
Setting the size of controls.
Essentially, data binding is an automatic way of setting any run-time accessible property of any control on a form.

Types of Data Binding


Windows Forms can take advantage of two types of data binding: simple binding and complex binding. Each
offers different advantages.

T Y P E O F DATA B IN DIN G DESC RIP T IO N

Simple data binding The ability of a control to bind to a single data element, such
as a value in a column in a dataset table. This is the type of
binding typical for controls such as a TextBox control or Label
control, which are controls that typically only displays a single
value. In fact, any property on a control can be bound to a
field in a database. There is extensive support for this feature
in Visual Studio.

For more information, see:

- Interfaces Related to Data Binding


- How to: Navigate Data in Windows Forms
- How to: Create a Simple-Bound Control on a Windows
Form

Complex data binding The ability of a control to bind to more than one data
element, typically more than one record in a database.
Complex binding is also called list-based binding. Examples of
controls that support complex binding are the DataGridView,
ListBox, and ComboBox controls. For an example of complex
data binding, see How to: Bind a Windows Forms ComboBox
or ListBox Control to Data.

BindingSource Component
To simplify data binding, Windows Forms enables you to bind a data source to the BindingSource component and
then bind controls to the BindingSource. You can use the BindingSource in simple or complex binding scenarios. In
either case, the BindingSource acts as an intermediary between the data source and bound controls providing
change notification currency management and other services.

Common Scenarios That Employ Data Binding


Nearly every commercial application uses information read from data sources of one type or another, usually
through data binding. The following list shows a few of the most common scenarios that utilize data binding as
the method of data presentation and manipulation.

SC EN A RIO DESC RIP T IO N

Reporting Reports provide a flexible way for you to display and


summarize your data in a printed document. It is very
common to create a report that prints selected contents of a
data source either to the screen or to a printer. Common
reports include lists, invoices, and summaries. Items are
usually formatted into columns of lists, with sub-items
organized under each list item, but you should choose the
layout that best suits the data.

Data entry A common way to enter large amounts of related data or to


prompt users for information is through a data entry form.
Users can enter information or select choices using text boxes,
option buttons, drop-down lists, and check boxes.
Information is then submitted and stored in a database,
whose structure is based on the information entered.

Master/detail relationship A master/detail application is one format for looking at


related data. Specifically, there are two tables of data with a
relation connecting them—in the classic business example, a
"Customers" table and an "Orders" table with a relationship
between them linking customers and their respective orders.
For more information about creating a master/detail
application with two Windows Forms DataGridView controls,
see How to: Create a Master/Detail Form Using Two Windows
Forms DataGridView Controls

Lookup Table Another common data presentation/manipulation scenario is


the table lookup. Often, as part of a larger data display, a
ComboBox control is used to display and manipulate data.
The key is that the data displayed in the ComboBox control is
different than the data written to the database. For example,
if you have a ComboBox control displaying the items available
from a grocery store, you would probably like to see the
names of the products (bread, milk, eggs). However, to ease
information retrieval within the database and for database
normalization, you would probably store the information for
the specific items of a given order as item numbers (#501,
#603, and so on). Thus, there is an implicit connection
between the "friendly name" of the grocery item in the
ComboBox control on your form and the related item number
that is present in an order. This is the essence of a table
lookup. For more information, see How to: Create a Lookup
Table with the Windows Forms BindingSource Component.

See also
Binding
Windows Forms Data Binding
How to: Bind the Windows Forms DataGrid Control to a Data Source
BindingSource Component
Data Sources Supported by Windows Forms
9/1/2020 • 3 minutes to read • Edit Online

Traditionally, data binding has been used within applications to take advantage of data stored in databases. With
Windows Forms data binding, you can access data from databases as well as data in other structures, such as
arrays and collections, so long as certain minimum requirements have been met.

Structures to Bind To
In Windows Forms, you can bind to a wide variety of structures, from simple objects (simple binding) to complex
lists such as ADO.NET data tables (complex binding). For simple binding, Windows Forms supports binding to the
public properties on the simple object. Windows Forms list-based binding generally requires that the object
support the IList interface or the IListSource interface. Additionally, if you are binding with through a
BindingSource component, you can bind to an object that supports the IEnumerable interface. For more
information about interfaces related to data binding, see Interfaces Related to Data Binding.
The following list shows the structures you can bind to in Windows Forms.
BindingSource
A BindingSource is the most common Windows Forms data source and acts a proxy between a data source and
Windows Forms controls. The general BindingSource usage pattern is to bind your controls to the BindingSource
and bind the BindingSource to the data source (for example, an ADO.NET data table or a business object). The
BindingSource provides services that enable and improve the level of data binding support. For example, Windows
Forms list based controls such as the DataGridView and ComboBox do not directly support binding to IEnumerable
data sources however, you can enable this scenario by binding through a BindingSource. In this case, the
BindingSource will convert the data source to an IList.
Simple objects
Windows Forms supports data binding control properties to public properties on the instance of an object using
the Binding type. Windows Forms also supports binding list based controls, such as a ListControl to an object
instance when a BindingSource is used.
array or collection
To act as a data source, a list must implement the IList interface; one example would be an array that is an instance
of the Array class. For more information on arrays, see How to: Create an Array of Objects (Visual Basic).
In general, you should use BindingList<T> when you create lists of objects for data binding. BindingList<T> is a
generic version of the IBindingList interface. The IBindingList interface extends the IList interface by adding
properties, methods and events necessary for two-way data binding.
IEnumerable
Windows Forms controls can be bound to data sources that only support the IEnumerable interface if they are
bound through a BindingSource component.
ADO.NET data objects
ADO.NET provides a number of data structures suitable for binding to. Each varies in its sophistication and
complexity.
DataColumn. A DataColumn is the essential building block of a DataTable, in that a number of columns
comprise a table. Each DataColumn has a DataType property that determines the kind of data the column
holds (for example, the make of an automobile in a table describing cars). You can simple-bind a control
(such as a TextBox control's Text property) to a column within a data table.
DataTable. A DataTable is the representation of a table, with rows and columns, in ADO.NET. A data table
contains two collections: DataColumn, representing the columns of data in a given table (which ultimately
determine the kinds of data that can be entered into that table), and DataRow, representing the rows of data
in a given table. You can complex-bind a control to the information contained in a data table (such as
binding the DataGridView control to a data table). However, when you bind to a DataTable, you are a really
binding to the table's default view.
DataView. A DataView is a customized view of a single data table that may be filtered or sorted. A data view
is the data "snapshot" used by complex-bound controls. You can simple-bind or complex-bind to the data
within a data view, but be aware that you are binding to a fixed "picture" of the data rather than a clean,
updating data source.
DataSet. A DataSet is a collection of tables, relationships, and constraints of the data in a database. You can
simple-bind or complex-bind to the data within a dataset, but be aware that you are binding to the default
DataViewManager for the DataSet (see the next bullet point).
DataViewManager. A DataViewManager is a customized view of the entire DataSet, analogous to a
DataView, but with relations included. With a DataViewSettings collection, you can set default filters and
sort options for any views that the DataViewManager has for a given table.

See also
Change Notification in Windows Forms Data Binding
Data Binding and Windows Forms
Windows Forms Data Binding
Interfaces Related to Data Binding
9/1/2020 • 8 minutes to read • Edit Online

With ADO.NET, you can create many different data structures to suit the binding needs of your application and the
data you are working with. You may want to create your own classes that provide or consume data in Windows
Forms. These objects can offer varying levels of functionality and complexity, from basic data binding, to providing
design-time support, error checking, change notification, or even support for a structured rollback of the changes
made to the data itself.

Consumers of Data-Binding Interfaces


The following sections describe two groups of interface objects. The first group lists interfaces that are
implemented on data sources by data source authors. These interfaces are designed to be consumed by data
source consumers, which are in most cases Windows Forms controls or components. The second group lists
interfaces designed for use by component authors. Component authors use these interfaces when they are
creating a component that supports data binding to be consumed by the Windows Forms data-binding engine.
You can implement these interfaces within classes associated with your form to enable data binding; each case
presents a class that implements an interface that enables interaction with data. Visual Studio rapid application
development (RAD) data design experience tools already take advantage of this functionality.
Interfaces for Implementation by Data Source Authors
The following interfaces are designed to be consumed by Windows Forms controls:
IList interface
A class that implements the IList interface could be an Array, ArrayList, or CollectionBase. These are indexed
lists of items of type Object. These lists must contain homogenous types, because the first item of the index
determines the type. IList would be available for binding only at run time.

NOTE
If you want to create a list of business objects for binding with Windows Forms, you should consider using the
BindingList<T>. The BindingList<T> is an extensible class that implements the primary interfaces required for two-
way Windows Forms data binding.

IBindingList interface
A class that implements the IBindingList interface provides a much higher level of data-binding
functionality. This implementation offers you basic sorting capabilities and change notification, both for
when the list items change (for example, the third item in a list of customers has a change to the Address
field), as well as when the list itself changes (for example, the number of items in the list increases or
decreases). Change notification is important if you plan to have multiple controls bound to the same data,
and you want data changes made in one of the controls to propagate to the other bound controls.

NOTE
Change notification is enabled for the IBindingList interface through the SupportsChangeNotification property
which, when true , raises a ListChanged event, indicating the list changed or an item in the list changed.

The type of change is described by the ListChangedType property of the ListChangedEventArgs parameter.
Hence, whenever the data model is updated, any dependent views, such as other controls bound to the
same data source, will also be updated. However, objects contained within the list will have to notify the list
when they change so that the list can raise the ListChanged event.

NOTE
The BindingList<T> provides a generic implementation of the IBindingList interface.

IBindingListView interface
A class that implements the IBindingListView interface provides all the functionality of an implementation
of IBindingList, as well as filtering and advanced sorting functionality. This implementation offers string-
based filtering, and multicolumn sorting with property descriptor-direction pairs.
IEditableObject interface
A class that implements the IEditableObject interface allows an object to control when changes to that
object are made permanent. This implementation affords you the BeginEdit, EndEdit, and CancelEdit
methods, which enable you to roll back changes made to the object. Following is a brief explanation of the
functioning of the BeginEdit, EndEdit, and CancelEdit methods and how they work in conjunction with one
another to enable a possible rollback of changes made to the data:
The BeginEdit method signals the start of an edit on an object. An object that implements this
interface will need to store any updates after the BeginEdit method call in such a way that the
updates can be discarded if the CancelEdit method is called. In data binding Windows Forms, you
can call BeginEdit multiple times within the scope of a single edit transaction (for example, BeginEdit,
BeginEdit, EndEdit). Implementations of IEditableObject should keep track of whether BeginEdit has
already been called and ignore subsequent calls to BeginEdit. Because this method can be called
multiple times, it is important that subsequent calls to it are nondestructive; that is, subsequent
BeginEdit calls cannot destroy the updates that have been made or change the data that was saved
on the first BeginEdit call.
The EndEdit method pushes any changes since BeginEdit was called into the underlying object, if the
object is currently in edit mode.
The CancelEdit method discards any changes made to the object.
For more information about how the BeginEdit, EndEdit, and CancelEdit methods work, see Save data back
to the database.
This transactional notion of data functionality is used by the DataGridView control.
ICancelAddNew interface
A class that implements the ICancelAddNew interface usually implements the IBindingList interface and
allows you to roll back an addition made to the data source with the AddNew method. If your data source
implements the IBindingList interface, you should also have it implement the ICancelAddNew interface.
IDataErrorInfo interface
A class that implements the IDataErrorInfo interface allows objects to offer custom error information to
bound controls:
The Error property returns general error message text (for example, "An error has occurred").
The Item[] property returns a string with the specific error message from the column (for example,
"The value in the State column is invalid").
IEnumerable interface
A class that implements the IEnumerable interface is typically consumed by ASP.NET. Windows Forms
support for this interface is only available through the BindingSource component.

NOTE
The BindingSource component copies all IEnumerable items into a separate list for binding purposes.

ITypedList interface
A collections class that implements the ITypedList interface provides the ability to control the order and the
set of properties exposed to the bound control.

NOTE
When you implement the GetItemProperties method, and the PropertyDescriptor array is not null, the last entry in
the array will be the property descriptor that describes the list property that is another list of items.

ICustomTypeDescriptor interface
A class that implements the ICustomTypeDescriptor interface provides dynamic information about itself.
This interface is similar to ITypedList but is used for objects rather than lists. This interface is used by
DataRowView to project the schema of the underlying rows. A simple implementation of
ICustomTypeDescriptor is provided by the CustomTypeDescriptor class.

NOTE
To support design-time binding to types that implement ICustomTypeDescriptor, the type must also implement
IComponent and exist as an instance on the Form.

IListSource interface
A class that implements the IListSource interface enables list-based binding on non-list objects. The GetList
method of IListSource is used to return a bindable list from an object that does not inherit from IList.
IListSource is used by the DataSet class.
IRaiseItemChangedEvents interface
A class that implements the IRaiseItemChangedEvents interface is a bindable list that also implements the
IBindingList interface. This interface is used to indicate if your type raises ListChanged events of type
ItemChanged through its RaisesItemChangedEvents property.

NOTE
You should implement the IRaiseItemChangedEvents if your data source provides the property to list event
conversion described previously and is interacting with the BindingSource component. Otherwise, the BindingSource
will also perform property to list event conversion resulting in slower performance.

ISupportInitialize interface
A component that implements the ISupportInitialize interface takes advantages of batch optimizations for
setting properties and initializing co-dependent properties. The ISupportInitialize contains two methods:
BeginInit signals that object initialization is starting.
EndInit signals that object initialization is finishing.
ISupportInitializeNotification interface
A component that implements the ISupportInitializeNotification interface also implements the
ISupportInitialize interface. This interface allows you to notify other ISupportInitialize components that
initialization is complete. The ISupportInitializeNotification interface contains two members:
IsInitialized returns a boolean value indicating whether the component is initialized.
Initialized occurs when EndInit is called.
INotifyPropertyChanged interface
A class that implements this interface is a type that raises an event when any of its property values change.
This interface is designed to replace the pattern of having a change event for each property of a control.
When used in a BindingList<T>, a business object should implement the INotifyPropertyChanged interface
and the BindingList`1 will convert PropertyChanged events to ListChanged events of type ItemChanged.

NOTE
For change notification to occur in a binding between a bound client and a data source your bound data-source
type should either implement the INotifyPropertyChanged interface (preferred) or you can provide propertyName
Changed events for the bound type, but you shouldn't do both.

Interfaces for Implementation by Component Authors


The following interfaces are designed for consumption by the Windows Forms data-binding engine:
IBindableComponent interface
A class that implements this interface is a non-control component that supports data binding. This class
returns the data bindings and binding context of the component through the DataBindings and
BindingContext properties of this interface.

NOTE
If your component inherits from Control, you do not need to implement the IBindableComponent interface.

ICurrencyManagerProvider interface
A class that implements the ICurrencyManagerProvider interface is a component that provides its own
CurrencyManager to manage the bindings associated with this particular component. Access to the custom
CurrencyManager is provided by the CurrencyManager property.

NOTE
A class that inherits from Control manages bindings automatically through its BindingContext property, so cases in
which you need to implement the ICurrencyManagerProvider are fairly rare.

See also
Data Binding and Windows Forms
How to: Create a Simple-Bound Control on a Windows Form
Windows Forms Data Binding
Change Notification in Windows Forms Data Binding
9/1/2020 • 2 minutes to read • Edit Online

One of the most important concepts of Windows Forms data binding is change notification. To ensure that your
data source and bound controls always have the most recent data, you must add change notification for data
binding. Specifically, you want to ensure that bound controls are notified of changes that were made to their data
source, and the data source is notified of changes that were made to the bound properties of a control.
There are different kinds of change notification, depending on the kind of data binding:
Simple binding, in which a single control property is bound to a single instance of an object.
List-based binding, which can include a single control property bound to the property of an item in a list or
a control property bound to a list of objects.
Additionally, if you are creating Windows Forms controls that you want to use for data binding, you must apply
the PropertyNameChanged pattern to the controls, so that changes to the bound property of a control are
propagated to the data source.

Change Notification for Simple Binding


For simple binding, business objects must provide change notification when the value of a bound property
changes. You can do this by exposing an PropertyNameChanged event for each property of your business object
and binding the business object to controls with the BindingSource or the preferred method in which your
business object implements the INotifyPropertyChanged interface and raises a PropertyChanged event when the
value of a property changes. For more information, see How to: Implement the INotifyPropertyChanged Interface.
When you use objects that implement the INotifyPropertyChanged interface, you do not have to use the
BindingSource to bind the object to a control, but using the BindingSource is recommended.

Change Notification for List-Based Binding


Windows Forms depends on a bound list to provide property change (a list item property value changes) and list
changed (an item is deleted or added to the list) information to bound controls. Therefore, lists used for data
binding must implement the IBindingList, which provides both types of change notification. The BindingList<T> is
a generic implementation of IBindingList and is designed for use with Windows Forms data binding. You can
create a BindingList<T> that contains a business object type that implements INotifyPropertyChanged and the list
will automatically convert the PropertyChanged events to ListChanged events. If the bound list is not an
IBindingList, you must bind the list of objects to Windows Forms controls by using the BindingSource component.
The BindingSource component will provide property-to-list conversion similar to that of the BindingList<T>. For
more information, see How to: Raise Change Notifications Using a BindingSource and the
INotifyPropertyChanged Interface.

Change Notification for Custom Controls


Finally, from the control side you must expose a PropertyNameChanged event for each property designed to be
bound to data. The changes to the control property are then propagated to the bound data source. For more
information, see How to: Apply the PropertyNameChanged Pattern

See also
BindingSource
INotifyPropertyChanged
BindingList<T>
Windows Forms Data Binding
Data Sources Supported by Windows Forms
Data Binding and Windows Forms
How to: Apply the PropertyNameChanged Pattern
9/1/2020 • 2 minutes to read • Edit Online

The following code example demonstrates how to apply the PropertyNameChanged pattern to a custom control.
Apply this pattern when you implement custom controls that are used with the Windows Forms data binding
engine.

Example
// This class implements a simple user control
// that demonstrates how to apply the propertyNameChanged pattern.
[ComplexBindingProperties("DataSource", "DataMember")]
public class CustomerControl : UserControl
{
private DataGridView dataGridView1;
private Label label1;
private DateTime lastUpdate = DateTime.Now;

public EventHandler DataSourceChanged;

public object DataSource


{
get
{
return this.dataGridView1.DataSource;
}
set
{
if (DataSource != value)
{
this.dataGridView1.DataSource = value;
OnDataSourceChanged();
}
}
}

public string DataMember


{
get { return this.dataGridView1.DataMember; }

set { this.dataGridView1.DataMember = value; }


}

private void OnDataSourceChanged()


{
if (DataSourceChanged != null)
{
DataSourceChanged(this, new EventArgs());
}
}

public CustomerControl()
{
this.dataGridView1 = new System.Windows.Forms.DataGridView();
this.label1 = new System.Windows.Forms.Label();
this.dataGridView1.ColumnHeadersHeightSizeMode =
System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dataGridView1.ImeMode = System.Windows.Forms.ImeMode.Disable;
this.dataGridView1.Location = new System.Drawing.Point(19, 55);
this.dataGridView1.Size = new System.Drawing.Size(350, 150);
this.dataGridView1.TabIndex = 1;
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(19, 23);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(76, 13);
this.label1.TabIndex = 2;
this.label1.Text = "Customer List:";
this.Controls.Add(this.label1);
this.Controls.Add(this.dataGridView1);
this.Size = new System.Drawing.Size(350, 216);
}
}
' This class implements a simple user control
' that demonstrates how to apply the propertyNameChanged pattern.
<ComplexBindingProperties("DataSource", "DataMember")> _
Public Class CustomerControl
Inherits UserControl
Private dataGridView1 As DataGridView
Private label1 As Label
Private lastUpdate As DateTime = DateTime.Now

Public DataSourceChanged As EventHandler

Public Property DataSource() As Object


Get
Return Me.dataGridView1.DataSource
End Get
Set
If DataSource IsNot Value Then
Me.dataGridView1.DataSource = Value
OnDataSourceChanged()
End If
End Set
End Property

Public Property DataMember() As String


Get
Return Me.dataGridView1.DataMember
End Get
Set
Me.dataGridView1.DataMember = value
End Set
End Property

Private Sub OnDataSourceChanged()


If (DataSourceChanged IsNot Nothing) Then
DataSourceChanged(Me, New EventArgs())
End If

End Sub

Public Sub New()


Me.dataGridView1 = New System.Windows.Forms.DataGridView()
Me.label1 = New System.Windows.Forms.Label()
Me.dataGridView1.ColumnHeadersHeightSizeMode = _
System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize
Me.dataGridView1.ImeMode = System.Windows.Forms.ImeMode.Disable
Me.dataGridView1.Location = New System.Drawing.Point(19, 55)
Me.dataGridView1.Size = New System.Drawing.Size(350, 150)
Me.dataGridView1.TabIndex = 1
Me.label1.AutoSize = True
Me.label1.Location = New System.Drawing.Point(19, 23)
Me.label1.Name = "label1"
Me.label1.Size = New System.Drawing.Size(76, 13)
Me.label1.TabIndex = 2
Me.label1.Text = "Customer List:"
Me.Controls.Add(Me.label1)
Me.Controls.Add(Me.dataGridView1)
Me.Size = New System.Drawing.Size(350, 216)
End Sub
End Class

Compiling the Code


To compile the previous code example:
Paste the code into an empty code file. You must use the custom control on a Windows Form that contains a
Main method.

See also
How to: Implement the INotifyPropertyChanged Interface
Change Notification in Windows Forms Data Binding
Windows Forms Data Binding
How to: Create a Bound Control and Format the
Displayed Data
9/1/2020 • 2 minutes to read • Edit Online

With Windows Forms data binding, you can format the data displayed in a data-bound control by using the
Formatting and Advanced Binding dialog box.

To bind a control and format the displayed data


1. Connect to a data source. For more information, see Connecting to a Data Source.
2. In Visual Studio, select the control on the form, and then open the Proper ties window.
3. Expand the (DataBindings) property, and then in the (Advanced) box, click the ellipsis button ( ) to
display the Formatting and Advanced Binding dialog box, which has a complete list of properties for
that control.
4. Select the property you want to bind, and then select the Binding arrow.
A list of available data sources is displayed.
5. Expand the data source you want to bind to until you find the single data element you want.
For example, if you are binding to a column value in a dataset's table, expand the name of the dataset, and
then expand the table name to display column names.
6. Select the name of an element to bind to.
7. In the Format type box, select the format you want to apply to the data displayed in the control.
In every case, you can specify the value displayed in the control if the data source contains DBNull.
Otherwise, the options vary slightly, depending on the format type you choose. The following table shows
the format types and options.

F O RM AT T Y P E F O RM AT T IN G O P T IO N

No Formatting No options.

Numeric Specify number of decimal places by using Decimal


places up-down control.

Currency Specify number of decimal places by using Decimal


places up-down control.

Date Time Select how the date and time should be displayed by
selecting one of the items in the Type selection box.

Scientific Specify number of decimal places by using Decimal


places up-down control.
F O RM AT T Y P E F O RM AT T IN G O P T IO N

Custom Specify a custom format string using.

For more information, see Formatting Types. Note:


Custom format strings are not guaranteed to successfully
round trip between the data source and bound control.
Instead handle the Parse or Format event for the binding
and apply custom formatting in the event-handling code.

8. Select OK to close the Formatting and Advanced Binding dialog box and return to the Properties
window.

See also
How to: Create a Simple-Bound Control on a Windows Form
User Input Validation in Windows Forms
Windows Forms Data Binding
How to: Create a simple-bound control on a
Windows Form
9/1/2020 • 2 minutes to read • Edit Online

With simple binding, you can display a single data element, such as a column value from a dataset table, in a
control. You can simple-bind any property of a control to a data value.

To simple-bind a control
1. Connect to a data source. For more information, see Connecting to a Data Source.
2. In Visual Studio, select the control on the form and display the Proper ties window.
3. Expand the (DataBindings) property.
The properties most often bound are displayed underneath the (DataBindings) property. For example, in
most controls, the Text property is most frequently bound.
4. If the property you want to bind is not one of the commonly bound properties, click the Ellipsis button (
) in the (Advanced) box to display the Formatting and Advanced Binding dialog box with a complete
list of properties for that control.
5. Select the property you want to bind and click the drop-down arrow under Binding .
A list of available data sources is displayed.
6. Expand the data source you want to bind to until you find the single data element you want. For example, if
you are binding to a column value in a dataset's table, expand the name of the dataset, and then expand the
table name to display column names.
7. Click the name of an element to bind to.
8. If you were working in the Formatting and Advanced Binding dialog box, click OK to return to the
Proper ties window.
9. If you want to bind additional properties of the control, repeat steps 3 through 7.

NOTE
Because simple-bound controls show only a single data element, it is very typical to include navigation logic in a
Windows Form with simple-bound controls.

See also
Binding
Windows Forms Data Binding
Data Binding and Windows Forms
How to: Ensure Multiple Controls Bound to the Same
Data Source Remain Synchronized
9/1/2020 • 3 minutes to read • Edit Online

Oftentimes when working with data binding in Windows Forms, multiple controls are bound to the same data
source. In some cases, it may be necessary to take extra steps to ensure that the bound properties of the controls
remain synchronized with each other and the data source. These steps are necessary in two situations:
If the data source does not implement IBindingList, and therefore generate ListChanged events of type
ItemChanged.
If the data source implements IEditableObject.
In the former case, you can use a BindingSource to bind the data source to the controls. In the latter case, you use a
BindingSource and handle the BindingComplete event and call EndCurrentEdit on the associated
BindingManagerBase.

Example
The following code example demonstrates how to bind three controls—two text-box controls and a DataGridView
control—to the same column in a DataSet using a BindingSource component. This example demonstrates how to
handle the BindingComplete event and ensure that when the text value of one text box is changed, the additional
text box and the DataGridView control are updated with the correct value.
The example uses a BindingSource to bind the data source and the controls. Alternatively, you can bind the controls
directly to the data source and retrieve the BindingManagerBase for the binding from the form's BindingContext
and then handle the BindingComplete event for the BindingManagerBase. For an example of how to do this, see
the Help page about the BindingComplete event of BindingManagerBase.

// Declare the controls to be used.


private BindingSource bindingSource1;
private TextBox textBox1;
private TextBox textBox2;
private DataGridView dataGridView1;

private void InitializeControlsAndDataSource()


{
// Initialize the controls and set location, size and
// other basic properties.
this.dataGridView1 = new DataGridView();
this.bindingSource1 = new BindingSource();
this.textBox1 = new TextBox();
this.textBox2 = new TextBox();
this.dataGridView1.ColumnHeadersHeightSizeMode =
DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dataGridView1.Dock = DockStyle.Top;
this.dataGridView1.Location = new Point(0, 0);
this.dataGridView1.Size = new Size(292, 150);
this.textBox1.Location = new Point(132, 156);
this.textBox1.Size = new Size(100, 20);
this.textBox2.Location = new Point(12, 156);
this.textBox2.Size = new Size(100, 20);
this.ClientSize = new Size(292, 266);
this.Controls.Add(this.textBox2);
this.Controls.Add(this.textBox1);
this.Controls.Add(this.dataGridView1);

// Declare the DataSet and add a table and column.


DataSet set1 = new DataSet();
set1.Tables.Add("Menu");
set1.Tables[0].Columns.Add("Beverages");

// Add some rows to the table.


set1.Tables[0].Rows.Add("coffee");
set1.Tables[0].Rows.Add("tea");
set1.Tables[0].Rows.Add("hot chocolate");
set1.Tables[0].Rows.Add("milk");
set1.Tables[0].Rows.Add("orange juice");

// Set the data source to the DataSet.


bindingSource1.DataSource = set1;

//Set the DataMember to the Menu table.


bindingSource1.DataMember = "Menu";

// Add the control data bindings.


dataGridView1.DataSource = bindingSource1;
textBox1.DataBindings.Add("Text", bindingSource1,
"Beverages", true, DataSourceUpdateMode.OnPropertyChanged);
textBox2.DataBindings.Add("Text", bindingSource1,
"Beverages", true, DataSourceUpdateMode.OnPropertyChanged);
bindingSource1.BindingComplete +=
new BindingCompleteEventHandler(bindingSource1_BindingComplete);
}

private void bindingSource1_BindingComplete(object sender, BindingCompleteEventArgs e)


{
// Check if the data source has been updated, and that no error has occurred.
if (e.BindingCompleteContext ==
BindingCompleteContext.DataSourceUpdate && e.Exception == null)

// If not, end the current edit.


e.Binding.BindingManagerBase.EndCurrentEdit();
}

' Declare the controls to be used.


Private WithEvents bindingSource1 As BindingSource
Private WithEvents textBox1 As TextBox
Private WithEvents textBox2 As TextBox
Private WithEvents dataGridView1 As DataGridView

Private Sub InitializeControlsAndDataSource()


' Initialize the controls and set location, size and
' other basic properties.
Me.dataGridView1 = New DataGridView()
Me.bindingSource1 = New BindingSource()
Me.textBox1 = New TextBox()
Me.textBox2 = New TextBox()
Me.dataGridView1.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize
Me.dataGridView1.Dock = DockStyle.Top
Me.dataGridView1.Location = New Point(0, 0)
Me.dataGridView1.Size = New Size(292, 150)
Me.textBox1.Location = New Point(132, 156)
Me.textBox1.Size = New Size(100, 20)
Me.textBox2.Location = New Point(12, 156)
Me.textBox2.Size = New Size(100, 20)
Me.ClientSize = New Size(292, 266)
Me.Controls.Add(Me.textBox2)
Me.Controls.Add(Me.textBox1)
Me.Controls.Add(Me.dataGridView1)
' Declare the DataSet and add a table and column.
Dim set1 As New DataSet()
set1.Tables.Add("Menu")
set1.Tables(0).Columns.Add("Beverages")

' Add some rows to the table.


set1.Tables(0).Rows.Add("coffee")
set1.Tables(0).Rows.Add("tea")
set1.Tables(0).Rows.Add("hot chocolate")
set1.Tables(0).Rows.Add("milk")
set1.Tables(0).Rows.Add("orange juice")

' Set the data source to the DataSet.


bindingSource1.DataSource = set1

'Set the DataMember to the Menu table.


bindingSource1.DataMember = "Menu"

' Add the control data bindings.


dataGridView1.DataSource = bindingSource1
textBox1.DataBindings.Add("Text", bindingSource1, "Beverages", _
True, DataSourceUpdateMode.OnPropertyChanged)
textBox2.DataBindings.Add("Text", bindingSource1, "Beverages", _
True, DataSourceUpdateMode.OnPropertyChanged)

End Sub

Private Sub bindingSource1_BindingComplete(ByVal sender As Object, _


ByVal e As BindingCompleteEventArgs) Handles bindingSource1.BindingComplete

' Check if the data source has been updated, and that no error has occurred.
If e.BindingCompleteContext = BindingCompleteContext.DataSourceUpdate _
AndAlso e.Exception Is Nothing Then

' If not, end the current edit.


e.Binding.BindingManagerBase.EndCurrentEdit()
End If

End Sub

Compiling the Code


This code example requires
References to the System, System.Windows.Forms, and System.Drawing assemblies.
A form with the Load event handled and a call to the InitializeControlsAndDataSource method in the
example from the form's Load event handler.

See also
How to: Share Bound Data Across Forms Using the BindingSource Component
Change Notification in Windows Forms Data Binding
Interfaces Related to Data Binding
Windows Forms Data Binding
How to: Ensure the Selected Row in a Child Table
Remains at the Correct Position
9/1/2020 • 11 minutes to read • Edit Online

Oftentimes when you work with data binding in Windows Forms, you will display data in what is called a
parent/child or master/details view. This refers to a data-binding scenario where data from the same source is
displayed in two controls. Changing the selection in one control causes the data displayed in the second control to
change. For example, the first control might contain a list of customers and the second a list of orders related to the
selected customer in the first control.
Starting with the .NET Framework version 2.0, when you display data in a parent/child view you might have to take
extra steps to make sure that the currently selected row in the child table is not reset to the first row of the table. In
order to do this, you will have to cache the child table position and reset it after the parent table changes. Typically
the child reset occurs the first time a field in a row of the parent table changes.
To Cache the Current Child Position
1. Declare an integer variable to store the child list position and a Boolean variable to store whether to cache
the child position.

private int cachedPosition = -1;


private bool cacheChildPosition = true;

Private cachedPosition As Integer = - 1


Private cacheChildPosition As Boolean = True

2. Handle the ListChanged event for the binding's CurrencyManager and check for a ListChangedType of Reset.
3. Check the current position of the CurrencyManager. If it is greater than first entry in the list (typically 0),
save it to a variable.

void relatedCM_ListChanged(object sender, ListChangedEventArgs e)


{
// Check to see if this is a caching situation.
if (cacheChildPosition && cachePositionCheckBox.Checked)
{
// If so, check to see if it is a reset situation, and the current
// position is greater than zero.
CurrencyManager relatedCM = sender as CurrencyManager;
if (e.ListChangedType == ListChangedType.Reset && relatedCM.Position > 0)

// If so, cache the position of the child table.


cachedPosition = relatedCM.Position;
}
}
Private Sub relatedCM_ListChanged(ByVal sender As Object, _
ByVal e As ListChangedEventArgs)
' Check to see if this is a caching situation.
If cacheChildPosition AndAlso cachePositionCheckBox.Checked Then
' If so, check to see if it is a reset situation, and the current
' position is greater than zero.
Dim relatedCM As CurrencyManager = sender
If e.ListChangedType = ListChangedType.Reset _
AndAlso relatedCM.Position > 0 Then

' If so, cache the position of the child table.


cachedPosition = relatedCM.Position
End If
End If

End Sub

4. Handle the parent list's CurrentChanged event for the parent currency manager. In the handler, set the
Boolean value to indicate it is not a caching scenario. If the CurrentChanged occurs, the change to the parent
is a list position change and not an item value change.

void bindingSource1_CurrentChanged(object sender, EventArgs e)


{
// If the CurrentChanged event occurs, this is not a caching
// situation.
cacheChildPosition = false;
}

' Handle the current changed event. This event occurs when
' the current item is changed, but not when a field of the current
' item is changed.
Private Sub bindingSource1_CurrentChanged(ByVal sender As Object, _
ByVal e As EventArgs) Handles bindingSource1.CurrentChanged
' If the CurrentChanged event occurs, this is not a caching
' situation.
cacheChildPosition = False

End Sub

To Reset the Child Position


1. Handle the PositionChanged event for the child binding's CurrencyManager.
2. Reset the child table position to the cached position saved in the previous procedure.
void relatedCM_PositionChanged(object sender, EventArgs e)
{
// Check to see if this is a caching situation.
if (cacheChildPosition && cachePositionCheckBox.Checked)
{
CurrencyManager relatedCM = sender as CurrencyManager;

// If so, check to see if the current position is


// not equal to the cached position and the cached
// position is not out of bounds.
if (relatedCM.Position != cachedPosition && cachedPosition
> 0 && cachedPosition < relatedCM.Count)
{
relatedCM.Position = cachedPosition;
cachedPosition = -1;
}
}
}

Private Sub relatedCM_PositionChanged(ByVal sender As Object, ByVal e As EventArgs)


' Check to see if this is a caching situation.
If cacheChildPosition AndAlso cachePositionCheckBox.Checked Then
Dim relatedCM As CurrencyManager = sender

' If so, check to see if the current position is


' not equal to the cached position and the cached
' position is not out of bounds.
If relatedCM.Position <> cachedPosition AndAlso _
cachedPosition > 0 AndAlso cachedPosition < _
relatedCM.Count Then
relatedCM.Position = cachedPosition
cachedPosition = -1
End If
End If
End Sub

Example
The following example demonstrates how to save the current position on the CurrencyManager.for a child table
and reset the position after an edit is completed on the parent table. This example contains two DataGridView
controls bound to two tables in a DataSet using a BindingSource component. A relation is established between the
two tables and the relation is added to the DataSet. The position in the child table is initially set to the third row for
demonstration purposes.

using System;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace BT2
{
public class Form1 : Form
{
public Form1()
{
InitializeControlsAndDataSource();
}

// Declare the controls to be used.


private BindingSource bindingSource1;
private DataGridView dataGridView1;
private Button button1;
private DataGridView dataGridView2;
private CheckBox cachePositionCheckBox;
public DataSet set1;

private void InitializeControlsAndDataSource()


{
// Initialize the controls and set location, size and
// other basic properties.
this.dataGridView1 = new DataGridView();
this.bindingSource1 = new BindingSource();
this.button1 = new Button();
this.dataGridView2 = new DataGridView();
this.cachePositionCheckBox = new System.Windows.Forms.CheckBox();
this.dataGridView1.ColumnHeadersHeightSizeMode =
DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dataGridView1.Dock = DockStyle.Top;
this.dataGridView1.Location = new Point(0, 20);
this.dataGridView1.Size = new Size(292, 170);
this.button1.Location = new System.Drawing.Point(18, 175);
this.button1.Size = new System.Drawing.Size(125, 23);

button1.Text = "Clear Parent Field";


this.button1.Click += new System.EventHandler(this.button1_Click);
this.dataGridView2.ColumnHeadersHeightSizeMode =
System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dataGridView2.Location = new System.Drawing.Point(0, 225);
this.dataGridView2.Size = new System.Drawing.Size(309, 130);
this.cachePositionCheckBox.AutoSize = true;
this.cachePositionCheckBox.Checked = true;
this.cachePositionCheckBox.Location = new System.Drawing.Point(150, 175);
this.cachePositionCheckBox.Name = "radioButton1";
this.cachePositionCheckBox.Size = new System.Drawing.Size(151, 17);
this.cachePositionCheckBox.Text = "Cache and restore position";
this.ClientSize = new System.Drawing.Size(325, 420);
this.Controls.Add(this.dataGridView1);
this.Controls.Add(this.cachePositionCheckBox);
this.Controls.Add(this.dataGridView2);
this.Controls.Add(this.button1);

// Initialize the data.


set1 = InitializeDataSet();

// Set the data source to the DataSet.


bindingSource1.DataSource = set1;

//Set the DataMember to the Menu table.


bindingSource1.DataMember = "Customers";

// Add the control data bindings.


dataGridView1.DataSource = bindingSource1;

// Set the data source and member for the second DataGridView.
dataGridView2.DataSource = bindingSource1;
dataGridView2.DataMember = "custOrders";

// Get the currency manager for the customer orders binding.


CurrencyManager relatedCM =
bindingSource1.GetRelatedCurrencyManager("custOrders");

// Set the position in the child table for demonstration purposes.


relatedCM.Position = 3;

// Handle the current changed event. This event occurs when


// the current item is changed, but not when a field of the current
// item is changed.
bindingSource1.CurrentChanged +=
new EventHandler(bindingSource1_CurrentChanged);
new EventHandler(bindingSource1_CurrentChanged);

// Handle the two events for caching and resetting the position.
relatedCM.ListChanged += new ListChangedEventHandler(relatedCM_ListChanged);
relatedCM.PositionChanged
+= new EventHandler(relatedCM_PositionChanged);

// Set cacheing to true in case current changed event


// occurred on set up.
cacheChildPosition = true;
}

// Establish the data set with two tables and a relationship


// between them.
private DataSet InitializeDataSet()
{
set1 = new DataSet();
// Declare the DataSet and add a table and column.
set1.Tables.Add("Customers");
set1.Tables[0].Columns.Add("CustomerID");
set1.Tables[0].Columns.Add("Customer Name");
set1.Tables[0].Columns.Add("Contact Name");

// Add some rows to the table.


set1.Tables["Customers"].Rows.Add("c1", "Fabrikam, Inc.", "Ellen Adams");
set1.Tables[0].Rows.Add("c2", "Lucerne Publishing", "Don Hall");
set1.Tables[0].Rows.Add("c3", "Northwind Traders", "Lori Penor");
set1.Tables[0].Rows.Add("c4", "Tailspin Toys", "Michael Patten");
set1.Tables[0].Rows.Add("c5", "Woodgrove Bank", "Jyothi Pai");

// Declare the DataSet and add a table and column.


set1.Tables.Add("Orders");
set1.Tables[1].Columns.Add("CustomerID");
set1.Tables[1].Columns.Add("OrderNo");
set1.Tables[1].Columns.Add("OrderDate");

// Add some rows to the table.


set1.Tables[1].Rows.Add("c1", "119", "10/04/2006");
set1.Tables[1].Rows.Add("c1", "149", "10/10/2006");
set1.Tables[1].Rows.Add("c1", "159", "10/12/2006");
set1.Tables[1].Rows.Add("c2", "169", "10/10/2006");
set1.Tables[1].Rows.Add("c2", "179", "10/10/2006");
set1.Tables[1].Rows.Add("c2", "189", "10/12/2006");
set1.Tables[1].Rows.Add("c3", "122", "10/04/2006");
set1.Tables[1].Rows.Add("c4", "130", "10/10/2006");
set1.Tables[1].Rows.Add("c5", "1.29", "10/14/2006");

DataRelation dr = new DataRelation("custOrders",


set1.Tables["Customers"].Columns["CustomerID"],
set1.Tables["Orders"].Columns["CustomerID"]);
set1.Relations.Add(dr);
return set1;
}
private int cachedPosition = -1;
private bool cacheChildPosition = true;

void relatedCM_ListChanged(object sender, ListChangedEventArgs e)


{
// Check to see if this is a caching situation.
if (cacheChildPosition && cachePositionCheckBox.Checked)
{
// If so, check to see if it is a reset situation, and the current
// position is greater than zero.
CurrencyManager relatedCM = sender as CurrencyManager;
if (e.ListChangedType == ListChangedType.Reset && relatedCM.Position > 0)

// If so, cache the position of the child table.


cachedPosition = relatedCM.Position;
}
}
}
void bindingSource1_CurrentChanged(object sender, EventArgs e)
{
// If the CurrentChanged event occurs, this is not a caching
// situation.
cacheChildPosition = false;
}
void relatedCM_PositionChanged(object sender, EventArgs e)
{
// Check to see if this is a caching situation.
if (cacheChildPosition && cachePositionCheckBox.Checked)
{
CurrencyManager relatedCM = sender as CurrencyManager;

// If so, check to see if the current position is


// not equal to the cached position and the cached
// position is not out of bounds.
if (relatedCM.Position != cachedPosition && cachedPosition
> 0 && cachedPosition < relatedCM.Count)
{
relatedCM.Position = cachedPosition;
cachedPosition = -1;
}
}
}
int count = 0;
private void button1_Click(object sender, EventArgs e)
{
// For demo purposes--modifies a value in the first row of the
// parent table.
DataRow row1 = set1.Tables[0].Rows[0];
row1[1] = DBNull.Value;
}

[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}

Imports System.ComponentModel
Imports System.Data
Imports System.Drawing
Imports System.Text
Imports System.Windows.Forms

Public Class Form1


Inherits Form

Public Sub New()


InitializeControlsAndDataSource()

End Sub

' Declare the controls to be used.


Private WithEvents bindingSource1 As BindingSource
Private dataGridView1 As DataGridView
Private WithEvents button1 As Button
Private dataGridView2 As DataGridView
Private cachePositionCheckBox As CheckBox
Public set1 As DataSet
Private Sub InitializeControlsAndDataSource()
' Initialize the controls and set location, size and
' other basic properties.
Me.dataGridView1 = New DataGridView()
Me.bindingSource1 = New BindingSource()
Me.button1 = New Button()
Me.dataGridView2 = New DataGridView()
Me.cachePositionCheckBox = New System.Windows.Forms.CheckBox()
Me.dataGridView1.ColumnHeadersHeightSizeMode = _
DataGridViewColumnHeadersHeightSizeMode.AutoSize
Me.dataGridView1.Dock = DockStyle.Top
Me.dataGridView1.Location = New Point(0, 20)
Me.dataGridView1.Size = New Size(292, 170)
Me.button1.Location = New System.Drawing.Point(18, 175)
Me.button1.Size = New System.Drawing.Size(125, 23)

button1.Text = "Clear Parent Field"

Me.dataGridView2.ColumnHeadersHeightSizeMode = _
System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize
Me.dataGridView2.Location = New System.Drawing.Point(0, 225)
Me.dataGridView2.Size = New System.Drawing.Size(309, 130)
Me.cachePositionCheckBox.AutoSize = True
Me.cachePositionCheckBox.Checked = True
Me.cachePositionCheckBox.Location = New System.Drawing.Point(150, 175)
Me.cachePositionCheckBox.Name = "radioButton1"
Me.cachePositionCheckBox.Size = New System.Drawing.Size(151, 17)
Me.cachePositionCheckBox.Text = "Cache and restore position"
Me.ClientSize = New System.Drawing.Size(325, 420)
Me.Controls.Add(Me.dataGridView1)
Me.Controls.Add(Me.cachePositionCheckBox)
Me.Controls.Add(Me.dataGridView2)
Me.Controls.Add(Me.button1)

' Initialize the data.


set1 = InitializeDataSet()

' Set the data source to the DataSet.


bindingSource1.DataSource = set1

'Set the DataMember to the Menu table.


bindingSource1.DataMember = "Customers"

' Add the control data bindings.


dataGridView1.DataSource = bindingSource1

' Set the data source and member for the second DataGridView.
dataGridView2.DataSource = bindingSource1
dataGridView2.DataMember = "custOrders"

' Get the currency manager for the customer orders binding.
Dim relatedCM As CurrencyManager = _
bindingSource1.GetRelatedCurrencyManager("custOrders")

' Handle the two events for caching and resetting the position.
AddHandler relatedCM.ListChanged, AddressOf relatedCM_ListChanged
AddHandler relatedCM.PositionChanged, AddressOf relatedCM_PositionChanged

' Set the position in the child table for demonstration purposes.
relatedCM.Position = 3

' Set cacheing to true in case current changed event


' occurred on set up.
cacheChildPosition = True

End Sub
' Establish the data set with two tables and a relationship
' between them.
Private Function InitializeDataSet() As DataSet
set1 = New DataSet()
' Declare the DataSet and add a table and column.
set1.Tables.Add("Customers")
set1.Tables(0).Columns.Add("CustomerID")
set1.Tables(0).Columns.Add("Customer Name")
set1.Tables(0).Columns.Add("Contact Name")

' Add some rows to the table.


set1.Tables("Customers").Rows.Add("c1", "Fabrikam, Inc.", _
"Ellen Adams")
set1.Tables(0).Rows.Add("c2", "Lucerne Publishing", "Don Hall")
set1.Tables(0).Rows.Add("c3", "Northwind Traders", "Lori Penor")
set1.Tables(0).Rows.Add("c4", "Tailspin Toys", "Michael Patten")
set1.Tables(0).Rows.Add("c5", "Woodgrove Bank", "Jyothi Pai")

' Declare the DataSet and add a table and column.


set1.Tables.Add("Orders")
set1.Tables(1).Columns.Add("CustomerID")
set1.Tables(1).Columns.Add("OrderNo")
set1.Tables(1).Columns.Add("OrderDate")

' Add some rows to the table.


set1.Tables(1).Rows.Add("c1", "119", "10/04/2006")
set1.Tables(1).Rows.Add("c1", "149", "10/10/2006")
set1.Tables(1).Rows.Add("c1", "159", "10/12/2006")
set1.Tables(1).Rows.Add("c2", "169", "10/10/2006")
set1.Tables(1).Rows.Add("c2", "179", "10/10/2006")
set1.Tables(1).Rows.Add("c2", "189", "10/12/2006")
set1.Tables(1).Rows.Add("c3", "122", "10/04/2006")
set1.Tables(1).Rows.Add("c4", "130", "10/10/2006")
set1.Tables(1).Rows.Add("c5", "1.29", "10/14/2006")

Dim dr As New DataRelation("custOrders", _


set1.Tables("Customers").Columns("CustomerID"), _
set1.Tables("Orders").Columns("CustomerID"))
set1.Relations.Add(dr)
Return set1

End Function '


Private cachedPosition As Integer = - 1
Private cacheChildPosition As Boolean = True

Private Sub relatedCM_ListChanged(ByVal sender As Object, _


ByVal e As ListChangedEventArgs)
' Check to see if this is a caching situation.
If cacheChildPosition AndAlso cachePositionCheckBox.Checked Then
' If so, check to see if it is a reset situation, and the current
' position is greater than zero.
Dim relatedCM As CurrencyManager = sender
If e.ListChangedType = ListChangedType.Reset _
AndAlso relatedCM.Position > 0 Then

' If so, cache the position of the child table.


cachedPosition = relatedCM.Position
End If
End If

End Sub

' Handle the current changed event. This event occurs when
' the current item is changed, but not when a field of the current
' item is changed.
Private Sub bindingSource1_CurrentChanged(ByVal sender As Object, _
ByVal e As EventArgs) Handles bindingSource1.CurrentChanged
ByVal e As EventArgs) Handles bindingSource1.CurrentChanged
' If the CurrentChanged event occurs, this is not a caching
' situation.
cacheChildPosition = False

End Sub

Private Sub relatedCM_PositionChanged(ByVal sender As Object, ByVal e As EventArgs)


' Check to see if this is a caching situation.
If cacheChildPosition AndAlso cachePositionCheckBox.Checked Then
Dim relatedCM As CurrencyManager = sender

' If so, check to see if the current position is


' not equal to the cached position and the cached
' position is not out of bounds.
If relatedCM.Position <> cachedPosition AndAlso _
cachedPosition > 0 AndAlso cachedPosition < _
relatedCM.Count Then
relatedCM.Position = cachedPosition
cachedPosition = -1
End If
End If
End Sub

Private count As Integer = 0

Private Sub button1_Click(ByVal sender As Object, _


ByVal e As EventArgs) Handles button1.Click
' For demo purposes--modifies a value in the first row of the
' parent table.
Dim row1 As DataRow = set1.Tables(0).Rows(0)
row1(1) = DBNull.Value
End Sub

<STAThread()> _
Shared Sub Main()
Application.EnableVisualStyles()
Application.SetCompatibleTextRenderingDefault(False)
Application.Run(New Form1())

End Sub
End Class

To test the code example, perform the following steps:


1. Run the example.
2. Make sure the Cache and reset position check box is selected.
3. Click the Clear parent field button to cause a change in a field of the parent table. Notice that the selected
row in the child table does not change.
4. Close and run the example again. You need to do this because the reset behavior occurs only on the first
change in the parent row.
5. Clear the Cache and reset position check box.
6. Click the Clear parent field button. Notice that the selected row in the child table changes to the first row.

Compiling the Code


This example requires:
References to the System, System.Data, System.Drawing, System.Windows.Forms, and System.XML assemblies.
See also
How to: Ensure Multiple Controls Bound to the Same Data Source Remain Synchronized
BindingSource Component
Data Binding and Windows Forms
How to: Implement the IListSource Interface
9/1/2020 • 7 minutes to read • Edit Online

Implement the IListSource interface to create a bindable class that does not implement IList but instead provides a
list from another location.

Example
The following code example demonstrates how to implement the IListSource interface. A component named
EmployeeListSource exposes an IList for data binding by implementing the GetList method.

using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;

namespace IListSourceCS
{
public class EmployeeListSource : Component, IListSource
{
public EmployeeListSource() {}

public EmployeeListSource(IContainer container)


{
container.Add(this);
}

#region IListSource Members

bool IListSource.ContainsListCollection
{
get { return false; }
}

System.Collections.IList IListSource.GetList()
{
BindingList<Employee> ble = new BindingList<Employee>();

if (!this.DesignMode)
{
ble.Add(new Employee("Aaberg, Jesper", 26000000));
ble.Add(new Employee("Cajhen, Janko", 19600000));
ble.Add(new Employee("Furse, Kari", 19000000));
ble.Add(new Employee("Langhorn, Carl", 16000000));
ble.Add(new Employee("Todorov, Teodor", 15700000));
ble.Add(new Employee("Verebélyi, Ágnes", 15700000));
}

return ble;
}

#endregion
}
}

Imports System.ComponentModel

Public Class EmployeeListSource


Inherits Component
Inherits Component
Implements IListSource

<System.Diagnostics.DebuggerNonUserCode()> _
Public Sub New(ByVal Container As System.ComponentModel.IContainer)
MyClass.New()

'Required for Windows.Forms Class Composition Designer support


Container.Add(Me)

End Sub

<System.Diagnostics.DebuggerNonUserCode()> _
Public Sub New()
MyBase.New()

'This call is required by the Component Designer.


InitializeComponent()

End Sub

'Component overrides dispose to clean up the component list.


<System.Diagnostics.DebuggerNonUserCode()> _
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing AndAlso components IsNot Nothing Then
components.Dispose()
End If
MyBase.Dispose(disposing)
End Sub

'Required by the Component Designer


Private components As System.ComponentModel.IContainer

'NOTE: The following procedure is required by the Component Designer


'It can be modified using the Component Designer.
'Do not modify it using the code editor.
<System.Diagnostics.DebuggerStepThrough()> _
Private Sub InitializeComponent()
components = New System.ComponentModel.Container()
End Sub

#Region "IListSource Members"

Public ReadOnly Property ContainsListCollection() As Boolean Implements


System.ComponentModel.IListSource.ContainsListCollection
Get
Return False
End Get
End Property

Public Function GetList() As System.Collections.IList Implements System.ComponentModel.IListSource.GetList

Dim ble As New BindingList(Of Employee)

If Not Me.DesignMode Then


ble.Add(New Employee("Aaberg, Jesper", 26000000))
ble.Add(New Employee("Cajhen, Janko", 19600000))
ble.Add(New Employee("Furse, Kari", 19000000))
ble.Add(New Employee("Langhorn, Carl", 16000000))
ble.Add(New Employee("Todorov, Teodor", 15700000))
ble.Add(New Employee("Verebélyi, Ágnes", 15700000))
End If

Return ble

End Function

#End Region

End Class
End Class

using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;

namespace IListSourceCS
{
public class Employee : BusinessObjectBase
{
private string _id;
private string _name;
private Decimal parkingId;

public Employee() : this(string.Empty, 0) {}


public Employee(string name) : this(name, 0) {}

public Employee(string name, Decimal parkingId) : base()


{
this._id = System.Guid.NewGuid().ToString();

// Set values
this.Name = name;
this.ParkingID = parkingId;
}

public string ID
{
get { return _id; }
}

const string NAME = "Name";


public string Name
{
get { return _name; }
set
{
if (_name != value)
{
_name = value;

// Raise the PropertyChanged event.


OnPropertyChanged(NAME);
}
}
}

const string PARKING_ID = "Salary";


public Decimal ParkingID
{
get { return parkingId; }
set
{
if (parkingId != value)
{
parkingId = value;

// Raise the PropertyChanged event.


OnPropertyChanged(PARKING_ID);
}
}
}
}
}
Imports System.ComponentModel

Public Class Employee


Inherits BusinessObjectBase

Private _id As String


Private _name As String
Private _parkingId As Decimal

Public Sub New(ByVal name As String, ByVal parkId As Decimal)


MyBase.New()
Me._id = System.Guid.NewGuid().ToString()
' Set values
Me.Name = name
Me.ParkingID = parkId
End Sub

Public ReadOnly Property ID() As String


Get
Return _id
End Get
End Property

Const NAME_Const As String = "Name"

Public Property Name() As String


Get
Return _name
End Get
Set(ByVal value As String)
If _name <> value Then
_name = value
OnPropertyChanged(NAME_Const)
End If
End Set
End Property

Const PARKINGID_Const As String = "ParkingID"

Public Property ParkingID() As Decimal


Get
Return _parkingId
End Get
Set(ByVal value As Decimal)
If _parkingId <> value Then
_parkingId = value
OnPropertyChanged(PARKINGID_Const)
End If
End Set
End Property

End Class
using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Diagnostics;

namespace IListSourceCS
{
public class BusinessObjectBase : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members

public event PropertyChangedEventHandler PropertyChanged;

protected virtual void OnPropertyChanged(string propertyName)


{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}

private void OnPropertyChanged(PropertyChangedEventArgs e)


{
if (null != PropertyChanged)
{
PropertyChanged(this, e);
}
}

#endregion
}
}

Imports System.ComponentModel

Public Class BusinessObjectBase


Implements INotifyPropertyChanged

#Region "INotifyPropertyChanged Members"

Public Event PropertyChanged(ByVal sender As Object, ByVal e As


System.ComponentModel.PropertyChangedEventArgs) Implements
System.ComponentModel.INotifyPropertyChanged.PropertyChanged

Protected Overridable Sub OnPropertyChanged(ByVal propertyName As String)


OnPropertyChanged(New PropertyChangedEventArgs(propertyName))
End Sub

Private Sub OnPropertyChanged(ByVal e As PropertyChangedEventArgs)


RaiseEvent PropertyChanged(Me, e)
End Sub

#End Region

End Class

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace IListSourceCS
{
public class Form1 : Form
public class Form1 : Form
{
private System.ComponentModel.IContainer components = null;
private FlowLayoutPanel flowLayoutPanel1;
private Label label2;
private DataGridView dataGridView1;
private DataGridViewTextBoxColumn nameDataGridViewTextBoxColumn;
private DataGridViewTextBoxColumn salaryDataGridViewTextBoxColumn;
private DataGridViewTextBoxColumn iDDataGridViewTextBoxColumn;
private EmployeeListSource employeeListSource1;

public Form1()
{
InitializeComponent();
}

protected override void Dispose(bool disposing)


{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}

#region Windows Form Designer generated code

private void InitializeComponent()


{
this.components = new System.ComponentModel.Container();
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new
System.Windows.Forms.DataGridViewCellStyle();
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new
System.Windows.Forms.DataGridViewCellStyle();
this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
this.label2 = new System.Windows.Forms.Label();
this.dataGridView1 = new System.Windows.Forms.DataGridView();
this.nameDataGridViewTextBoxColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.salaryDataGridViewTextBoxColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.iDDataGridViewTextBoxColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.employeeListSource1 = new EmployeeListSource(this.components);
this.flowLayoutPanel1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();
this.SuspendLayout();
//
// flowLayoutPanel1
//
this.flowLayoutPanel1.AutoSize = true;
this.flowLayoutPanel1.Controls.Add(this.label2);
this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Top;
this.flowLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.flowLayoutPanel1.Name = "flowLayoutPanel1";
this.flowLayoutPanel1.Size = new System.Drawing.Size(416, 51);
this.flowLayoutPanel1.TabIndex = 11;
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(3, 6);
this.label2.Margin = new System.Windows.Forms.Padding(3, 6, 3, 6);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(408, 39);
this.label2.TabIndex = 0;
this.label2.Text = "This sample demonstrates how to implement the IListSource interface. In this
sam" +
"ple, a DataGridView is bound at design time to a Component (employeeListSource1)" +
" that implements IListSource.";
//
// dataGridView1
//
//
this.dataGridView1.AllowUserToAddRows = false;
this.dataGridView1.AllowUserToDeleteRows = false;
dataGridViewCellStyle1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)
(((byte)(255)))), ((int)(((byte)(192)))));
this.dataGridView1.AlternatingRowsDefaultCellStyle = dataGridViewCellStyle1;
this.dataGridView1.AutoGenerateColumns = false;
this.dataGridView1.ColumnHeadersHeightSizeMode =
System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
this.nameDataGridViewTextBoxColumn,
this.salaryDataGridViewTextBoxColumn,
this.iDDataGridViewTextBoxColumn});
this.dataGridView1.DataSource = this.employeeListSource1;
this.dataGridView1.Dock = System.Windows.Forms.DockStyle.Fill;
this.dataGridView1.Location = new System.Drawing.Point(0, 51);
this.dataGridView1.Name = "dataGridView1";
this.dataGridView1.RowHeadersVisible = false;
this.dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
this.dataGridView1.Size = new System.Drawing.Size(416, 215);
this.dataGridView1.TabIndex = 12;
//
// nameDataGridViewTextBoxColumn
//
this.nameDataGridViewTextBoxColumn.DataPropertyName = "Name";
this.nameDataGridViewTextBoxColumn.FillWeight = 131.7987F;
this.nameDataGridViewTextBoxColumn.HeaderText = "Name";
this.nameDataGridViewTextBoxColumn.Name = "nameDataGridViewTextBoxColumn";
//
// salaryDataGridViewTextBoxColumn
//
this.salaryDataGridViewTextBoxColumn.DataPropertyName = "ParkingID";
this.salaryDataGridViewTextBoxColumn.DefaultCellStyle = dataGridViewCellStyle2;
this.salaryDataGridViewTextBoxColumn.FillWeight = 121.8274F;
this.salaryDataGridViewTextBoxColumn.HeaderText = "Parking ID";
this.salaryDataGridViewTextBoxColumn.Name = "salaryDataGridViewTextBoxColumn";
//
// iDDataGridViewTextBoxColumn
//
this.iDDataGridViewTextBoxColumn.AutoSizeMode =
System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
this.iDDataGridViewTextBoxColumn.DataPropertyName = "ID";
this.iDDataGridViewTextBoxColumn.FillWeight = 46.37391F;
this.iDDataGridViewTextBoxColumn.HeaderText = "ID";
this.iDDataGridViewTextBoxColumn.Name = "iDDataGridViewTextBoxColumn";
this.iDDataGridViewTextBoxColumn.ReadOnly = true;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(416, 266);
this.Controls.Add(this.dataGridView1);
this.Controls.Add(this.flowLayoutPanel1);
this.Name = "Form1";
this.Text = "IListSource Sample";
this.flowLayoutPanel1.ResumeLayout(false);
this.flowLayoutPanel1.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}

#endregion
}

static class Program


{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}

Imports System.ComponentModel
Imports System.Windows.Forms

Public Class Form1


Inherits System.Windows.Forms.Form

Friend WithEvents flowLayoutPanel1 As FlowLayoutPanel


Friend WithEvents label2 As Label
Friend WithEvents dataGridView1 As DataGridView
Friend WithEvents nameDataGridViewTextBoxColumn As DataGridViewTextBoxColumn
Friend WithEvents salaryDataGridViewTextBoxColumn As DataGridViewTextBoxColumn
Friend WithEvents iDDataGridViewTextBoxColumn As DataGridViewTextBoxColumn
Friend WithEvents employeeListSource1 As EmployeeListSource

Public Sub New()


MyBase.New()

Me.InitializeComponent()
End Sub

'Form overrides dispose to clean up the component list.


<System.Diagnostics.DebuggerNonUserCode()> _
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing AndAlso components IsNot Nothing Then
components.Dispose()
End If
MyBase.Dispose(disposing)
End Sub

'Required by the Windows Form Designer


Private components As System.ComponentModel.IContainer

'NOTE: The following procedure is required by the Windows Form Designer


'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
<System.Diagnostics.DebuggerStepThrough()> _
Private Sub InitializeComponent()
components = New System.ComponentModel.Container()

Dim dataGridViewCellStyle1 = New System.Windows.Forms.DataGridViewCellStyle()


Dim dataGridViewCellStyle2 = New System.Windows.Forms.DataGridViewCellStyle()
Me.flowLayoutPanel1 = New System.Windows.Forms.FlowLayoutPanel()
Me.label2 = New System.Windows.Forms.Label()
Me.dataGridView1 = New System.Windows.Forms.DataGridView()
Me.nameDataGridViewTextBoxColumn = New System.Windows.Forms.DataGridViewTextBoxColumn()
Me.salaryDataGridViewTextBoxColumn = New System.Windows.Forms.DataGridViewTextBoxColumn()
Me.iDDataGridViewTextBoxColumn = New System.Windows.Forms.DataGridViewTextBoxColumn()
Me.employeeListSource1 = New EmployeeListSource(Me.components)
Me.flowLayoutPanel1.SuspendLayout()
CType(Me.dataGridView1, System.ComponentModel.ISupportInitialize).BeginInit()
Me.SuspendLayout()
'
' flowLayoutPanel1
'
Me.flowLayoutPanel1.AutoSize = True
Me.flowLayoutPanel1.Controls.Add(Me.label2)
Me.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Top
Me.flowLayoutPanel1.Location = New System.Drawing.Point(0, 0)
Me.flowLayoutPanel1.Name = "flowLayoutPanel1"
Me.flowLayoutPanel1.Size = New System.Drawing.Size(416, 51)
Me.flowLayoutPanel1.TabIndex = 11
'
' label2
'
Me.label2.AutoSize = True
Me.label2.Location = New System.Drawing.Point(3, 6)
Me.label2.Margin = New System.Windows.Forms.Padding(3, 6, 3, 6)
Me.label2.Name = "label2"
Me.label2.Size = New System.Drawing.Size(408, 39)
Me.label2.TabIndex = 0
Me.label2.Text = "This sample demonstrates how to implement the IListSource interface. In this sam" +
_
"ple, a DataGridView is bound at design time to a Component (employeeListSource1)" + _
" that implements IListSource."
'
' dataGridView1
'
Me.dataGridView1.AllowUserToAddRows = False
Me.dataGridView1.AllowUserToDeleteRows = False
dataGridViewCellStyle1.BackColor = System.Drawing.Color.FromArgb(255, 255, 192)
Me.dataGridView1.AlternatingRowsDefaultCellStyle = dataGridViewCellStyle1
Me.dataGridView1.AutoGenerateColumns = False
Me.dataGridView1.ColumnHeadersHeightSizeMode =
System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize
Me.dataGridView1.Columns.AddRange(New System.Windows.Forms.DataGridViewColumn() { _
Me.nameDataGridViewTextBoxColumn, Me.salaryDataGridViewTextBoxColumn, Me.iDDataGridViewTextBoxColumn})
Me.dataGridView1.DataSource = Me.employeeListSource1
Me.dataGridView1.Dock = System.Windows.Forms.DockStyle.Fill
Me.dataGridView1.Location = New System.Drawing.Point(0, 51)
Me.dataGridView1.Name = "dataGridView1"
Me.dataGridView1.RowHeadersVisible = False
Me.dataGridView1.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect
Me.dataGridView1.Size = New System.Drawing.Size(416, 215)
Me.dataGridView1.TabIndex = 12
'
' nameDataGridViewTextBoxColumn
'
Me.nameDataGridViewTextBoxColumn.DataPropertyName = "Name"
Me.nameDataGridViewTextBoxColumn.FillWeight = 131.7987F
Me.nameDataGridViewTextBoxColumn.HeaderText = "Name"
Me.nameDataGridViewTextBoxColumn.Name = "nameDataGridViewTextBoxColumn"
'
' salaryDataGridViewTextBoxColumn
'
Me.salaryDataGridViewTextBoxColumn.DataPropertyName = "ParkingID"
Me.salaryDataGridViewTextBoxColumn.DefaultCellStyle = dataGridViewCellStyle2
Me.salaryDataGridViewTextBoxColumn.FillWeight = 121.8274F
Me.salaryDataGridViewTextBoxColumn.HeaderText = "Parking ID"
Me.salaryDataGridViewTextBoxColumn.Name = "salaryDataGridViewTextBoxColumn"
'
' iDDataGridViewTextBoxColumn
'
Me.iDDataGridViewTextBoxColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill
Me.iDDataGridViewTextBoxColumn.DataPropertyName = "ID"
Me.iDDataGridViewTextBoxColumn.FillWeight = 46.37391F
Me.iDDataGridViewTextBoxColumn.HeaderText = "ID"
Me.iDDataGridViewTextBoxColumn.Name = "iDDataGridViewTextBoxColumn"
Me.iDDataGridViewTextBoxColumn.ReadOnly = True
'
' Form1
'
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0F, 13.0F)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
Me.ClientSize = New System.Drawing.Size(416, 266)
Me.Controls.Add(Me.dataGridView1)
Me.Controls.Add(Me.flowLayoutPanel1)
Me.Name = "Form1"
Me.Text = "IListSource Sample"
Me.flowLayoutPanel1.ResumeLayout(False)
Me.flowLayoutPanel1.PerformLayout()
CType(Me.dataGridView1, System.ComponentModel.ISupportInitialize).EndInit()
Me.ResumeLayout(False)
Me.PerformLayout()

End Sub

Shared Sub Main()


Application.Run(New Form1())
End Sub
End Class

Compiling the Code


This example requires:
References to the System.Drawing and System.Windows.Forms assemblies.

See also
IListSource
ITypedList
BindingList<T>
IBindingList
Data Binding and Windows Forms
How to: Implement the INotifyPropertyChanged
Interface
9/1/2020 • 5 minutes to read • Edit Online

The following code example demonstrates how to implement the INotifyPropertyChanged interface. Implement
this interface on business objects that are used in Windows Forms data binding. When implemented, the interface
communicates to a bound control the property changes on a business object.

Example
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.CompilerServices;
using System.Windows.Forms;

// Change the namespace to the project name.


namespace TestNotifyPropertyChangedCS
{
// This form demonstrates using a BindingSource to bind
// a list to a DataGridView control. The list does not
// raise change notifications. However the DemoCustomer type
// in the list does.
public partial class Form1 : Form
{
// This button causes the value of a list element to be changed.
private Button changeItemBtn = new Button();

// This DataGridView control displays the contents of the list.


private DataGridView customersDataGridView = new DataGridView();

// This BindingSource binds the list to the DataGridView control.


private BindingSource customersBindingSource = new BindingSource();

public Form1()
{
InitializeComponent();

// Set up the "Change Item" button.


this.changeItemBtn.Text = "Change Item";
this.changeItemBtn.Dock = DockStyle.Bottom;
this.changeItemBtn.Click +=
new EventHandler(changeItemBtn_Click);
this.Controls.Add(this.changeItemBtn);

// Set up the DataGridView.


customersDataGridView.Dock = DockStyle.Top;
this.Controls.Add(customersDataGridView);

this.Size = new Size(400, 200);


}

private void Form1_Load(object sender, EventArgs e)


{
// Create and populate the list of DemoCustomer objects
// which will supply data to the DataGridView.
BindingList<DemoCustomer> customerList = new BindingList<DemoCustomer>();
customerList.Add(DemoCustomer.CreateNewCustomer());
customerList.Add(DemoCustomer.CreateNewCustomer());
customerList.Add(DemoCustomer.CreateNewCustomer());
customerList.Add(DemoCustomer.CreateNewCustomer());

// Bind the list to the BindingSource.


this.customersBindingSource.DataSource = customerList;

// Attach the BindingSource to the DataGridView.


this.customersDataGridView.DataSource =
this.customersBindingSource;
}

// Change the value of the CompanyName property for the first


// item in the list when the "Change Item" button is clicked.
void changeItemBtn_Click(object sender, EventArgs e)
{
// Get a reference to the list from the BindingSource.
BindingList<DemoCustomer> customerList =
this.customersBindingSource.DataSource as BindingList<DemoCustomer>;

// Change the value of the CompanyName property for the


// first item in the list.
customerList[0].CustomerName = "Tailspin Toys";
customerList[0].PhoneNumber = "(708)555-0150";
}
}

// This is a simple customer class that


// implements the IPropertyChange interface.
public class DemoCustomer : INotifyPropertyChanged
{
// These fields hold the values for the public properties.
private Guid idValue = Guid.NewGuid();
private string customerNameValue = String.Empty;
private string phoneNumberValue = String.Empty;

public event PropertyChangedEventHandler PropertyChanged;

// This method is called by the Set accessor of each property.


// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}

// The constructor is private to enforce the factory pattern.


private DemoCustomer()
{
customerNameValue = "Customer";
phoneNumberValue = "(312)555-0100";
}

// This is the public factory method.


public static DemoCustomer CreateNewCustomer()
{
return new DemoCustomer();
}

// This property represents an ID, suitable


// for use as a primary key in a database.
public Guid ID
{
get
{
return this.idValue;
}
}

public string CustomerName


{
get
{
return this.customerNameValue;
}

set
{
if (value != this.customerNameValue)
{
this.customerNameValue = value;
NotifyPropertyChanged();
}
}
}

public string PhoneNumber


{
get
{
return this.phoneNumberValue;
}

set
{
if (value != this.phoneNumberValue)
{
this.phoneNumberValue = value;
NotifyPropertyChanged();
}
}
}
}
}

Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Drawing
Imports System.Runtime.CompilerServices
Imports System.Windows.Forms

' This form demonstrates using a BindingSource to bind


' a list to a DataGridView control. The list does not
' raise change notifications. However the DemoCustomer type
' in the list does.

Public Class Form1


Inherits System.Windows.Forms.Form
' This button causes the value of a list element to be changed.
Private changeItemBtn As New Button()

' This DataGridView control displays the contents of the list.


Private customersDataGridView As New DataGridView()

' This BindingSource binds the list to the DataGridView control.


Private customersBindingSource As New BindingSource()

Public Sub New()


InitializeComponent()

' Set up the "Change Item" button.


Me.changeItemBtn.Text = "Change Item"
Me.changeItemBtn.Dock = DockStyle.Bottom
AddHandler Me.changeItemBtn.Click, AddressOf changeItemBtn_Click
Me.Controls.Add(Me.changeItemBtn)
Me.Controls.Add(Me.changeItemBtn)

' Set up the DataGridView.


customersDataGridView.Dock = DockStyle.Top
Me.Controls.Add(customersDataGridView)

Me.Size = New Size(400, 200)


End Sub

Private Sub Form1_Load(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles Me.Load

' Create and populate the list of DemoCustomer objects


' which will supply data to the DataGridView.
Dim customerList As New BindingList(Of DemoCustomer)

customerList.Add(DemoCustomer.CreateNewCustomer())
customerList.Add(DemoCustomer.CreateNewCustomer())
customerList.Add(DemoCustomer.CreateNewCustomer())

' Bind the list to the BindingSource.


Me.customersBindingSource.DataSource = customerList

' Attach the BindingSource to the DataGridView.


Me.customersDataGridView.DataSource = Me.customersBindingSource
End Sub

' This event handler changes the value of the CompanyName


' property for the first item in the list.
Private Sub changeItemBtn_Click(ByVal sender As Object, ByVal e As EventArgs)
' Get a reference to the list from the BindingSource.
Dim customerList As BindingList(Of DemoCustomer) = _
CType(customersBindingSource.DataSource, BindingList(Of DemoCustomer))

' Change the value of the CompanyName property for the


' first item in the list.
customerList(0).CustomerName = "Tailspin Toys"
customerList(0).PhoneNumber = "(708)555-0150"
End Sub
End Class

' This class implements a simple customer type


' that implements the IPropertyChange interface.
Public Class DemoCustomer
Implements INotifyPropertyChanged

' These fields hold the values for the public properties.
Private idValue As Guid = Guid.NewGuid()
Private customerNameValue As String = String.Empty
Private phoneNumberValue As String = String.Empty

Public Event PropertyChanged As PropertyChangedEventHandler _


Implements INotifyPropertyChanged.PropertyChanged

' This method is called by the Set accessor of each property.


' The CallerMemberName attribute that is applied to the optional propertyName
' parameter causes the property name of the caller to be substituted as an argument.
Private Sub NotifyPropertyChanged(<CallerMemberName()> Optional ByVal propertyName As String = Nothing)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub

' The constructor is private to enforce the factory pattern.


Private Sub New()
customerNameValue = "Customer"
phoneNumberValue = "(312)555-0100"
End Sub

' This is the public factory method.


Public Shared Function CreateNewCustomer() As DemoCustomer
Return New DemoCustomer()
End Function

' This property represents an ID, suitable


' for use as a primary key in a database.
Public ReadOnly Property ID() As Guid
Get
Return Me.idValue
End Get
End Property

Public Property CustomerName() As String


Get
Return Me.customerNameValue
End Get

Set(ByVal value As String)


If Not (value = customerNameValue) Then
Me.customerNameValue = value
NotifyPropertyChanged()
End If
End Set
End Property

Public Property PhoneNumber() As String


Get
Return Me.phoneNumberValue
End Get

Set(ByVal value As String)


If Not (value = phoneNumberValue) Then
Me.phoneNumberValue = value
NotifyPropertyChanged()
End If
End Set
End Property
End Class

See also
How to: Apply the PropertyNameChanged Pattern
Windows Forms Data Binding
How to: Raise Change Notifications Using a BindingSource and the INotifyPropertyChanged Interface
Change Notification in Windows Forms Data Binding
How to: Implement the ITypedList Interface
9/1/2020 • 9 minutes to read • Edit Online

Implement the ITypedList interface to enable discovery of the schema for a bindable list.

Example
The following code example demonstrates how to implement the ITypedList interface. A generic type named
SortableBindingList derives from the BindingList<T> class and implements the ITypedList interface. A simple class
named Customer provides data, which is bound to the header of a DataGridView control.
using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Windows.Forms;
using System.Collections;
using System.Reflection;

namespace ITypedListCS
{
[Serializable()]
public class SortableBindingList<T> : BindingList<T>, ITypedList
{
[NonSerialized()]
private PropertyDescriptorCollection properties;

public SortableBindingList() : base()


{
// Get the 'shape' of the list.
// Only get the public properties marked with Browsable = true.
PropertyDescriptorCollection pdc = TypeDescriptor.GetProperties(
typeof(T),
new Attribute[] { new BrowsableAttribute(true) });

// Sort the properties.


properties = pdc.Sort();
}

#region ITypedList Implementation

public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)


{
PropertyDescriptorCollection pdc;

if (listAccessors!=null && listAccessors.Length>0)


{
// Return child list shape.
pdc = ListBindingHelper.GetListItemProperties(listAccessors[0].PropertyType);
}
else
{
// Return properties in sort order.
pdc = properties;
}

return pdc;
}

// This method is only used in the design-time framework


// and by the obsolete DataGrid control.
public string GetListName(PropertyDescriptor[] listAccessors)
{
return typeof(T).Name;
}

#endregion
}
}
Imports System.ComponentModel
Imports System.Collections.Generic
Imports System.Windows.Forms

<Serializable()> _
Public Class SortableBindingList(Of Tkey)
Inherits BindingList(Of Tkey)
Implements ITypedList

<NonSerialized()> _
Private properties As PropertyDescriptorCollection

Public Sub New()


MyBase.New()

' Get the 'shape' of the list.


' Only get the public properties marked with Browsable = true.
Dim pdc As PropertyDescriptorCollection = TypeDescriptor.GetProperties(GetType(Tkey), New Attribute()
{New BrowsableAttribute(True)})

' Sort the properties.


properties = pdc.Sort()

End Sub

#Region "ITypedList Implementation"

Public Function GetItemProperties(ByVal listAccessors() As System.ComponentModel.PropertyDescriptor) As


System.ComponentModel.PropertyDescriptorCollection Implements
System.ComponentModel.ITypedList.GetItemProperties

Dim pdc As PropertyDescriptorCollection

If (Not (listAccessors Is Nothing)) And (listAccessors.Length > 0) Then


' Return child list shape
pdc = ListBindingHelper.GetListItemProperties(listAccessors(0).PropertyType)
Else
' Return properties in sort order
pdc = properties
End If

Return pdc

End Function

' This method is only used in the design-time framework


' and by the obsolete DataGrid control.
Public Function GetListName( _
ByVal listAccessors() As PropertyDescriptor) As String _
Implements System.ComponentModel.ITypedList.GetListName

Return GetType(Tkey).Name

End Function

#End Region

End Class

using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;

namespace ITypedListCS
{
class Customer : INotifyPropertyChanged
{
public Customer() {}

public Customer(int id, string name, string company, string address, string city, string state, string
zip)
{
this._id = id;
this._name = name;
this._company = company;
this._address = address;
this._city = city;
this._state = state;
this._zip = zip;
}

#region Public Properties

private int _id;

public int ID
{
get { return _id; }
set
{
if (_id != value)
{
_id = value;
OnPropertyChanged(new PropertyChangedEventArgs("ID"));
}
}
}

private string _name;

public string Name


{
get { return _name; }
set
{
if (_name != value)
{
_name = value;
OnPropertyChanged(new PropertyChangedEventArgs("Name"));
}
}
}

private string _company;

public string Company


{
get { return _company; }
set
{
if (_company != value)
{
_company = value;
OnPropertyChanged(new PropertyChangedEventArgs("Company"));
}
}
}

private string _address;

public string Address


{
get { return _address; }
set
{
if (_address != value)
{
_address = value;
OnPropertyChanged(new PropertyChangedEventArgs("Address"));
}
}
}

private string _city;

public string City


{
get { return _city; }
set
{
if (_city != value)
{
_city = value;
OnPropertyChanged(new PropertyChangedEventArgs("City"));
}
}
}

private string _state;

public string State


{
get { return _state; }
set
{
if (_state != value)
{
_state = value;
OnPropertyChanged(new PropertyChangedEventArgs("State"));
}
}
}

private string _zip;

public string ZipCode


{
get { return _zip; }
set
{
if (_zip != value)
{
_zip = value;
OnPropertyChanged(new PropertyChangedEventArgs("ZipCode"));
}
}
}

#endregion

#region INotifyPropertyChanged Members

public event PropertyChangedEventHandler PropertyChanged;

protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)


{
if (null != PropertyChanged)
{
PropertyChanged(this, e);
}
}

#endregion
#endregion
}
}

Imports System.ComponentModel

Public Class Customer


Implements INotifyPropertyChanged

Public Sub New()

End Sub

Public Sub New(ByVal id As Integer, ByVal name As String, ByVal company As String, ByVal address As
String, ByVal city As String, ByVal state As String, ByVal zip As String)
Me._id = id
Me._name = name
Me._company = company
Me._address = address
Me._city = city
Me._state = state
Me._zip = zip

End Sub

#Region "Public Properties"

Private _id As Integer


Public Property ID() As Integer
Get
Return _id
End Get
Set(ByVal value As Integer)
If _id <> value Then
_id = value
OnPropertyChanged(New PropertyChangedEventArgs("ID"))
End If
End Set
End Property

Private _name As String

Public Property Name() As String


Get
Return _name
End Get
Set(ByVal value As String)
If _name <> value Then
_name = value
OnPropertyChanged(New PropertyChangedEventArgs("Name"))
End If
End Set
End Property

Private _company As String

Public Property Company() As String


Get
Return _company
End Get
Set(ByVal value As String)
If _company <> value Then
_company = value
OnPropertyChanged(New PropertyChangedEventArgs("Company"))
End If
End Set
End Property
Private _address As String

Public Property Address() As String


Get
Return _address
End Get
Set(ByVal value As String)
If _address <> value Then
_address = value
OnPropertyChanged(New PropertyChangedEventArgs("Address"))
End If
End Set
End Property

Private _city As String

Public Property City() As String


Get
Return _city
End Get
Set(ByVal value As String)
If _city <> value Then
_city = value
OnPropertyChanged(New PropertyChangedEventArgs("City"))
End If
End Set
End Property

Private _state As String

Public Property State() As String


Get
Return _state
End Get
Set(ByVal value As String)
If _state <> value Then
_state = value
OnPropertyChanged(New PropertyChangedEventArgs("State"))
End If
End Set
End Property

Private _zip As String

Public Property ZipCode() As String


Get
Return _zip
End Get
Set(ByVal value As String)
If _zip <> value Then
_zip = value
OnPropertyChanged(New PropertyChangedEventArgs("ZipCode"))
End If
End Set
End Property

#End Region

#Region "INotifyPropertyChanged Members"

Public Event PropertyChanged(ByVal sender As Object, ByVal e As


System.ComponentModel.PropertyChangedEventArgs) Implements
System.ComponentModel.INotifyPropertyChanged.PropertyChanged

Protected Overridable Sub OnPropertyChanged(ByVal e As PropertyChangedEventArgs)


Protected Overridable Sub OnPropertyChanged(ByVal e As PropertyChangedEventArgs)
RaiseEvent PropertyChanged(Me, e)
End Sub

#End Region

End Class

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace ITypedListCS
{
public partial class Form1 : Form
{
private SortableBindingList<Customer> sortableBindingListOfCustomers;
private BindingList<Customer> bindingListOfCustomers;

private System.ComponentModel.IContainer components = null;


private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1;
private System.Windows.Forms.Label label2;
private DataGridView dataGridView1;
private Button button1;
private Button button2;

public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)


{
this.sortableBindingListOfCustomers = new SortableBindingList<Customer>();
this.bindingListOfCustomers = new BindingList<Customer>();

this.dataGridView1.DataSource = this.bindingListOfCustomers;
}

private void button1_Click(object sender, EventArgs e)


{
this.dataGridView1.DataSource = null;
this.dataGridView1.DataSource = this.sortableBindingListOfCustomers;
}

private void button2_Click(object sender, EventArgs e)


{
this.dataGridView1.DataSource = null;
this.dataGridView1.DataSource = this.bindingListOfCustomers;
}

protected override void Dispose(bool disposing)


{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}

#region Windows Form Designer generated code

private void InitializeComponent()


{
{
System.ComponentModel.ComponentResourceManager resources = new
System.ComponentModel.ComponentResourceManager(typeof(Form1));
this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
this.label2 = new System.Windows.Forms.Label();
this.dataGridView1 = new System.Windows.Forms.DataGridView();
this.button1 = new System.Windows.Forms.Button();
this.button2 = new System.Windows.Forms.Button();
this.flowLayoutPanel1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();
this.SuspendLayout();
//
// flowLayoutPanel1
//
this.flowLayoutPanel1.AutoSize = true;
this.flowLayoutPanel1.Controls.Add(this.label2);
this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Top;
this.flowLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.flowLayoutPanel1.Name = "flowLayoutPanel1";
this.flowLayoutPanel1.Size = new System.Drawing.Size(566, 51);
this.flowLayoutPanel1.TabIndex = 13;
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(3, 6);
this.label2.Margin = new System.Windows.Forms.Padding(3, 6, 3, 6);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(558, 39);
this.label2.TabIndex = 0;
this.label2.Text = "This sample demonstrates how to implement the ITypedList interface. Clicking
on the 'Sort Columns' button will bind the DataGridView to a sub-classed BindingList<T> that implements
ITypedList to provide a sorted list of columns. Clicking on the 'Reset' button will bind the DataGridView to
a normal BindingList<T>.";
//
// dataGridView1
//
this.dataGridView1.AllowUserToAddRows = false;
this.dataGridView1.AllowUserToDeleteRows = false;
this.dataGridView1.Anchor = ((System.Windows.Forms.AnchorStyles)
((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.dataGridView1.AutoSizeColumnsMode =
System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill;
this.dataGridView1.ColumnHeadersHeightSizeMode =
System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dataGridView1.Location = new System.Drawing.Point(6, 57);
this.dataGridView1.Name = "dataGridView1";
this.dataGridView1.ReadOnly = true;
this.dataGridView1.RowHeadersVisible = false;
this.dataGridView1.Size = new System.Drawing.Size(465, 51);
this.dataGridView1.TabIndex = 14;
//
// button1
//
this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top
| System.Windows.Forms.AnchorStyles.Right)));
this.button1.Location = new System.Drawing.Point(477, 57);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(82, 23);
this.button1.TabIndex = 15;
this.button1.Text = "Sort Columns";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// button2
//
this.button2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top
| System.Windows.Forms.AnchorStyles.Right)));
| System.Windows.Forms.AnchorStyles.Right)));
this.button2.Location = new System.Drawing.Point(477, 86);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(82, 23);
this.button2.TabIndex = 16;
this.button2.Text = "Reset";
this.button2.UseVisualStyleBackColor = true;
this.button2.Click += new System.EventHandler(this.button2_Click);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(566, 120);
this.Controls.Add(this.button2);
this.Controls.Add(this.button1);
this.Controls.Add(this.dataGridView1);
this.Controls.Add(this.flowLayoutPanel1);
this.Name = "Form1";
this.Text = "ITypedList Sample";
this.Load += new System.EventHandler(this.Form1_Load);
this.flowLayoutPanel1.ResumeLayout(false);
this.flowLayoutPanel1.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}

#endregion
}

static class Program


{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}

Imports System.ComponentModel
Imports System.Windows.Forms

Public Class Form1


Inherits System.Windows.Forms.Form

Friend WithEvents flowLayoutPanel1 As FlowLayoutPanel


Friend WithEvents label2 As System.Windows.Forms.Label
Friend WithEvents dataGridView1 As DataGridView
Friend WithEvents button1 As Button
Friend WithEvents button2 As Button

Dim sortableBindingListOfCustomers As SortableBindingList(Of Customer)


Dim bindingListOfCustomers As BindingList(Of Customer)

Public Sub New()


MyBase.New()

Me.InitializeComponent()
End Sub

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load


Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

sortableBindingListOfCustomers = New SortableBindingList(Of Customer)()


bindingListOfCustomers = New BindingList(Of Customer)()

Me.dataGridView1.DataSource = bindingListOfCustomers
End Sub

Private Sub button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles


button1.Click
Me.dataGridView1.DataSource = Nothing
Me.dataGridView1.DataSource = sortableBindingListOfCustomers
End Sub

Private Sub button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles


button2.Click
Me.dataGridView1.DataSource = Nothing
Me.dataGridView1.DataSource = bindingListOfCustomers
End Sub

'Form overrides dispose to clean up the component list.


<System.Diagnostics.DebuggerNonUserCode()> _
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing AndAlso components IsNot Nothing Then
components.Dispose()
End If
MyBase.Dispose(disposing)
End Sub

'Required by the Windows Form Designer


Private components As System.ComponentModel.IContainer

'NOTE: The following procedure is required by the Windows Form Designer


'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
<System.Diagnostics.DebuggerStepThrough()> _
Private Sub InitializeComponent()
Dim resources As System.ComponentModel.ComponentResourceManager = New
System.ComponentModel.ComponentResourceManager(GetType(Form1))
Me.flowLayoutPanel1 = New System.Windows.Forms.FlowLayoutPanel
Me.label2 = New System.Windows.Forms.Label
Me.dataGridView1 = New System.Windows.Forms.DataGridView
Me.button1 = New System.Windows.Forms.Button
Me.button2 = New System.Windows.Forms.Button
Me.flowLayoutPanel1.SuspendLayout()
CType(Me.dataGridView1, System.ComponentModel.ISupportInitialize).BeginInit()
Me.SuspendLayout()
'
'flowLayoutPanel1
'
Me.flowLayoutPanel1.AutoSize = True
Me.flowLayoutPanel1.Controls.Add(Me.label2)
Me.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Top
Me.flowLayoutPanel1.Location = New System.Drawing.Point(0, 0)
Me.flowLayoutPanel1.Name = "flowLayoutPanel1"
Me.flowLayoutPanel1.Size = New System.Drawing.Size(566, 51)
Me.flowLayoutPanel1.TabIndex = 13
'
'label2
'
Me.label2.AutoSize = True
Me.label2.Location = New System.Drawing.Point(3, 6)
Me.label2.Margin = New System.Windows.Forms.Padding(3, 6, 3, 6)
Me.label2.Name = "label2"
Me.label2.Size = New System.Drawing.Size(558, 39)
Me.label2.TabIndex = 0
Me.label2.Text = "This sample demonstrates how to implement the ITypedList interface. Clicking on the
'Sort Columns' button will bind the DataGridView to a sub-classed BindingList<T> that implements ITypedList to
'Sort Columns' button will bind the DataGridView to a sub-classed BindingList<T> that implements ITypedList to
provide a sorted list of columns. Clicking on the 'Reset' button will bind the DataGridView to a normal
BindingList<T>."
'
'dataGridView1
'
Me.dataGridView1.AllowUserToAddRows = False
Me.dataGridView1.AllowUserToDeleteRows = False
Me.dataGridView1.Anchor = CType((((System.Windows.Forms.AnchorStyles.Top Or
System.Windows.Forms.AnchorStyles.Bottom) _
Or System.Windows.Forms.AnchorStyles.Left) _
Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
Me.dataGridView1.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill
Me.dataGridView1.ColumnHeadersHeightSizeMode =
System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize
Me.dataGridView1.Location = New System.Drawing.Point(6, 57)
Me.dataGridView1.Name = "dataGridView1"
Me.dataGridView1.ReadOnly = True
Me.dataGridView1.RowHeadersVisible = False
Me.dataGridView1.Size = New System.Drawing.Size(465, 51)
Me.dataGridView1.TabIndex = 14
'
'button1
'
Me.button1.Anchor = CType((System.Windows.Forms.AnchorStyles.Top Or
System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
Me.button1.Location = New System.Drawing.Point(477, 57)
Me.button1.Name = "button1"
Me.button1.Size = New System.Drawing.Size(82, 23)
Me.button1.TabIndex = 15
Me.button1.Text = "Sort Columns"
Me.button1.UseVisualStyleBackColor = True
'
'button2
'
Me.button2.Location = New System.Drawing.Point(477, 86)
Me.button2.Name = "button2"
Me.button2.Size = New System.Drawing.Size(82, 23)
Me.button2.TabIndex = 16
Me.button2.Text = "Reset"
Me.button2.UseVisualStyleBackColor = True
'
'Form1
'
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
Me.ClientSize = New System.Drawing.Size(566, 120)
Me.Controls.Add(Me.button2)
Me.Controls.Add(Me.button1)
Me.Controls.Add(Me.dataGridView1)
Me.Controls.Add(Me.flowLayoutPanel1)
Me.Name = "Form1"
Me.Text = "ITypedList Sample"
Me.flowLayoutPanel1.ResumeLayout(False)
Me.flowLayoutPanel1.PerformLayout()
CType(Me.dataGridView1, System.ComponentModel.ISupportInitialize).EndInit()
Me.ResumeLayout(False)
Me.PerformLayout()
End Sub

Shared Sub Main()


Application.Run(New Form1())
End Sub

End Class
Compiling the Code
This example requires:
References to the System.Drawing and System.Windows.Forms assemblies.

See also
ITypedList
BindingList<T>
IBindingList
Data Binding and Windows Forms
How to: Navigate Data in Windows Forms
9/1/2020 • 2 minutes to read • Edit Online

In a Windows application, the easiest way to navigate through records in a data source is to bind a BindingSource
component to the data source and then bind controls to the BindingSource. You can then use the built-in
navigation method on the BindingSource such a MoveNext, MoveLast, MovePrevious and MoveFirst. Using these
methods will adjust the Position and Current properties of the BindingSource appropriately. You can also find an
item and set it as the current item by setting the Position property.
To increment the position in a data source
1. Set the Position property of the BindingSource for your bound data to the record position to go to. The
following example illustrates using the MoveNext method of the BindingSource to increment the Position
property when the nextButton is clicked. The BindingSource is associated with the Customers table of a
dataset Northwind .

NOTE
Setting the Position property to a value beyond the first or last record does not result in an error, as the .NET
Framework will not allow you to set the position to a value outside the bounds of the list. If it is important in your
application to know whether you have gone past the first or last record, include logic to test whether you will exceed
the data element count.

private void nextButton_Click(object sender, System.EventArgs e)


{
this.customersBindingSource.MoveNext();
}

Private Sub nextButton_Click(ByVal sender As Object, _


ByVal e As System.EventArgs) Handles nextButton.Click
Me.customersBindingSource.MoveNext()
End Sub

To check whether you have passed the end or beginning


1. Create an event handler for the PositionChanged event. In the handler, you can test whether the proposed
position value has exceeded the actual data element count.
The following example illustrates how you can test whether you have reached the last data element. In the
example, if you are at the last element, the Next button on the form is disabled.

NOTE
Be aware that, should you change the list you are navigating in code, you should re-enable the Next button, so that
users may browse the entire length of the new list. Additionally, be aware that the above PositionChanged event for
the specific BindingSource you are working with needs to be associated with its event-handling method. The
following is an example of a method for handling the PositionChanged event:
void customersBindingSource_PositionChanged(object sender, EventArgs e)
{
if (customersBindingSource.Position == customersBindingSource.Count - 1)
nextButton.Enabled = false;
else
nextButton.Enabled = true;
}

Sub customersBindingSource_PositionChanged(ByVal sender As Object, _


ByVal e As EventArgs)

If customersBindingSource.Position = _
customersBindingSource.Count - 1 Then
nextButton.Enabled = False
Else
nextButton.Enabled = True
End If
End Sub

To find an item and set it as the current item


1. Find the record you wish to set as the current item. You can do this using the Find method of the
BindingSource, if your data source implements IBindingList. Some examples of data sources that implement
IBindingList are BindingList<T> and DataView.

void findButton_Click(object sender, EventArgs e)


{
int foundIndex = customersBindingSource.Find("CustomerID", "ANTON");
customersBindingSource.Position = foundIndex;
}

Sub findButton_Click(ByVal sender As Object, ByVal e As EventArgs) _


Handles findButton.Click
Dim foundIndex As Integer = customersBindingSource.Find("CustomerID", _
"ANTON")
customersBindingSource.Position = foundIndex
End Sub

See also
Data Sources Supported by Windows Forms
Change Notification in Windows Forms Data Binding
Data Binding and Windows Forms
Windows Forms Data Binding
Windows Forms Security
9/1/2020 • 2 minutes to read • Edit Online

Windows Forms features a security model that is code-based (security levels are set for code, regardless of the
user running the code). This is in addition to any security schemas that may be in place already on your computer
system. These can include those in the browser (such as the zone-based security available in Internet Explorer) or
the operating system (such as the credential-based security of Windows NT).

In This Section
Security in Windows Forms Overview
Briefly explains the .NET Framework security model and the basic steps necessary to ensure the Windows Forms in
your application are secure.
More Secure File and Data Access in Windows Forms
Describes how to access files and data in a semi-trusted environment.
More Secure Printing in Windows Forms
Describes how to access printing features in a semi-trusted environment.
Additional Security Considerations in Windows Forms
Describes performing window manipulation, using the Clipboard, and making calls to unmanaged code in a semi-
trusted environment.

Related Sections
Default Security Policy
Lists the default permissions granted in the Full Trust, Local Intranet, and Internet permission sets.
General Security Policy Administration
Gives information about the administering the .NET Framework security policy and elevating permissions.
Dangerous Permissions and Policy Administration
Discusses some of the.NET Framework permissions that can potentially allow the security system to be
circumvented.
Secure Coding Guidelines
Links to topics that explain the best practices for securely writing code against the .NET Framework.
Requesting Permissions
Discusses the use of attributes to let the runtime know what permissions your code needs to run.
Key Security Concepts
Links to topics that cover the basic aspects of code security.
Code Access Security Basics
Discusses the basics of working with the .NET Framework run time security policy.
Determining When to Modify Security Policy
Explains how to determine when your applications need to diverge from the default security policy.
Deploying Security Policy
Discusses the best manner for deploying security policy changes.
Security in Windows Forms Overview
9/1/2020 • 8 minutes to read • Edit Online

Before the release of the .NET Framework, all code running on a user's computer had the same rights or
permissions to access resources that a user of the computer had. For example, if the user was allowed to access
the file system, the code was allowed to access the file system; if the user was allowed to access a database, the
code was allowed to access that database. Although these rights or permissions may be acceptable for code in
executables that the user has explicitly installed on the local computer, they may not be acceptable for potentially
malicious code coming from the Internet or a local Intranet. This code should not be able to access the user's
computer resources without permission.
The .NET Framework introduces an infrastructure called Code Access Security that lets you differentiate the
permissions, or rights, that code has from the rights that the user has. By default, code coming from the Internet
and the Intranet can only run in what is known as partial trust. Partial trust subjects an application to a series of
restrictions: among other things, an application is restricted from accessing the local hard disk, and cannot run
unmanaged code. The .NET Framework controls the resources that code is allowed to access based on the identity
of that code: where it came from, whether it has a Strong-Named Assemblies, whether it is signed with a
certificate, and so on.
ClickOnce technology, which you use to deploy Windows Forms applications, helps make it easier for you to
develop applications that run in partial trust, in full trust, or in partial trust with elevated permissions. ClickOnce
provides features such as Permission Elevation and Trusted Application Deployment so that your application can
request full trust or elevated permissions from the local user in a responsible manner.

Understanding Security in the .NET Framework


Code access security allows code to be trusted to varying degrees, depending on where the code originates and
on other aspects of the code's identity. For more information about the evidence the common language runtime
uses to determine security policy, see Evidence. It helps protect computer systems from malicious code and helps
protect trusted code from intentionally or accidentally compromising security. Code access security also gives you
more control over what actions your application can perform, because you can specify only those permissions you
need your application to have. Code access security affects all managed code that targets the common language
runtime, even if that code does not make a single code-access-security permission check. For more information
about security in the .NET Framework, see Key Security Concepts and Code Access Security Basics.
If the user run a Windows Forms executable file directly off of a Web server or a file share, the degree of trust
granted to your application depends on where the code resides, and how it is started. When an application runs, it
is automatically evaluated and it receives a named permission set from the common language runtime. By default,
the code from the local computer is granted the Full Trust permission set, code from a local network is granted the
Local Intranet permission set, and code from the Internet is granted the Internet permission set.
NOTE
In the .NET Framework version 1.0 Service Pack 1 and Service Pack 2, the Internet zone code group receives the Nothing
permission set. In all other releases of the .NET Framework, the Internet zone code group receives the Internet permissions
set.
The default permissions granted in each of these permission sets are listed in the Default Security Policy topic. Depending on
the permissions that the application receives, it either runs correctly or generates a security exception.
Many Windows Forms applications will be deployed using ClickOnce. The tools used for generating a ClickOnce deployment
have different security defaults than what was discussed earlier. For more information, see the following discussion.

The actual permissions granted to your application can be different from the default values, because the security
policy can be modified; this means that your application can have permission on one computer, but not on another.

Developing a More Secure Windows Forms Application


Security is important in all steps of application development. Start by reviewing and following the Secure Coding
Guidelines.
Next, decide whether your application must run in full trust, or whether it should run in partial trust. Running your
application in full trust makes it easier to access resources on the local computer, but exposes your application and
its users to high security risks if you do not design and develop your application strictly according to the Secure
Coding Guidelines topic. Running your application in partial trust makes it easier to develop a more secure
application and reduces much risk, but requires more planning in how to implement certain features.
If you choose partial trust (that is, either the Internet or Local Intranet permission sets), decide how you want your
application to behave in this environment. Windows Forms provides alternative, more secure ways to implement
features when in a semi-trusted environment. Certain parts of your application, such as data access, can be
designed and written differently for both partial trust and full trust environments. Some Windows Forms features,
such as application settings, are designed to work in partial trust. For more information, see Application Settings
Overview.
If your application needs more permissions than partial trust allows, but you do not want to run in full trust, you
can run in partial trust while asserting only those additional permissions you need. For example, if you want to run
in partial trust, but must grant your application read-only access to a directory on the user's file system, you can
request FileIOPermission only for that directory. Used correctly, this approach can give your application increased
functionality and minimize security risks to your users.
When you develop an application that will run in partial trust, keep track of what permissions your application
must run and what permissions your application could optionally use. When all the permissions are known, you
should make a declarative request for permission at the application level. Requesting permissions informs the
.NET Framework run time about which permissions your application needs and which permissions it specifically
does not want. For more information about requesting permissions, see Requesting Permissions.
When you request optional permissions, you must handle security exceptions that will be generated if your
application performs an action that requires permissions not granted to it. Appropriate handling of the
SecurityException will ensure that your application can continue to operate. Your application can use the exception
to determine whether a feature should become disabled for the user. For example, an application can disable the
Save menu option if the required file permission is not granted.
Sometimes, it is difficult to know if you have asserted all the appropriate permissions. A method call which looks
innocuous on the surface, for example, may access the file system at some point during its execution. If you do not
deploy your application with all the required permissions, it may test fine when you debug it on your desktop, but
fail when deployed. Both the .NET Framework 2.0 SDK and Visual Studio 2005 contain tools for calculating the
permissions an application needs: the MT.exe command line tool and the Calculate Permissions feature of Visual
Studio, respectively.
The following topics describe additional Windows Forms security features.

TO P IC DESC RIP T IO N

- More Secure File and Data Access in Windows Forms Describes how to access files and data in a partial trust
environment.

- More Secure Printing in Windows Forms Describes how to access printing features in a partial trust
environment.

- Additional Security Considerations in Windows Forms Describes performing window manipulation, using the
Clipboard, and making calls to unmanaged code in a partial
trust environment.

Deploying an Application with the Appropriate Permissions


The most common means of deploying a Windows Forms application to a client computer is with ClickOnce, a
deployment technology that describes all of the components your application needs to run. ClickOnce uses XML
files called manifests to describe the assemblies and files that make up your application, and also the permissions
your application requires.
ClickOnce has two technologies for requesting elevated permissions on a client computer. Both technologies rely
on the use of Authenticode certificates. The certificates help provide some assurance to your users that the
application has come from a trusted source.
The following table describes these technologies.

EL EVAT ED P ERM ISSIO N T EC H N O LO GY DESC RIP T IO N

Permission Elevation Prompts the user with a security dialog box the first time your
application runs. The Permission Elevation dialog box
informs the user about who published the application, so that
the user can make an informed decision about whether to
grant it additional trust

Trusted Application Deployment Involves a system administrator performing a one-time


installation of a publisher's Authenticode certificate on a client
computer. From that point on, any applications signed with
the certificate are regarded as trusted, and can run at full
trust on the local computer without additional prompting.

Which technology you choose will depend on your deployment environment. For more information, see Choosing
a ClickOnce Deployment Strategy.
By default, ClickOnce applications deployed using either Visual Studio or the .NET Framework SDK tools (Mage.exe
and MageUI.exe) are configured to run on a client computer that has Full Trust. If you are deploying your
application by using partial trust or by using only some additional permissions, you will have to change this
default. You can do this with either Visual Studio or the .NET Framework SDK tool MageUI.exe when you configure
your deployment. For more information about how to use MageUI.exe, see Walkthrough: Manually deploying a
ClickOnce application. Also see How to: Set Custom Permissions for a ClickOnce Application or How to: Set
Custom Permissions for a ClickOnce Application.
For more information about the security aspects of ClickOnce and Permission Elevation, see Securing ClickOnce
Applications. For more information about Trusted Application Deployment, see Trusted Application Deployment
Overview.
Testing the Application
If you have deployed your Windows Forms application by using Visual Studio, you can enable debugging in partial
trust or a restricted permission set from the development environment. Also see How to: Debug a ClickOnce
Application with Restricted Permissions.

See also
Windows Forms Security
Code Access Security Basics
ClickOnce Security and Deployment
Trusted Application Deployment Overview
Mage.exe (Manifest Generation and Editing Tool)
MageUI.exe (Manifest Generation and Editing Tool, Graphical Client)
More Secure File and Data Access in Windows Forms
9/1/2020 • 9 minutes to read • Edit Online

The .NET Framework uses permissions to help protect resources and data. Where your application can read or
write data depends on the permissions granted to the application. When your application runs in a partial trust
environment, you might not have access to your data or you might have to change the way you access the data.
When you encounter a security restriction, you have two options: assert the permission (assuming it has been
granted to your application), or use a version of the feature written to work in partial trust. The following sections
discuss how to work with file, database, and registry access from applications that are running in a partial trust
environment.

NOTE
By default, tools that generate ClickOnce deployments default these deployments to requesting Full Trust from the
computers on which they run. If you decide you want the added security benefits of running in partial trust, you must
change this default in either Visual Studio or one of the Windows SDK tools (Mage.exe or MageUI.exe). For more information
about Windows Forms security, and on how to determine the appropriate trust level for your application, see Security in
Windows Forms Overview.

File Access
The FileIOPermission class controls file and folder access in the .NET Framework. By default, the security system
does not grant the FileIOPermission to partial trust environments such as the local intranet and Internet zones.
However, an application that requires file access can still function in these environments if you modify the design
of your application or use different methods to access files. By default, the local intranet zone is granted the right
to have same site access and same directory access, to connect back to the site of its origin, and to read from its
installation directory. By default, the Internet zone, is only granted the right to connect back to the site of its origin.
User-Specified Files
One way to deal with not having file access permission is to prompt the user to provide specific file information by
using the OpenFileDialog or SaveFileDialog class. This user interaction helps provide some assurance that the
application cannot maliciously load private files or overwrite important files. The OpenFile and OpenFile methods
provide read and write file access by opening the file stream for the file that the user specified. The methods also
help protect the user's file by obscuring the file's path.

NOTE
These permissions differ depending on whether your application is in the Internet zone or Intranet zone. Internet zone
applications can only use the OpenFileDialog, whereas Intranet applications have unrestricted file dialog permission.

The FileDialogPermission class specifies what type of file dialog box your application can use. The following table
shows the value you must have to use each FileDialog class.

C L A SS REQ UIRED A C C ESS VA L UE

OpenFileDialog Open

SaveFileDialog Save
NOTE
The specific permission is not requested until the OpenFile method is actually called.

Permission to display a file dialog box does not grant your application full access to all members of the FileDialog,
OpenFileDialog, and SaveFileDialog classes. For the exact permissions that are required to call each method, see
the reference topic for that method in the .NET Framework class library documentation.
The following code example uses the OpenFile method to open a user-specified file into a RichTextBox control. The
example requires FileDialogPermission and the associated Open enumeration value. The example demonstrates
how to handle the SecurityException to determine whether the save feature should be disabled. This example
requires that your Form has a Button control named ButtonOpen , and a RichTextBox control named RtfBoxMain .

NOTE
The programming logic for the save feature is not shown in the example.

Private Sub ButtonOpen_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles ButtonOpen.Click

Dim editingFileName as String = ""


Dim saveAllowed As Boolean = True

' Displays the OpenFileDialog.


If (OpenFileDialog1.ShowDialog() = DialogResult.OK) Then
Dim userStream as System.IO.Stream
Try
' Opens the file stream for the file selected by the user.
userStream =OpenFileDialog1.OpenFile()
Me.RtfBoxMain.LoadFile(userStream, _
RichTextBoxStreamType.PlainText)
Finally
userStream.Close()
End Try

' Tries to get the file name selected by the user.


' Failure means that the application does not have
' unrestricted permission to the file.
Try
editingFileName = OpenFileDialog1.FileName
Catch ex As Exception
If TypeOf ex Is System.Security.SecurityException Then
' The application does not have unrestricted permission
' to the file so the save feature will be disabled.
saveAllowed = False
Else
Throw ex
End If
End Try
End If
End Sub
private void ButtonOpen_Click(object sender, System.EventArgs e)
{
String editingFileName = "";
Boolean saveAllowed = true;

// Displays the OpenFileDialog.


if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
// Opens the file stream for the file selected by the user.
using (System.IO.Stream userStream = openFileDialog1.OpenFile())
{
this.RtfBoxMain.LoadFile(userStream,
RichTextBoxStreamType.PlainText);
userStream.Close();
}

// Tries to get the file name selected by the user.


// Failure means that the application does not have
// unrestricted permission to the file.
try
{
editingFileName = openFileDialog1.FileName;
}
catch (Exception ex)
{
if (ex is System.Security.SecurityException)
{
// The application does not have unrestricted permission
// to the file so the save feature will be disabled.
saveAllowed = false;
}
else
{
throw ex;
}
}
}
}

NOTE
In Visual C#, ensure that you add code to enable the event handler. By using the code from the previous example, the
following code shows how to enable the event handler.
this.ButtonOpen.Click += newSystem.Windows.Forms.EventHandler(this.ButtonOpen_Click);

Other Files
Sometimes you will need to read or write to files that the user does not specify, such as when you must persist
application settings. In the local intranet and Internet zones, your application will not have permission to store data
in a local file. However, your application will be able to store data in isolated storage. Isolated storage is an abstract
data compartment (not a specific storage location) that contains one or more isolated storage files, called stores,
that contain the actual directory locations where data is stored. File access permissions like FileIOPermission are
not required; instead, the IsolatedStoragePermission class controls the permissions for isolated storage. By default,
applications that are running in the local intranet and Internet zones can store data using isolated storage;
however, settings like disk quota can vary. For more information about isolated storage, see Isolated Storage.
The following example uses isolated storage to write data to a file located in a store. The example requires
IsolatedStorageFilePermission and the DomainIsolationByUser enumeration value. The example demonstrates
reading and writing certain property values of the Button control to a file in isolated storage. The Read function
would be called after the application starts and the Write function would be called before the application ends.
The example requires that the Read and Write functions exist as members of a Form that contains a Button
control named MainButton .

' Reads the button options from the isolated storage. Uses Default values
' for the button if the options file does not exist.
Public Sub Read()
Dim isoStore As System.IO.IsolatedStorage.IsolatedStorageFile = _
System.IO.IsolatedStorage.IsolatedStorageFile. _
GetUserStoreForDomain()

Dim filename As String = "options.txt"


Try
' Checks to see if the options.txt file exists.
If (isoStore.GetFileNames(filename).GetLength(0) <> 0) Then

' Opens the file because it exists.


Dim isos As New System.IO.IsolatedStorage.IsolatedStorageFileStream _
(filename, IO.FileMode.Open,isoStore)
Dim reader as System.IO.StreamReader
Try
reader = new System.IO.StreamReader(isos)

' Reads the values stored.


Dim converter As System.ComponentModel.TypeConverter
converter = System.ComponentModel.TypeDescriptor.GetConverter _
(GetType(Color))

Me.MainButton.BackColor = _
CType(converter.ConvertFromString _
(reader.ReadLine()), Color)
me.MainButton.ForeColor = _
CType(converter.ConvertFromString _
(reader.ReadLine()), Color)

converter = System.ComponentModel.TypeDescriptor.GetConverter _
(GetType(Font))
me.MainButton.Font = _
CType(converter.ConvertFromString _
(reader.ReadLine()), Font)

Catch ex As Exception
Debug.WriteLine("Cannot read options " + _
ex.ToString())
Finally
reader.Close()
End Try
End If

Catch ex As Exception
Debug.WriteLine("Cannot read options " + ex.ToString())
End Try
End Sub

' Writes the button options to the isolated storage.


Public Sub Write()
Dim isoStore As System.IO.IsolatedStorage.IsolatedStorageFile = _
System.IO.IsolatedStorage.IsolatedStorageFile. _
GetUserStoreForDomain()

Dim filename As String = "options.txt"


Try
' Checks if the file exists, and if it does, tries to delete it.
If (isoStore.GetFileNames(filename).GetLength(0) <> 0) Then
isoStore.DeleteFile(filename)
End If
Catch ex As Exception
Debug.WriteLine("Cannot delete file " + ex.ToString())
End Try
' Creates the options.txt file and writes the button options to it.
Dim writer as System.IO.StreamWriter
Try
Dim isos As New System.IO.IsolatedStorage.IsolatedStorageFileStream _
(filename, IO.FileMode.CreateNew, isoStore)

writer = New System.IO.StreamWriter(isos)


Dim converter As System.ComponentModel.TypeConverter

converter = System.ComponentModel.TypeDescriptor.GetConverter _
(GetType(Color))
writer.WriteLine(converter.ConvertToString( _
Me.MainButton.BackColor))
writer.WriteLine(converter.ConvertToString( _
Me.MainButton.ForeColor))

converter = System.ComponentModel TypeDescriptor.GetConverter _


(GetType(Font))
writer.WriteLine(converter.ConvertToString( _
Me.MainButton.Font))

Catch ex as Exception
Debug.WriteLine("Cannot write options " + ex.ToString())

Finally
writer.Close()
End Try
End Sub

// Reads the button options from the isolated storage. Uses default values
// for the button if the options file does not exist.
public void Read()
{
System.IO.IsolatedStorage.IsolatedStorageFile isoStore =
System.IO.IsolatedStorage.IsolatedStorageFile.
GetUserStoreForDomain();

string filename = "options.txt";


try
{
// Checks to see if the options.txt file exists.
if (isoStore.GetFileNames(filename).GetLength(0) != 0)
{
// Opens the file because it exists.
System.IO.IsolatedStorage.IsolatedStorageFileStream isos =
new System.IO.IsolatedStorage.IsolatedStorageFileStream
(filename, System.IO.FileMode.Open,isoStore);
System.IO.StreamReader reader = null;
try
{
reader = new System.IO.StreamReader(isos);

// Reads the values stored.


TypeConverter converter ;
converter = TypeDescriptor.GetConverter(typeof(Color));

this.MainButton.BackColor =
(Color)(converter.ConvertFromString(reader.ReadLine()));
this.MainButton.ForeColor =
(Color)(converter.ConvertFromString(reader.ReadLine()));

converter = TypeDescriptor.GetConverter(typeof(Font));
this.MainButton.Font =
(Font)(converter.ConvertFromString(reader.ReadLine()));
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine
("Cannot read options " + ex.ToString());
}
finally
{
reader.Close();
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine
("Cannot read options " + ex.ToString());
}
}

// Writes the button options to the isolated storage.


public void Write()
{
System.IO.IsolatedStorage.IsolatedStorageFile isoStore =
System.IO.IsolatedStorage.IsolatedStorageFile.
GetUserStoreForDomain();

string filename = "options.txt";


try
{
// Checks if the file exists and, if it does, tries to delete it.
if (isoStore.GetFileNames(filename).GetLength(0) != 0)
{
isoStore.DeleteFile(filename);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine
("Cannot delete file " + ex.ToString());
}

// Creates the options file and writes the button options to it.
System.IO.StreamWriter writer = null;
try
{
System.IO.IsolatedStorage.IsolatedStorageFileStream isos = new
System.IO.IsolatedStorage.IsolatedStorageFileStream(filename,
System.IO.FileMode.CreateNew,isoStore);

writer = new System.IO.StreamWriter(isos);


TypeConverter converter ;

converter = TypeDescriptor.GetConverter(typeof(Color));
writer.WriteLine(converter.ConvertToString(
this.MainButton.BackColor));
writer.WriteLine(converter.ConvertToString(
this.MainButton.ForeColor));

converter = TypeDescriptor.GetConverter(typeof(Font));
writer.WriteLine(converter.ConvertToString(
this.MainButton.Font));

}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine
("Cannot write options " + ex.ToString());
}
finally
{
writer.Close();
}
}
}

Database Access
The permissions required to access a database vary based on the database provider; however, only applications
that are running with the appropriate permissions can access a database through a data connection. For more
information about the permissions that are required to access a database, see Code Access Security and ADO.NET.
If you cannot directly access a database because you want your application to run in partial trust, you can use a
Web service as an alternative means to access your data. A Web service is a piece of software that can be
programmatically accessed over a network. With Web services, applications can share data across code group
zones. By default, applications in the local intranet and Internet zones are granted the right to access their sites of
origin, which enables them to call a Web service hosted on the same server. For more information see Web
Services in ASP.NET AJAX or Windows Communication Foundation.

Registry Access
The RegistryPermission class controls access to the operating system registry. By default, only applications that are
running locally can access the registry. RegistryPermission only grants an application the right to try registry
access; it does not guarantee access will succeed, because the operating system still enforces security on the
registry.
Because you cannot access the registry under partial trust, you may need to find other methods of storing your
data. When you store application settings, use isolated storage instead of the registry. Isolated storage can also be
used to store other application-specific files. You can also store global application information about the server or
site of origin, because by default an application is granted the right to access the site of its origin.

See also
More Secure Printing in Windows Forms
Additional Security Considerations in Windows Forms
Security in Windows Forms Overview
Windows Forms Security
Mage.exe (Manifest Generation and Editing Tool)
MageUI.exe (Manifest Generation and Editing Tool, Graphical Client)
More Secure Printing in Windows Forms
9/1/2020 • 2 minutes to read • Edit Online

Windows Forms applications frequently include printing abilities. The .NET Framework uses the
PrintingPermission class to control access to printing capabilities and the associated PrintingPermissionLevel
enumeration value to indicate the level of access. By default, printing is enabled by default in the Local Intranet and
Internet zones; however, the level of access is restricted in both zones. Whether your application can print, requires
user interaction, or cannot print depends upon the permission value granted to the application. By default, the
Local Intranet zone receives DefaultPrinting access and the Intranet zone receives SafePrinting access.
The following table shows the functionality available at each printing permission level.

P RIN T IN GP ERM ISSIO N L EVEL DESC RIP T IO N

AllPrinting Provides full access to all installed printers.

DefaultPrinting Enables programmatic printing to the default printer and safer


printing through a restrictive printing dialog box.
DefaultPrinting is a subset of AllPrinting.

SafePrinting Provides printing only from a more-restricted dialog box.


SafePrinting is a subset of DefaultPrinting.

NoPrinting Prevents access to printers. NoPrinting is a subset of


SafePrinting.

See also
More Secure File and Data Access in Windows Forms
Additional Security Considerations in Windows Forms
Security in Windows Forms Overview
Windows Forms Security
Additional Security Considerations in Windows
Forms
9/1/2020 • 6 minutes to read • Edit Online

.NET Framework security settings might cause your application to run differently in a partial trust environment
than on your local computer. The .NET Framework restricts access to such critical local resources as the file system,
network, and unmanaged APIs, among other things. The security settings affect the ability to call the Microsoft
Windows API or other APIs that cannot be verified by the security system. Security also affects other aspects of
your application, including file and data access, and printing. For more information about file and data access in a
partial trust environment, see More Secure File and Data Access in Windows Forms. For more information about
printing in a partial trust environment, see More Secure Printing in Windows Forms.
The following sections discuss how to work with the Clipboard, perform window manipulation, and call the
Windows API from applications that are running in a partial trust environment.

Clipboard Access
The UIPermission class controls access to the Clipboard, and the associated UIPermissionClipboard enumeration
value indicates the level of access. The following table shows the possible permission levels.

UIP ERM ISSIO N C L IP B O A RD VA L UE DESC RIP T IO N

AllClipboard The Clipboard can be used without restriction.

OwnClipboard The Clipboard can be used with some restrictions. The ability
to put data on the Clipboard (Copy or Cut command
operations) is unrestricted. Intrinsic controls that accept paste,
such as a text box, can accept Clipboard data, but user
controls cannot programmatically read from the Clipboard.

NoClipboard The Clipboard cannot be used.

By default, the Local Intranet zone receives AllClipboard access and the Internet zone receives OwnClipboard
access. This means that the application can copy data to the Clipboard, but the application cannot
programmatically paste to or read from the Clipboard. These restrictions prevent programs without full trust from
reading content copied to the Clipboard by another application. If your application requires full Clipboard access
but you do not have the permissions, you will have to elevate the permissions for your application. For more
information about elevating permissions, see General Security Policy Administration.

Window Manipulation
The UIPermission class also controls permission to perform window manipulation and other UI-related actions,
and the associated UIPermissionWindow enumeration value indicates the level of access. The following table
shows the possible permission levels.
By default, the Local Intranet zone receives AllWindows access and the Internet zone receives
SafeTopLevelWindows access. This means that in the Internet zone, the application can perform most windowing
and UI actions, but the window's appearance will be modified. The modified window displays a balloon notification
when first run, contains modified title bar text, and requires a close button on the title bar. The balloon notification
and the title bar identify to the user of the application that the application is running under partial trust.
UIP ERM ISSIO N W IN DO W VA L UE DESC RIP T IO N

AllWindows Users can use all windows and user input events without
restriction.

SafeTopLevelWindows Users can use only safer top-level windows and safer
subwindows for drawing, and can use only user input events
for the user interface within those top-level windows and
subwindows. These safer windows are clearly labeled and have
minimum and maximum size restrictions. The restrictions
prevent potentially harmful spoofing attacks, such as imitating
system logon screens or the system desktop, and restricts
programmatic access to parent windows, focus-related APIs,
and use of the ToolTip control,

SafeSubWindows Users can use only safer subwindows for drawing, and can use
only user input events for the user interface within that
subwindow. A control displayed within a browser is an
example of a safer subwindow.

NoWindows Users cannot use any windows or user interface events. No


user interface can be used.

Each permission level identified by the UIPermissionWindow enumeration allows fewer actions than the level
above it. The following tables indicate the actions that are restricted by the SafeTopLevelWindows and
SafeSubWindows values. For exact permissions that are required for each member, see the reference for that
member in the .NET Framework class library documentation.
SafeTopLevelWindows permission restricts the actions listed in the following table.

C O M P O N EN T REST RIC T ED A C T IO N S

Application - Setting the SafeTopLevelCaptionFormat property.

Control - Getting the Parent property.


- Setting the Region property.
- Calling the FindForm , Focus, FromChildHandle and
FromHandle, PreProcessMessage, ReflectMessage, or
SetTopLevel method.
- Calling the GetChildAtPoint method if the control returned
is not a child of the calling control.
- Modify control focus inside a container control.

Cursor - Setting the Clip property.


- Calling the Hide method.

DataGrid - Calling the ProcessTabKey method.

Form - Getting the ActiveForm or MdiParent property.


- Setting the ControlBox, ShowInTaskbar, or TopMost
property.
- Setting the Opacity property below 50%.
- Setting the WindowState property to Minimized
programmatically.
- Calling the Activate method.
- Using the None, FixedToolWindow, and
SizableToolWindowFormBorderStyle enumeration values.
C O M P O N EN T REST RIC T ED A C T IO N S

NotifyIcon - Using the NotifyIcon component is completely restricted.

The SafeSubWindows value restricts the actions listed in the following table, in addition to the restrictions placed
by the SafeTopLevelWindows value.

C O M P O N EN T REST RIC T ED A C T IO N S

CommonDialog - Showing a dialog box derived from the CommonDialog


class.

Control - Calling the CreateGraphics method.


- Setting the Cursor property.

Cursor - Setting the Current property.

MessageBox - Calling the Show method.

Hosting Third-Party Controls


Another kind of window manipulation can occur if your forms host third-party controls. A third-party control is
any custom UserControl that you have not developed and compiled yourself. Although the hosting scenario is
hard to exploit, it is theoretically possible for a third-party control to expand its rendering surface to cover the
entire area of your form. This control could then mimic a critical dialog box, and request information such as
username/password combinations or bank account numbers from your users.
To limit this potential risk, use third-party controls only from vendors you can trust. If you use third-party controls
you have downloaded from an unverifiable source, we recommend that you review the source code for potential
exploits. After you've verified that the source is non-malicious, you should compile the assembly yourself to
ensure that the source matches the assembly.

Windows API Calls


If your application design requires calling a function from the Windows API, you are accessing unmanaged code.
In this case the code's actions to the window or operating system cannot be determined when you are working
with Windows API calls or values. The SecurityPermission class and the UnmanagedCode value of the
SecurityPermissionFlag enumeration control access to unmanaged code. An application can access unmanaged
code only when it is granted the UnmanagedCode permission. By default, only applications that are running
locally can call unmanaged code.
Some Windows Forms members provide unmanaged access that requires the UnmanagedCode permission. The
following table lists the members in the System.Windows.Forms namespace that require the permission. For more
information about the permissions that are required for a member, see the .NET Framework class library
documentation.

C O M P O N EN T M EM B ER

Application - AddMessageFilter method


- CurrentInputLanguage property
- Exit method
- ExitThread method
- ThreadException event
C O M P O N EN T M EM B ER

CommonDialog - HookProc method


- OwnerWndProc\ method
- Reset method
- RunDialog method

Control - CreateParams method


- DefWndProc method
- DestroyHandle method
- WndProc method

Help - ShowHelp methods


- ShowHelpIndex method

NativeWindow - NativeWindow class

Screen - FromHandle method

SendKeys - Send method


- SendWait method

If your application does not have permission to call unmanaged code, your application must request
UnmanagedCode permission, or you must consider alternative ways of implementing features; in many cases,
Windows Forms provides a managed alternative to Windows API functions. If no alternative means exist and the
application must access unmanaged code, you will have to elevate the permissions for the application.
Permission to call unmanaged code allows an application to perform most anything. Therefore, permission to call
unmanaged code should only be granted for applications that come from a trusted source. Alternatively,
depending on the application, the piece of application functionality that makes the call to unmanaged code could
be optional, or enabled in the full trust environment only. For more information about dangerous permissions, see
Dangerous Permissions and Policy Administration. For more information about elevating permissions, see General
Security Policy Administration.

See also
More Secure File and Data Access in Windows Forms
More Secure Printing in Windows Forms
Security in Windows Forms Overview
Windows Forms Security
Securing ClickOnce Applications
ClickOnce Deployment for Windows Forms
9/1/2020 • 2 minutes to read • Edit Online

The following topics describe ClickOnce, a technology used for easily deploying Windows Forms applications to
client computers.

Related Sections
Choosing a ClickOnce Deployment Strategy
Presents several options for deploying ClickOnce applications.
Choosing a ClickOnce Update Strategy
Presents several options for updating ClickOnce applications.
Securing ClickOnce Applications
Explains the security implications of ClickOnce deployment.
Troubleshooting ClickOnce Deployments
Describes various problems that can occur when deploying ClickOnce applications, and documents the top-level
error messages that ClickOnce might generate.
ClickOnce and Application Settings
Describes how ClickOnce deployment works with application settings, which stores application and user settings
for future retrieval.
Trusted Application Deployment Overview
Describes a ClickOnce feature that allows trusted applications to run with a higher level of permission on client
computers.
ClickOnce and Authenticode
Describes how Authenticode technology is used in trusted application deployment.
Walkthrough: Manually Deploying a ClickOnce Application
Demonstrates using command-line and SDK tools to deploy a ClickOnce application without using Visual Studio.
How to: Add a Trusted Publisher to a Client Computer for ClickOnce Applications
Demonstrates the one-time configuration of client computers required for trusted application deployment.
How to: Specify an Alternate Location for Deployment Updates
Demonstrates configuring a ClickOnce application, using SDK tools, to check a different location for new versions
of an application.
Walkthrough: Downloading Assemblies on Demand with the ClickOnce Deployment API
Demonstrates using API calls to retrieve an assembly the first time your application attempts to load it.
How to: Retrieve Query String Information in an Online ClickOnce Application
Demonstrates retrieving parameters from the URL used to run a ClickOnce application.
ClickOnce Cache Overview
Describes the cache used to store ClickOnce applications on the local computer.
Accessing Local and Remote Data in ClickOnce Applications
Describes how to access local data files and remote data sources from a ClickOnce application.
How to: Include a Data File in a ClickOnce Application
Demonstrates how to mark a file so that it is available in the ClickOnce data directory.

See also
Application Settings Overview
Publishing ClickOnce Applications
Building ClickOnce Applications from the Command Line
Debugging ClickOnce Applications That Use System.Deployment.Application
Deploying COM Components with ClickOnce
How to: Publish a ClickOnce Application using the Publish Wizard
Accessibility improvements in Windows Forms
controls for .NET Core 3.0
9/1/2020 • 4 minutes to read • Edit Online

Windows Forms is continuing to improve how it works with accessibility technologies to better support Windows
Forms customers. These improvements include the following changes:
Changes in various areas of interaction with accessibility client applications, including Narrator.
Changes in the Accessible hierarchy (improving navigation through the UI Automation tree).
Changes in keyboard navigation.

IMPORTANT
Accessibility changes made in .NET Framework 4.7.1 through .NET Framework 4.8 are included in .NET Core 3.0 and above,
and are enabled by default. The .NET Framework supported compatibility switches that allowed applications to opt out of the
new accessibility behavior. On the other hand, .NET Core doesn't support these settings and doesn't allow applications to opt
out of accessibility behavior.

Starting with .NET Core 3.0, Windows Forms applications benefit from all the new accessibility features (introduced
in .NET Framework 4.7.1 - 4.8) without additional configuration.

ListBox Accessibility support


The following changes apply to the ListBox control:
Enabled UI Automation support for ListBox control.
Improved ListBox accessibility support by adding the ScrollItemPattern to ListBox items and by enhancing
the accessibility event raising and handling and Narrator navigation through the items (caps lock navigation
isn't correct and doesn't throw the navigation outside the control unintentionally).

CheckedListBox Accessibility support


The following changes apply to the CheckedListBox control:
Corrected CheckedListBox Bounds provided by accessibility properties for entries.
Improved overall ListBox and CheckedListBox accessibility: corrected property values and event model.

ComboBox Accessibility support


The following changes apply to the ComboBox control:
Updated the process of getting ComboBox items' accessibility objects to enable generating IDs for items instead
of getting hash codes from items, which may be unsafe in case the GetHashCode function is overridden.

DataGridView Accessibility support


The following changes apply to the DataGridView control:
Corrected DataGridView.Bounds provided by accessibility properties for columns, rows, cells and corresponding
headers, improved performance of bounding rectangle calculation. All accessibility bounds are represented
correctly taking into account the bounds of entire control, along with its viewport.
Corrected Value.IsReadOnly property value providing for accessible client applications. The property now
shows correct IsReadOnly status for cells.
Fixed the issue with CellParsing event raising for the first cell change. Cell value can be changed without any
issues including the first DataGridView control interaction.
Improved DataGridView background color contrast when using Windows High Contrast themes. Changed
DataGridView default back color when using HC#1, HC#2, and HC Black themes.

PropertyGrid Accessibility support


The following changes apply to the PropertyGrid control:
Corrected PropertyGrid.Bounds provided by accessibility properties for grid entries, improved performance of
bounding rectangle calculation. For now all accessibility bounds are represented correctly taking into account
the bounds of entire control, along with its viewport.
Corrected accessible names and descriptions of subcontrols to not include control type names and to avoid
double announcement for control type names.

ToolStrip Accessibility support


The following changes apply to the ToolStrip control:
Improved navigation through ToolStrip , MenuStrip , and StatusStrip items. Corrected ToolStrip and
MenuStrip shift-tab navigation, back-looping the menu items when shift-tab up-arrow is pressed, which
navigates to the bottom menu element.
Improved MenuStrip accessible navigation, corrected menu accessible control types for submenus to make
submenus of type 'Menu' instead of 'MenuItem'.

PrintPreviewControl and PrintPreviewDialog Accessibility support


The following changes apply to the print controls:
Improved accessible navigation (including Narrator navigation) through menu items.
Improved High Contrast themes support and made the control element more contrasted.

StringCollectionEditor Accessibility support


Windows Forms designer now uses the string collection editor with improved accessibility support.

MonthCalendar Accessibility support (available in .NET Core 3.1)


The following changes apply to the MonthCalendar control:
Added UI Automation server providers to MonthCalendar control, added UI Automation Grid pattern and Table
pattern providers.
Changed table accessible control type to calendar accessible control type for MonthCalendar except the case
when the control has a preceding label control that defines MonthCalendar control accessible name, in this
specific case accessible control type becomes table.
Improved announcement of selected date for MonthCalendar control.
Improved MonthCalendar control support for screen readers and other accessibility tools. At this moment, users
can navigate the control elements and interact with these elements using keyboard-only input. With Narrator,
use CAPS + arrow keys to navigation through the control elements and CAPS + Enter to invoke element default
action.
Improved arrow key navigation across MonthCalendar child elements with a focusing rectangle: blue focus
rectangle for Narrator.
Improved accessibility for hit test action for MonthCalendar control elements to allow getting MonthCalendar
child accessible element by provided coordinates.

ToolTips accessibility (available in .NET Core 3.1)


Added ability to announce a tooltip text by screen reader applications such as NVDA and Narrator. Screen reader
application can now announce the text of keyboard or mouse tooltip of any Windows Forms control that
configured to show tooltips.

UI automation support for DataGridView, PropertyGrid, ListBox,


ComboBox, ToolStrip, and other controls
UI Automation support is enabled for controls at runtime but isn't used during design time. For an overview of UI
automation, see the UI Automation Overview.

See also
Accessibility Best Practices.
How to: Access Keyed Collections in Windows Forms
9/1/2020 • 2 minutes to read • Edit Online

You can access individual collection items by key. This functionality has been added to many collection
classes that are typically used by Windows Forms applications. The following list shows some of the
collection classes that have accessible keyed collections:
ListView.ListViewItemCollection
ListViewItem.ListViewSubItemCollection
Control.ControlCollection
TabControl.TabPageCollection
TreeNodeCollection
The key associated with an item in a collection is typically the name of the item. The following procedures show
you how to use collection classes to perform common tasks.
To find and give focus to a nested control in a control collection
Use the Find and Focus methods to specify the name of the control to find and give focus to.

OrderForm OrderForm1 = new OrderForm();


OrderForm1.Show();
OrderForm1.Controls.Find("textBox1", true)[0].Focus();

Dim OrderForm1 As New OrderForm()


OrderForm1.Show()
OrderForm1.Controls.Find("textBox1", True)(0).Focus()

To access an image in an image collection


Use the Item[] property to specify the name of the image you want to access.

this.BackgroundImage = imageList1.Images["logo.gif"];

Me.BackgroundImage = imageList1.Images("logo.gif")

To set a tab page as the selected tab


Use the SelectedTab property together with the Item[] property to specify the name of the tab page to set as
the selected tab.

tabControl1.SelectedTab = tabControl1.TabPages["shippingOptions"];

tabControl1.SelectedTab = tabControl1.TabPages("shippingOptions")

See also
Getting Started with Windows Forms
How to: Add or Remove Images with the Windows Forms ImageList Component
Enhancing Windows Forms Applications
9/1/2020 • 2 minutes to read • Edit Online

Windows Forms contains many features that you can use to enhance your Windows-based applications to meet
the specific needs of your users. The following topics describe these features and how to use them.

In This Section
Graphics and Drawing in Windows Forms
Contains links to topics that describe and show how to use the graphics interface in Windows Forms.
Application Settings for Windows Forms.
Contains links to topics that describe and show how to use the Application Settings feature.
Windows Forms Print Support
Contains links to topics that describe and show how to print files from Windows Forms applications.
Drag-and-Drop Operations and Clipboard Support
Contains links to topics that describe and show how to use the drag-and-drop feature and the Clipboard in
Windows Forms.
Networking in Windows Forms Applications
Contains links to topics that describe and show how to use networking in Windows Forms.
Globalizing Windows Forms applications
Contains links to topics that show how to globalize Windows Forms applications.
Windows Forms and Unmanaged Applications
Contains links to topics that describe and show how to access COM components from Windows Form
applications.
System Information and Windows Forms
Describes how to use system information in Windows Forms.
Power Management in Windows Forms
Describes how to manage power use in Windows Forms applications.
Windows Forms Visual Inheritance
Describes how to inherit from a base form.
Multiple-Document Interface (MDI) Applications
Describes how to create multiple-document interface (MDI) applications.
Integrating User Help in Windows Forms
Describes how to integrate user help in your applications.
Windows Forms Accessibility
Describes how to make your applications available to a wide variety of users.
Using WPF Controls
Describes how to use WPF controls in your Windows Forms-based applications.

Related Sections
Help Systems in Windows Forms Applications
Contains links to topics that describe and show how to provide user help in Windows Forms applications.
Getting Started with Windows Forms
Contains links to topics that describe how to use the basic features of Windows Forms.

You might also like