1. Trang chủ
  2. » Công Nghệ Thông Tin

Professional C# Third Edition phần 8 potx

140 864 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 140
Dung lượng 3,62 MB

Nội dung

We use the TagPrefix option in the same way as before, but we don’t use the TagName or Src attributes. This is because the custom control assembly we use may contain several custom controls, and each of these will be named by its class, so TagName is redundant. In addition, since we can use the dynamic dis- covery capabilities of.NET Framework to find our assembly we simply have to name it and the names- pace in it that contains our controls. In the previous line of code, we are instruct the program to use an assembly called PCSCustomWeb Controls.dll with controls in the PCSCustomWebControls namespace, and use the tag prefix PCS. If we have a control called Control1 in this namespace we could use it with the ASP.NET code: <PCS:Control1 Runat=”server” ID=”MyControl1”/> With custom controls it is also possible to reproduce some of the control nesting behavior that exists in list controls: <asp:DropDownList ID=”roomList” Runat=”server” Width=”160px”> <asp:ListItem Value=”1”>The Happy Room</asp:ListItem> <asp:ListItem Value=”2”>The Angry Room</asp:ListItem> <asp:ListItem Value=”3”>The Depressing Room</asp:ListItem> <asp:ListItem Value=”4”>The Funked Out Room</asp:ListItem> </asp:DropDownList> We can create controls that should be interpreted as being children of other controls in a very similar way to this. We’ll discuss how to do this later in this chapter. Custom Control Project Configuration Let’s start putting some of this theory into practice. We’ll use a single assembly to hold all of the example custom controls in this chapter for simplicity, which we can create in Visual Studio .NET by choosing a new project of type Web Control Library. We’ll call our library PCSCustomWebControls, as shown in Figure 27-5. Figure 27-5 940 Chapter 27 33 557599 Ch27.qxd 4/29/04 11:43 AM Page 940 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Here I have created the project in C:\ProCSharp\CustomControls. There is no need for the project to be created on the Web server as with Web applications, since it doesn’t need to be externally accessible in the same way. Of course, we can create Web control libraries anywhere, as long as we remember to copy the generated assembly somewhere where the Web application that uses it can find it! One technique we can use to facilitate testing is to add a Web application project to the same solution. We’ll call this application PCSCustomWebControlsTestApp. For now, this is the only application that will use our custom control library, so to speed things up a little we can make the output assembly for our library be created in the correct bin directory (this means that we don’t have to copy the file across every time we recompile). We can do this through the property pages for the PCSCustomWebControls project (see Figure 27-6). Figure 27-6 Note that we have changed the Configuration dropdown to All Configurations, debug and release build outputs will be placed in the same place. The Output Path has been changed to C:\Inetpub\wwwroot\ PCSCustomWebControlsTestApp\bin. To make debugging easier we can also change the Debug Mode option on the Debugging property page to URL and the Start URL to, http://localhost/ PCSCustomWebControlsTestApp/WebForm1.aspx so we can just execute our project in debug mode to see our results. We can make sure that this is all working by testing the control that is supplied by default in the .cs file of our custom control library, WebCustomControl1.cs. We just need to make the following changes to the code in WebForm1.aspx, which simply references the newly created control library and embeds the default control in this library into the page body: 941 User Controls and Custom Controls 33 557599 Ch27.qxd 4/29/04 11:43 AM Page 941 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com <%@ Page language=”c#” Codebehind=”WebForm1.aspx.cs” AutoEventWireup=”false” Inherits=”PCSCustomWebControlsTestApp.WebForm1” %> <%@ Register TagPrefix=”PCS” Namespace=”PCSCustomWebControls” Assembly=”PCSCustomWebControls”%> <!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.0 Transitional//EN” > <HTML> <HEAD> <title>WebForm1</title> <meta name=”GENERATOR”content=”Microsoft Visual Studio 7.0” > <meta name=”CODE_LANGUAGE”content=”C#” > <meta name=”vs_defaultClientScript”content=”JavaScript” > <meta name=”vs_targetSchema” content=”http://schemas.microsoft.com/intellisense/ie5” > </HEAD> <body MS_POSITIONING=”GridLayout”> <form id=”Form1” method=”post” runat=”server”> <PCS:WebCustomControl1 ID=”testControl” Runat=”server” Text=”Testing again ”/> </form> </body> </html> In fact, there is an even better way of doing this after the control library project has been compiled. The Visual Studio .NET Toolbox has a tab called My User Controls that you can use to add your own controls (or you can add your own tab). Right-click the tab to which you want to add your new control and choose the Add/Remove Items menu option. Next, from the .NET Framework Components tab browse to the PCSCustomWebControls.dll assembly, load it, and then choose controls from the list, as shown in Figure 27-7. Figure 27-7 942 Chapter 27 33 557599 Ch27.qxd 4/29/04 11:43 AM Page 942 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Select WebCustomControl1 as shown in Figure 27-7 to display the new control in the Toolbox, ready for adding to our form (see Figure 27-8). Figure 27-8 The nice thing about this is that if we add the control from the Toolbox both a project reference to PCSCustomWebControls and the <%@ Register %> directive are added automatically to the control. The tag prefix for the control library is also assigned automatically (in our example: cc1). This is fine, although doing this ourselves gives us greater flexibility that could improve code readability. In the rest of the code in this chapter we’ll assume that the prefix PCS is used for controls from PCSCustomWebControls. Note that the prefix used for Toolbox items can be controlled in the code for the control, by adding the following attribute to the code for the control: [assembly: TagPrefix(“WebCustomControl1”, “PCS”)] One thing that isn’t added for us is a using statement to our PCSCustomWebControlsTestApp names- pace in WebForm1.aspx.cs. Rather than use fully qualified names for our controls we can add the using statement ourselves: using PCSCustomWebControls; This will enable us to use our custom controls from the code behind the form without full name qualification. Now, as long as we have the PCSCustomWebControls library configured as our startup application we can click the Debug button to see our results. Try changing the Text property of the newly added con- trol to Testing again and run the application. The result is shown in Figure 27-9. Figure 27-9 943 User Controls and Custom Controls 33 557599 Ch27.qxd 4/29/04 11:43 AM Page 943 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Basic Custom Controls As can be inferred from the results in the previous section, the sample control generated by default is simply a version of the standard <asp:Label> control. The code generated in the .cs file for the project, WebCustomControl1.cs, is as follows (omitting the standard and XML documentation comments): using System; using System.Web.UI; using System.Web.UI.WebControls; using System.ComponentModel; namespace PCSCustomWebControls { [DefaultProperty(“Text”), ToolboxData(“<{0}:WebCustomControl1 runat=server></{0}:WebCustomControl1>”)] public class WebCustomControl1 : System.Web.UI.WebControls.WebControl { private string text; [Bindable(true), Category(“Appearance”), DefaultValue(“”)] public string Text { get { return text; } set { text = value; } } protected override void Render(HtmlTextWriter output) { output.Write(Text); } } } The single class defined here is the WebCustomControl1 class (note how the class name mapped directly to an ASP.NET element in the simple example we saw before), which is derived from the WebControl class as discussed earlier. Two attributes are provided for this class: DefaultProperty and ToolboxData. The DefaultProperty attribute specifies what the default property for the control will be if used in languages that support this functionality. The ToolboxData attribute specifies exactly what HTML will be added to an .aspx page if this control is added using the Toolbox (as we discussed earlier, once the project is compiled we can add the control to the toolbox by configuring the toolbox to use the assembly created). Note that a {0} placeholder is used to specify where the tag prefix will be placed. The class contains one property: Text. This is a very simple text property much like those we’ve seen before. The only point to note here is the three attributes: 944 Chapter 27 33 557599 Ch27.qxd 4/29/04 11:43 AM Page 944 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com ❑ Bindable, which specifies whether the property can be bound to data. ❑ Category, which specifies where the property will be displayed in the property pages. ❑ DefaultValue, which specifies the default value for the property. Exposing properties in this way works in exactly the same way as it did for custom controls, and is defi- nitely preferable to exposing public fields. The remainder of the class consists of the Render() method. This is the single most important method to implement when designing custom controls, as it is where we have access to the output stream to dis- play our control content. There are only two cases where we don’t need to implement this method: ❑ When we are designing a control that has no visual representation (usually known as a component). ❑ When we are deriving from an existing control and don’t need to change its display characteristics. Custom controls can also expose custom methods, raise custom events, and respond to child controls (if any). In the remainder of this chapter, where we discuss how to: ❑ Create a derived control. ❑ Create a composite control. ❑ Create a more advanced control. The final example is a straw poll control, capable of allowing the user to vote for one of several candi- dates, and displaying voting progress graphically. Options are defined using nested child controls, in the manner described earlier. We’ll start with a simple derived control. The RainbowLabel derived control For this first example we’ll derive a control from a Label control and override its Render() method to output multicolored text. To keep the code for the sample controls in this chapter separate, we’ll create new source files as necessary, so for this control add a new .cs code file called RainbowLabel.cs to the PCSCustomWebControls project and add the following code: using System; using System.Web.UI; using System.Web.UI.WebControls; using System.ComponentModel; using System.Drawing; namespace PCSCustomWebControls { public class RainbowLabel : System.Web.UI.WebControls.Label { 945 User Controls and Custom Controls 33 557599 Ch27.qxd 4/29/04 11:43 AM Page 945 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com private Color[] colors = new Color[] {Color.Red, Color.Orange, Color.Yellow, Color.GreenYellow, Color.Blue, Color.Indigo, Color.Violet}; protected override void Render(HtmlTextWriter output) { string text = Text; for (int pos=0; pos < text.Length; pos++) { int rgb = colors[pos % colors.Length].ToArgb() & 0xFFFFFF; output.Write(“<font color=’#” + rgb.ToString(“X6”) + “‘>” + text[pos] + “</font>”); } } } } This class derives from the existing Label control (System.Web.UI.WebControls.Label) and doesn’t require any additional properties, because the inherited Text one will do fine. We have added a new pri- vate field, colors[], which contains an array of colors that we’ll cycle through when we output text. The main functionality of the control is in Render(), which we have overridden, because we want to change the HTML output. Here we get the string to display from the Text property and display each character in a color from the colors[] array. To test this control we add it to the form in PCSCustomWebControlsTestApp: <form method=”post” runat=”server” ID=”Form1”> <PCS:RainbowLabel Runat=”server” Text=”Multicolored label!” ID=”rainbowLabel1”/> </form> Figure 27-10 Maintaining state in custom controls Each time a control is created on the server in response to a server request it is created from scratch. This means that any simple field of the control will be reinitialized. In order for controls to maintain state between requests they must use the ViewState maintained on the client, which means we need to write controls with this in mind. 946 Chapter 27 33 557599 Ch27.qxd 4/29/04 11:43 AM Page 946 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com To illustrate this, we’ll add an additional capability to the RainbowLabel control. We’ll add a method called Cycle() that cycles through the colors available, which will make use of a stored offset field to determine which color should be used for the first letter in the string displayed. This field must use the ViewState of the control in order to be persisted between requests. We’ll show the code for both with and without ViewState storage cases to demonstrate how easy it is to make an error that results in a non-persistent control First we’ll look at code that fails to make use of the ViewState: public class RainbowLabel : System.Web.UI.WebControls.Label { private Color[] colors = new Color[] {Color.Red, Color.Orange, Color.Yellow, Color.GreenYellow, Color.Blue, Color.Indigo, Color.Violet}; private int offset = 0; protected override void Render(HtmlTextWriter output) { string text = Text; for (int pos=0; pos < text.Length; pos++) { int rgb = colors[(pos + offset) % colors.Length].ToArgb() & 0xFFFFFF; output.Write(“<font color=’#” + rgb.ToString(“X6”) + “‘>” + text[pos] + “</font>”); } } public void Cycle() { offset = ++offset; } } Here we initialize the offset field to zero, then allow the Cycle() method to increment it, using the % operator to ensure that it wraps round to 0 if it reaches 7 (the number of colors in the colors array). To test this we need a way of calling Cycle(), and the simplest way to do that is to add a button to our form: <form method=”post” runat=”server” ID=”Form1”> <PCS:RainbowLabel Runat=”server” Text=”Multicolored label!” ID=”rainbowLabel1”/> <asp:Button Runat=”server” ID=”cycleButton” Text=”Cycle colors”/> </form> Add an event handler by double-clicking on the button in design view and add the following code (you’ll need to change the protection level to protected): 947 User Controls and Custom Controls 33 557599 Ch27.qxd 4/29/04 11:43 AM Page 947 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com protected void cycleButton_Click(object sender, System.EventArgs e) { this.rainbowLabel1.Cycle(); } If you run this code you’ll find that the colors change the first time you click the button, but further clicks will leave the colors as they are. If this control persisted itself on the server between requests then it would work adequately, because the offset field would maintain its state without us having to worry about it. However, this technique wouldn’t make sense for a Web application, with thousands of users potentially using it at the same time. Creating a separate instance for each user would be counterproductive. In any case, the solution is quite simple. We have to use the ViewState property bag of our control to store and retrieve data. We don’t have to worry about how this is serialized, recreated, or anything else. We just put things in and take things out, safe in the knowledge that state will be maintained between requests in the standard ASP.NET way. To place the offset field into ViewState we simply use: ViewState[“_offset”] = offset; ViewState consists of name-value pairs, and here we are using one called _offset. We don’t have to declare this anywhere; it will be created the first time this code is used. Similarly, to retrieve state we use: offset = (int)ViewState[“_offset”]; If we do this when nothing is stored in the ViewState under that name we will get a null value. Casting a null value in this code will throw an exception, so we can either test for this or check whether the object type retrieved from ViewState is null before we cast it, which is what we’ll do in our code. In fact, we can update our code in a very simple way by replacing the existing offset member with a pri- vate offset property that makes use of ViewState, with code as follows: public class RainbowLabel : System.Web.UI.WebControls.Label { private int offset { get { object rawOffset = ViewState[“_offset”]; if (rawOffset != null) { return (int)rawOffset; } else { 948 Chapter 27 33 557599 Ch27.qxd 4/29/04 11:43 AM Page 948 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com ViewState[“_offset”] = 0; return 0; } } set { ViewState[“_offset”] = value; } } } This time, the control allows the Cycle() method to work each time. In general, we might see ViewState being used for simple public properties, for example: public string Name { get { return (string)ViewState[“_name”]; } set { ViewState[“_name”] = value; } } One further point about using ViewState concerns child controls. If our control has children and is used more than once on a page, then we have the problem that the children will share their ViewState by default. In almost every case this isn’t the behavior we’d like to see, and luckily we have a simple solu- tion. By implementing INamingContainer on the parent control we force child controls to use qualified storage in ViewState, such that child controls will not share their ViewState with similar child controls with a different parent. Using this interface doesn’t require any property or method implementation, we just have to use it, as if it were simply a marker for interpretation by the ASP.NET server, as discussed in the following sections. Creating a Composite Custom Control As a simple example of a composite custom control, we can combine the control from the previous sec- tion with the cycle button we have in the test form. We’ll call this composite control RainbowLabel2, and place it in a new file, RainbowLabel2.cs. This con- trol needs to: ❑ Inherit from WebControl (not Label). ❑ Support INamingContainer. ❑ Possess two fields to hold its child controls. 949 User Controls and Custom Controls 33 557599 Ch27.qxd 4/29/04 11:43 AM Page 949 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com [...]... Split Unregistered Version - http://www.simpopdf.com // CCOMDemo [ coclass, threading(“apartment”), vi_progid(“COMServer.COMDemo”), progid(“COMServer.COMDemo.1”), version(1.0), uuid(“2 388 AAA8-AD72-4022-948D-555316F708E8”), helpstring(“COMDemo Class”) ] class ATL_NO_VTABLE CCOMDemo : public IWelcome { public: CCOMDemo() { } DECLARE_PROTECT_FINAL_CONSTRUCT() HRESULT FinalConstruct() { return S_OK; } void... 28- 2, with a dispatch interface two tables are needed The first one maps the method or property name to a dispatch id; the second one maps the dispatch-id to the implementation of the method or property 967 Chapter 28 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com QueryInterface "Add" 47 AddRef "Sub" 48 Release GetTypeInfoCount GetIDsOfNames 47 pAdd 48 pSub Invoke Figure 28- 2... seen in Figure 28- 3, a dual interface derives from IDispatch, but offers the additional methods of the interface directly in the vtable Scripting clients can use the IDispatch interface to invoke the methods, while clients aware of the vtable can call the methods directly QueryInterface "Add" 47 AddRef "Sub" 48 Release GetTypeInfoCount GetIDsOfNames 47 pAdd 48 pSub Invoke Add Sub Figure 28- 3 Casting and... identifier 0F21F359-AB84-41e8-9A 78- 36D110E6D2F9, and the name how it should appear within NET Add the custom attribute with the same identifier and the name Wrox.ProCSharp.COMInterop Server.IWelcome to the header section of the IWelcome interface Add the same attribute with a corresponding name to the class CCOMDemo // IWelcome [ object, uuid(“015ED275-3DE6-4716-A6FA-4EBC71E4A8EA”), dual, helpstring(“ICOMDemo... detailed error information with an exception in NET 971 Chapter 28 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Event Handling NET offers an event-handling mechanism with the C# keywords event and delegate (see Chapter 6) In Chapter 16 we discuss that the same mechanism is also available with NET Remoting Figure 28- 6 shows the COM event-handling architecture With COM events,... Figure 28- 7) The COM component will offer two interfaces, so that you can see how QueryInterface() is mapped from NET, and just three simple methods, so that you can see how the interaction takes place In the Class View, select the interface IWelcome, and add the method Greeting() (see Figure 28- 7), with these parameters: HRESULT Greeting([in] BSTR name, [out, retval] BSTR* message); 973 Chapter 28 Simpo... you should be able to start building (and experiment with) your own custom controls For more details on this subject, check out Professional ASP.NET 1.1 (ISBN: 0-7645- 589 0-0) 962 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Par t VI: Interop Chapter 28: COM Interoperability Chapter 29: Enterprise Services Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com... 969 Chapter 28 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Single-threaded apartment The single-threaded apartment (STA) was introduced with Windows NT 3.51 With an STA only one thread (the thread that created the instance) is allowed to access the component However, it is legal having multiple STAs inside one process (see Figure 28- 4) Process STA1 STA2 Figure 28- 4 With STAs... by using memory addresses As a conclusion, custom interfaces cannot be used by scripting clients Figure 28- 1 shows the vtable of the custom interface IMath that offers the methods Add() and Sub() in addition to the methods of the IUnknown interface QueryInterface AddRef Release Add Sub Figure 28- 1 Dispatch interfaces Because a scripting client (and earlier Visual Basic clients) doesn’t support custom... All COM objects do have a unique identifier that consists of a 1 28- bit number, and that is also known as class id (CLSID) Creating a COM object, the COM API call CoCreateInstance() just looks into the registry to find the CLSID and the path to the DLL or EXE to load the DLL or launch the EXE and instantiate the component Because such a 1 28- bit number cannot be easily remembered, many COM objects also . 27-7 to display the new control in the Toolbox, ready for adding to our form (see Figure 27 -8) . Figure 27 -8 The nice thing about this is that if we add the control from the Toolbox both a project. ViewState[“_offset”]; if (rawOffset != null) { return (int)rawOffset; } else { 9 48 Chapter 27 33 557599 Ch27.qxd 4/29/04 11:43 AM Page 9 48 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com ViewState[“_offset”]. Votes=”101”/> <PCS:Candidate Name=”Roger Moore” Votes= 83 ”/> <PCS:Candidate Name=”George Lazenby” Votes=”32”/> <PCS:Candidate Name=”Timothy Dalton” Votes=” 28 /> <PCS:Candidate Name=”Pierce

Ngày đăng: 13/08/2014, 15:21

TỪ KHÓA LIÊN QUAN