Your Position: Home > Home Garden > .NET Windows Forms in a Nutshell [Book]
Guest Posts

.NET Windows Forms in a Nutshell [Book]

Author:

Helen

Mar. 07, 2024
  • 260
  • 0

Any type will need to define some members to be of any use. Members are either associated with data or behavior. In C++ this means fields and methods, respectively. In addition to these, which the CLR supports, the CLR adds some new member types. All these member types are described here.

The public keyword in both languages indicates that any code is allowed to call this method. We will talk more about such protection keywords towards the end of this section.

The method takes a string as a single parameter and returns an integer. The code for the method attempts to convert the string to an integer by using the C# int type’s or VB Integer type’s static Parse method. (Both int and Integer are identical to the .NET Framework’s System.Int32 type.) MyMethod is an instance method—users of MyFirstClass will need an instance of MyFirstClass to call this method.

Methods can either be instance methods or static methods. (Instance methods are the default, but you can use the static keyword in C# or the Shared keyword in Visual Basic to specify a static method.) Instance methods are invoked with respect to a particular object or value, and they have access to that object through the this keyword in C# and the Me operator in VB. They can also refer to members simply by their names—if they are instance members, the this or Me reference will be used implicitly. Static methods do not need an object in order to be invoked, but they will only have access to other static members of the class. Visual Basic is not trying to maintain any look-and-feel compatibility with C++, so it uses the rather more sensible Shared keyword for members that are shared across all instances of a class.

Methods are where we define code. In most .NET languages, all code must be defined in a method of some type. (Because properties also can contain code, they would appear to be an exception, but they are actually implemented by .NET language compilers as method calls.) As with C++, the method must have a signature (consisting of its name and the types of parameters it takes), and that signature must be different from any other methods defined in the same class. Overloading is allowed, i.e., the names of two methods can be the same if their signatures are different. Methods must also have a return type (even if the method returns void or Nothing ); overloading based on return type alone is not allowed. (C++ doesn’t allow this either.) Note that methods that return void or Nothing in VB are declared using the Sub statement rather than the Function statement.

This class defines a private instance field called x , which can store an integer. The method IncrementTotal adjusts this field and returns its value. The code does not use the this or Me reference; it just refers to x by name. The compiler will detect that the code refers to the instance field x and presume that the author meant this.x or Me.x .

Fields hold data. As with methods, fields can be either instance or static. If a field is declared as static, it is singular—all instances of the class or value will share the same piece of data, and that data will be accessible to instance and static methods alike. But instance fields (the default) are stored as part of each instance of the type, so every instance has its own set.

In this particular example, there is no field. (Feel free to implement your own properties using private fields internally.) This will just run the get and set methods defined for the property. In this case, reading the property will always get the value 42, and writing it will just cause a message to be printed. Most properties behave more usefully, of course, but the point is the client code is not dependent on how the property works—it could rely on a normal field, derive the value from those in other fields, or retrieve the value from a database. The client just accesses the property, and the object can handle that however it sees fit.

The use of {0} in the string passed to Console.WriteLine indicates that the parameter following the string should be inserted into the output at this point. It has a similar role to placeholders such as %d in the format string for printf in C.

This defines an int or Integer property called MyProp . Note that value is a keyword in C# and VB, and it is used in property set functions. It is the value that the caller is trying to give the property. (In this case, we are just writing that value to the console.)

Just as COM did, .NET specifies a standard way of exposing properties through methods. And as with COM, some languages (including Visual Basic .NET and C#) provide special syntax to support this, allowing field-like syntax to be used when reading or writing properties, even though they are implemented in terms of methods. So in C#, we can provide properties like this:

It is considered good practice never to expose a data field as a public member of an object, because that would cause client code to become too tightly coupled with that type’s implementation. Exposing properties through get and set methods is a popular technique for allowing components’ implementations the flexibility to evolve while still providing public members that feel like fields.

Event handling

Components often need to notify client code when something interesting has happened. This is particularly common in user interface code—applications need to know when buttons are clicked, when windows are resized, when text is typed in, and so on. .NET defines a standard way in which objects can deliver event notifications to their clients. Visual Basic and C# both have special syntax for declaring and consuming such events. These two syntaxes are quite different—C# presents the CLR’s event handling mechanisms directly, while VB uses a style that is much more like the event handling in previous versions of VB. However, both languages are based on the same fundamental mechanisms, so they have much in common.

A class that wishes to be able to raise events (most Windows Forms controls do this) must declare the fact by adding a special member for each type of event it can raise. In C#, we use the following syntax:

public class EventSource

{

    

public event MouseEventHandler MouseDown;

. . . }

In Visual Basic, the equivalent event declaration looks like this:

Public Class EventSource

    

Public Event MouseDown As MouseEventHandler

. . . End Class

Both examples declare an event whose name is MouseDown and whose type is MouseEventHandler. (The MouseEventHandler type is defined in the System.Windows.Forms namespace, and we will see its definition later.) As it happens, all Windows Forms controls support this event—it is raised whenever a mouse button is pressed while the cursor is over the control.

When an event occurs, the event source notifies the client by calling the relevant handler function. The way we determine which particular function it calls is different in VB and C#. In VB, the class that wishes to receive the event simply uses the WithEvents keyword to indicate that it is interested in events from the event source object. It then identifies a particular method as being the handler for a given event using the Handles keyword. The signature of the handler method must match the type of the event. In this case, the event is of type MouseEventHandler. (We will look at this type’s definition shortly.) So our code looks like this:

Public Class EventReceiver

    

Private WithEvents src As EventSource

. . . Private Sub src_OnMouseDown( _ sender As Object, e As MouseEventArgs) _

Handles src.MouseDown

Console.WriteLine("src object raised MouseDown event") End Class

This style is similar to how previous versions of Visual Basic handled events. However, it hides the details of how events really work. C# does not provide such a level of abstraction—it exposes the CLR’s underlying mechanisms directly. Consequently, we need to do slightly more work in C# to handle events. Moreover, we must understand the mechanism on which events are based.

The CLR provides a special kind of object that is used to connect an event source to its corresponding event handler method. These special objects are called delegates. Delegates are .NET’s nearest equivalent to function pointers—they hold typed references to functions. As with a C++ function pointer, a delegate’s type (MouseEventHandler, in this case) determines the signature that the client’s handler function must have. MouseEventHandler is defined (in System.Windows.Forms) thus:

public delegate void MouseEventHandler(

    object sender, MouseEventArgs e);

The equivalent Visual Basic definition is:

Public Delegate Sub MouseEventHandler( _

    sender As Object, e As MouseEventArgs

So if we wish to receive MouseDown event notifications from some control, we must provide a function with a matching signature:

private void OnMouseDown (object sender, MouseEventArgs e)

{

    ... handle the MouseDown event ...

}

Of course, we must also tell the control that we are interested in the MouseDown event and would like notifications to be delivered to our OnMouseDown method. In Visual Basic, we did this by using the Handles keyword, but in C#, we must create a MouseEventHandler delegate initialized with a reference to our method, and then attach it to the relevant event on the control, using the following rather strange syntax:

src.MouseDown += new MouseEventHandler(OnMouseDown);

This is roughly equivalent to passing the address of a callback function as a function pointer in C++; the delegate acts as a typed reference to a function that can be passed as a parameter or stored in a field so that the function can be called back later. But we can’t use function pointers as we would in C++, and not just for the ideological reason that it doesn’t enter into the spirit of the brave new pointerless world of the CLR. There is a rather more prosaic reason not to use raw function pointers: JIT compilation means that functions don’t necessarily remain in the same place for the life of a program. In fact, when the code above is run, there is every chance that the OnMouseDown method has not been JIT compiled at all yet, so it might not even have an address. So instead, we rely on delegates to provide us with behavior equivalent to function pointers, while shielding us from the complexities of using pointers in the world of movable code.

Delegates can hold an object reference as well as a function reference. (In C++ terms, this would mean that a delegate is really two pointers—a function pointer and a pointer to an object.) In the example above, OnMouseDown is not a static function, so it can only be invoked in conjunction with an object reference. (The value for the implicit this reference has to come from somewhere.) If a function requires an object reference, a suitable one must be supplied when a delegate to that function is created. This can be done explicitly, for example:

myDelegate = new MyDelegateType(myObj.MyMethod);

creates a new delegate whose type is MyDelegateType and attaches it to the MyMethod method on the object to which myObj refers. (Delegates store their own copy of the reference, so if the myObj variable is later modified to refer to a different object, the delegate will still refer to the original one.) Or the object reference can be inferred—if the delegate is created in the scope of a non-static method, the this reference will be used if no explicit reference is supplied. The MouseEventHandler example above illustrates this, and is typical of code inside a form’s initialization function: because an object reference has not been supplied explicitly, the C# compiler automatically supplies a reference to whichever form is being initialized. That code is shorthand for the following:

src.MouseDown += new MouseEventHandler(this.OnMouseDown);

This use of the += syntax, peculiar to C#, is simply shorthand for a method call. For each event that a class defines, the C# compiler will actually define two methods, one for adding a handler and one for removing it. C# hides this detail with the += syntax (and the corresponding -= syntax used for disconnecting an event handler), and it also shields us from the details of declaring events if we wish to raise them ourselves. If we add an event declaration such as the one shown above to our own class, the C# compiler will automatically generate the functions to add and remove event handlers for us; the code it generates is able to cope with multiple event handlers being attached simultaneously, as all events should.

Note that the -= syntax used for detaching an event handler is smart enough to work out which method a delegate refers to. It doesn’t require the same delegate object that was used in the += to be passed back in. So looking at the code above, you might have thought that we would need to store the delegate being created with the new operator to pass it back when we wish to detach. In fact, it works just fine if we create a new delegate at detachment time:

src.MouseDown -= new MouseEventHandler(this.OnMouseDown);

In Visual Basic, all these details of creating delegates and attaching them are hidden—using the WithEvents and Handles keywords causes all this code to be generated automatically. However, VB also supports the explicit style that C# requires. The syntax is different, but the meaning is the same. We can create a delegate object using VB’s AddressOf keyword. And VB’s equivalents to the += and -= event operators are the AddHandler and RemoveHandler keywords. So we can add a handler explicitly, just as we are required to in C#, with the following VB code:

AddHandler src.MouseDown, AddressOf Me.OnMouseDown

And the corresponding code to remove a handler is:

RemoveHandler src.MouseDown, AddressOf Me.OnMouseDown

Most of the time, you would not need to use this explicit style in Visual Basic. However, it can be useful for attaching handlers dynamically at runtime. In addition, if you want a single event handler to handle an event from every object in a collection, you will need to use this explicit style.

All the event handler delegates defined in the .NET Framework follow a common pattern. They define function signatures that take two parameters. The first parameter is always of type object, and is a reference to the object that raised the event. (So when a control raises the MouseDown event, it passes a reference to itself to the event handler. This can be useful it you want to have events from multiple controls on a form all handled by a single function—this parameter lets it know which control a particular event came from.) The second parameter contains information about the event. The various delegates defined in .NET all specify different types for this second parameter. For example, the drag-and-drop events use a delegate type called DragEventHandler, which defines the second parameter to be a DragEventArgs, while MouseEventHandler (see above) defines it to be a MouseEventArgs. Some events provide no special information—for example, the Click event raised by a button simply indicates that a particular button has been clicked, so there is no use for a final parameter. .NET defines a generic delegate for such methods:

public delegate void EventHandler(object sender, EventArgs e);

The second parameter is usually a special value, EventArgs.Empty. This may seem pointless—if the same value is passed every time, why not just leave off the second parameter? It is left there just in case peculiar circumstances arise in which it would be useful to be able to pass some information. For example, if you were to define a custom derivative of the standard Button class, you might wish to pass some information in your Click event. If you define a class that derives from EventArgs, you can pass it as the second parameter. If EventHandler didn’t provide this second argument, you would not be able to do this.

Note that you are not required to use this style of event handling for your own components. You can define classes whose events use a delegate of your own devising, which may have any signature you like. Of course, if you stick to the framework’s style, your code will look more consistent, so it is recommended that you do this. But there is nothing magic about delegates whose first parameter is an object and whose second parameter is some type deriving from EventArgs.

What is the difference between nettype and “wire logic”. Can you please explain with example?

I Know the below details.

For “wire logic” there is an inbuilt resolution function and for “nettype”we can have our own resolution function, we can drive whatever the data that we want to drive (4-state data, 2-state data,real value data),i.e., we can define the type of driving data in case of “nettype”.

Can you please explain with some examples on advantages of nettype over “wire logic”?

.NET Windows Forms in a Nutshell [Book]

Difference between nettype and "wire logic"

Comments

0/2000

Get in Touch