Control Internals

function OpenWin(url, w, h) { if(!w) w = 400; if(!h) h = 300; window.open(url, "_new", "width=" + w + ",height=" + h + ",menubar=no,toobar=no,scrollbars=yes", true); }

The .NET SDK ships with the tool ILDASM (IL Disassembler). ILis the Intermediate Language to which all .NET languages are compiled. By now, most of you are probably familiar with this tool. ILDASM displays the contents of a managed assembly, including the manifest, value types, interfaces, classes, enums, and delegates. In addition, ILDASM displays the IL code for all class methods and properties. This IL code is fairly readable and can be translated into C# with little effort. Producing the exact original C# code should not be expected because IL is not a one-to-one mapping tool as far as code is concerned.

Examples are often the best way to learn a new task, and with ILDASM you essentially have a huge set of examples just waiting to be explored. Every control, designer, and UITypeEditor for the controls shipped with .NET is located within one of three assemblies: System.Windows.Forms.dll, System.Design.dll, or System.Drawing.Design.dll. These three assemblies contain all the code you could ever need to use as examples. However, there is one drawback: the only way to view the code is with ILDASM and the code shows as IL. This does not represent an insurmountable obstacle because IL is not very hard to understand even if you don't know how to program in it. You can figure out most of what you need to know by looking at the IL code itself.

Hand Translation

The easiest path for translating IL code to C# is to do it by hand. Given the readability of IL, such a task is not difficult. Consider the UITypeEditor ImageEditor found in the System.Drawing.Design namespace. The Image object uses this editor during design-time as the UITypeEditor. The ImageEditor class displays an Open File dialog and draws the selected image within the property grid, as shown in Figure 6.2.

Figure 6.2. ImageEditor at work.

figure 6.2. imageeditor at work.

UITypeEditors can provide the implementation of the virtual PaintValue method. The ImageEditor overrides this method to draw the selected image within the property grid cell, as shown in Figure 6.2. Remember the UITypeEditor for the IconButton control? The PaintValue method is a basic C# translation of the IL code for the ImageEditor's PaintValue method. Listing 6.1 shows the IL code from the ImageEditor.PaintValue method. This IL code was copied from the code window of ILDASM.

Listing 6.1 IL Code for ImageEditor.PaintValue
  1: .method public hidebysig virtual instance void  2:         PaintValue(class [System.Drawing] System.Drawing.Design.PaintValueEventArgs  graphics/ccc.gife) cil managed  3: {  4:   // Code size       98 (0x62)  5:   .maxstack  4  6:   .locals (class [System.Drawing]System.Drawing.Image V_0,  7:            valuetype [System.Drawing]System.Drawing.Rectangle V_1)  8:   IL_0000:  ldarg.1  9:   IL_0001:  callvirt   instance object  graphics/ccc.gif[System.Drawing]System.Drawing.Design.PaintValueEventArgs::get_Value() 10:   IL_0006:  isinst     [System.Drawing]System.Drawing.Image 11:   IL_000b: brfalse.s  IL_0061 12:   IL_000d:  ldarg.1 13:   IL_000e:  callvirt   instance object  graphics/ccc.gif[System.Drawing]System.Drawing.Design.PaintValueEventArgs::get_Value() 14:   IL_0013:  castclass  [System.Drawing]System.Drawing.Image 15:   IL_0018:  stloc.0 16:   IL_0019:  ldarg.1 17:   IL_001a:  callvirt   instance valuetype [System.Drawing]System.Drawing.Rectangle  graphics/ccc.gif[System.Drawing]System.Drawing.Design.PaintValueEventArgs::get_Bounds() 18:   IL_001f:  stloc.1 19:   IL_0020:  ldloca.s   V_1 20:   IL_0022:  dup 21:   IL_0023:  call       instance int32  graphics/ccc.gif[System.Drawing]System.Drawing.Rectangle::get_Width() 22:   IL_0028:  ldc.i4.1 23:   IL_0029:  sub 24:   IL_002a:  call       instance void  graphics/ccc.gif[System.Drawing]System.Drawing.Rectangle::set_Width(int32) 25:   IL_002f:  ldloca.s   V_1 26:   IL_0031:  dup 27:   IL_0032:  call       instance int32  graphics/ccc.gif[System.Drawing]System.Drawing.Rectangle::get_Height() 28:   IL_0037:  ldc.i4.1 29:   IL_0038:  sub 30:   IL_0039:  call       instance void  graphics/ccc.gif[System.Drawing]System.Drawing.Rectangle::set_Height(int32) 31:   IL_003e:  ldarg.1 32:   IL_003f:  callvirt   instance class [System.Drawing]System.Drawing.Graphics  graphics/ccc.gif[System.Drawing]System.Drawing.Design.PaintValueEventArgs:: get_Graphics() 33:   IL_0044:  call       class [System.Drawing]System.Drawing.Pen  graphics/ccc.gif[System.Drawing]System.Drawing.SystemPens::get_WindowFrame() 34:   IL_0049:  ldloc.1 35:   IL_004a:  callvirt   instance void [System.Drawing]System.Drawing.  graphics/ccc.gifGraphics::DrawRectangle(class [System.Drawing]System.Drawing.Pen, 36:                   valuetype [System.Drawing]System.Drawing.Rectangle) 37:   IL_004f:  ldarg.1 38:   IL_0050:  callvirt   instance class [System.Drawing]System.Drawing. Graphics  graphics/ccc.gif[System.Drawing]System.Drawing.Design.PaintValueEventArgs:: get_Graphics() 39:   IL_0055:  ldloc.0 40:   IL_0056:  ldarg.1 41:   IL_0057:  callvirt   instance valuetype [System.Drawing]System.Drawing. Rectangle  graphics/ccc.gif[System.Drawing]System.Drawing.Design.PaintValueEventArgs:: get_Bounds() 42:   IL_005c:  callvirt   instance void [System.Drawing]System.Drawing.  graphics/ccc.gifGraphics::DrawImage(class [System.Drawing]System.Drawing.Image, 43:                 valuetype [System.Drawing]System.Drawing.Rectangle) 44:   IL_0061:  ret 45: }  // end of method ImageEditor::PaintValue 

The 45 lines of code in Listing 6.1 translate into about 12 lines of C# code. Listing 6.2 shows the approximate translation from the original IL code to the C# equivalent.

Listing 6.2 C# Translation from the Original IL Method
  1: public override void PaintValue( System.Drawing.Design.PaintValueEventArgs e) {  2:     if( !(e.Value is Icon) )  3:         return;  4:  5:     Image img = ((Icon)e.Value).ToBitmap( );  6:     Rectangle rcBounds = e.Bounds;  7:     rcBounds.Inflate(-1,-1);  8:     Pen p = System.Drawing.SystemPens.WindowFrame;  9:     e.Graphics.DrawRectangle(p, rcBounds ); 10:     if( img != null ) 11:     e.Graphics.DrawImage( img, e.Bounds ); 12: } 

I translated the IL code to C# in just a few short minutes. Again, it is important to note that the translation is not exact, but rather an approximation of the original C# code. The main reason for showing the IL is so that you can compare the IL statements to the C# code.

IL is somewhat verbose when it comes to the code listing. This is due to the low-level nature of IL, and it serves as a good example of the level of abstraction provided by languages such as C#. When you need to know how a control, editor, type converter, or designer is functioning, make use of ILDASM to view its implementation.

Control Relationships

Every Windows Form control derives from the Control base class. This includes the Form class. Because controls are capable of hosting or parenting other controls, it's possible to have a Form as the child of a Panel control. Strange as this may sound, Figure 6.3 shows a child Form being parented by a Panel control.

Figure 6.3. A form within a panel.

figure 6.3. a form within a panel.

Not only is the Form in Figure 6.3 a child of the Panel, but also the Panel, true to form, will clip child controls. This means that dragging around the Form will cause it to be clipped to the bounding area of the Panel. Figure 6.4 shows the child Form being clipped by the Panel.

Figure 6.4. An example of clipping.

figure 6.4. an example of clipping.

Even stranger than hosting a Form within a Panel is the capability to host a Form within a Button. Although there is no practical reason to host a form in a button, it's important that you understand the relationship of controls and windows and how that relationship affects behavior.

This begs the question of why you would want to use this design. Why allow a Form, which should be a TopLevel component, be the child of a Panel or Button? Everything is a window, regardless of what shape or functionality it takes on. Because the Control class offers the basic framework, everything is now considered a control.

The Control base class implements all necessary ActiveX control interfaces, basic drag-and-drop, and the all important IWin32Window interface. The IWin32Window interface provides access to the underlying window handle. This window handle can be used to call Win32 functions that require a handle to the current window.

Most of the controls and classes provided with Windows Forms are available for your own use when developing controls or applications. However, several classes are private to the Windows.Forms and Windows.Forms.Design namespace. One of the more interesting classes is the UnsafeNativeMethods class. This is, of course, just one of many private classes found within the .NET base class library. Even though the classes are private and cannot be used to create new controls, nothing prevents your peeking at their implementation with ILDASM to see what functionality they provide and how they are used.

Designer Internals

Designers represent one of the most critical aspects of custom control development; however, the documentation surrounding their implementation is somewhat lacking, to say the least. Without a proper designer, a newly developed custom control will not respond to the design-time actions of a developer or the development environment. Even though the documentation for designers is rather light, the openness of .NET and the capability to peek inside the internals of other designers provides some insight as to the design and development of control designers.

There Can Be Only One Designer

One of the most important things to note about a designer is the fact that there is only one instance of a designer. Let's use the IconButton as an example. If there are three IconButtons on the form, there exists only a single IconButtonDesigner. Figure 6.5 shows this basic relationship between a control and its designer.

Figure 6.5. The designer relationship.

figure 6.5. the designer relationship.

Just before the designer is to be utilized, the context information for the designer is updated. When a different IconButton is selected, the designer's Control property is set to the currently selected control.

IComponentChangeService

Whenever an action such as a component being added or removed takes place, all active designers that have subscribed to the ComponentChangeService are notified. Remember that a designer subscribes to the ComponentChangeService by overriding the Initialize method. During initialization, the designer can choose to subscribe to the events listed in Table 6.1.

Table 6.1. IComponentChangeService Events
Event Description
ComponentAdded A component has been added.
ComponentAdding A component is about to be added.
ComponentChanged A component has been changed.
ComponentChanging A component is in the process of being changed.
ComponentRemoved A component has been removed.
ComponentRemoving A component is about to be removed.
ComponentRename A component has been renamed.

Because there is only a single designer for the IconButton, each time an IconButton is added, the IconButtonDesigner's Initialize method is invoked. This means that the designer will receive one notification for each control it is responsible for designing when a subscribed change event occurs. If there are three IconButtons on the form, then three notifications will be sent to the designer. It is important to note that anytime a control or component changes, all designers are notified of the change. This is true even if the designer does not manage the control being changed.

The delegate parameters for a change event specify a parameter: ComponentChangeEventArgs. The ComponentChangeEventArgs provides a property, Component, that specifies the component associated with the event. This allows for the receiver of the event, in this case the designer, to determine the context of the event and to determine whether the change event should be responded to. In Chapter 8, "OutlookBar Control," the designer uses the IComponentChangeService to track which controls it is parenting within the individual tabs.



    .NET Windows Forms Custom Controls
    User Interfaces in VB .NET: Windows Forms and Custom Controls
    ISBN: 1590590449
    EAN: 2147483647
    Year: 2002
    Pages: 74

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net