Tải bản đầy đủ (.pdf) (10 trang)

Professional ASP.NET 3.5 in C# and Visual Basic Part 130 ppsx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (116.74 KB, 10 trang )

Evjen c26.tex V2 - 01/28/2008 3:48pm Page 1251
Chapter 26: User and Server Controls
[Bindable(true)]
[DefaultValue("")]
public string Name
{
get { return _name;}
set { _name = value;}
}
[Bindable(true)]
[DefaultValue("")]
public string Text
{
get { return _text;}
set { _text = value;}
}
public override void DataBind()
{
CreateChildControls();
ChildControlsCreated = true;
base.DataBind();
}
protected override void CreateChildControls()
{
this.Controls.Clear();
_message = new Message(Name,Text);
if (this.MessageTemplate == null)
{
this.MessageTemplate = new DefaultMessageTemplate();
}
this.MessageTemplate.InstantiateIn(_message);


Controls.Add(_message);
}
protected override void RenderContents(HtmlTextWriter writer)
{
EnsureChildControls();
ChildControlsCreated = true;
base.RenderContents(writer);
}
}
}
To start to dissect this sample, first notice the
MessageTemplate
property. This property allows Visual
Studio to understand that the control can contain a template, and allows it to display the IntelliSense
for that template. The property has been marked with the
PersistanceMode
attribute indicating that
the template control should be persisted as an inner property within the control’s tag in the ASPX page.
Additionally, the property is marked with the
TemplateContainer
attribute, which helps ASP.NET figure
out what type of template control this property represents. In this case, it’s the Message template control
you created earlier.
1251
Evjen c26.tex V2 - 01/28/2008 3:48pm Page 1252
Chapter 26: User and Server Controls
The container control exposes two public properties, Name and Text. These properties are used to popu-
late the
Name
and

Text
properties of the Message control since that class does not allow developers to set
the properties directly.
Finally, the
CreateChildControls
method, called by the
DataBind
method, does most of the heavy
lifting in this control. It creates a new
Message
object, passing the values of
Name
and
Text
as constructor
values. Once the
CreateChildControls
method completes, the base DataBind operation comtinues to
execute. This is important because that is where the evaluation of the Name and Text properties occurs,
which allows you to insert these properties values into the template control.
After the control and template are created, you can drop them onto a test Web page. Listing 26-37
shows how the control can be used to customize the display of the data.
Listing 26-37: Adding a templated control to a Web page
VB
<%@ Page Language="VB" %>
<%@ Register Assembly="WebControlLibrary1" Namespace="WebControlLibrary1"
TagPrefix="cc1" %>
<script runat="server">
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
Me.TemplatedControl1.DataBind()

End Sub
</script>
<html xmlns=" >
<head runat="server">
<title>Templated Web Controls</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<cc1:TemplatedControl Name="John Doe" Text="Hello World!"
ID="TemplatedControl1" runat="server">
<MessageTemplate>The user ’<%# Container.Name %>’
has a message for you: <br />"<%#Container.Text%>"
</MessageTemplate>
</cc1:TemplatedControl>
</div>
</form>
</body>
</html>
C#
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
this.TemplatedControl1.DataBind();
}
</script>
As you can see in the listing, the <
cc1:TemplatedControl
> control contains a
MessageTemplate

within
it, which has been customized to display the
Name
and
Text
values. Figure 26-16 shows this page after it
has been rendered in the browser.
1252
Evjen c26.tex V2 - 01/28/2008 3:48pm Page 1253
Chapter 26: User and Server Controls
Figure 26-16
One item to consider when creating templated controls is what happens if the developer does not include
a template control inside of the container control. In the previous example, if you removed the Mes-
sageTemplate control from the TemplateContainer, a NullReferenceException would occur when you
tried to run your Web page because the container control’s MessageTemplate property would return
a null value. In order to prevent this, you can include a default template class as part of the container
control. An example of a default template is shown in Listing 26-38.
Listing 26-38: Creating the templated control’s default template class
VB
Friend Class DefaultMessageTemplate
Implements ITemplate
Public Sub InstantiateIn(ByVal container As System.Web.UI.Control) _
Implements System.Web.UI.ITemplate.InstantiateIn
Dim l As New Literal()
l.Text="No MessageTemplate was included."
container.Controls.Add(l)
End Sub
End Class
C#
internal sealed class DefaultMessageTemplate : ITemplate

{
public void InstantiateIn(Control container)
{
Literal l = new Literal();
l.Text="No MessageTemplate was included.";
container.Controls.Add(l);
}
}
Notice that the DefaultMessageTemplate implements the ITemplate interface. This interface requires that
the
InstantiateIn
method be implemented, which we use to provide the default template content.
To include the default template, simply add the class to the
TemplatedControl
class. You will also
need to modify the
CreateChildControls
method to detect the null MessageTemplate and instead create
an instance of and use the default template.
1253
Evjen c26.tex V2 - 01/28/2008 3:48pm Page 1254
Chapter 26: User and Server Controls
VB
If template = Nothing Then
template = New DefaultMessageTemplate()
End If
C#
if (template == null)
{
template = new DefaultMessageTemplate();

}
Creating Control Design-Time Experiences
So far in this chapter, you concentrated primarily on what gets rendered to the client’s browser, but
the browser is not the only consumer of server controls. Visual Studio and the developer using a server
control are also consumers, and you need to consider their experiences when using your control.
Note that beginning with Visual Studio 2008, the Web Page Design Surface used to provide Web page
designers with a WYSIWYG design experience has been completely rewritten. The design-surface, which
in prior versions was derived from the core Internet Explorer rendering engine has been replaced by a
completely independent and new rendering engine. This is good news for Web page developers because
they are no longer subject to the quirks of IE rendering. If you have existing controls you should be
sure to test them thoroughly on the new design-surface to ensure compatibility. From a control design
perspective, all of the previous functionality has been retained. Therefore, any controls you have written
to take advantage of design-time tools such as SmartTags or Designer regions should function normally
on the new design surface.
ASP.NET offers numerous improvements in the design-time experience you give to developers using
your control. Some of these improvements require no additional coding, such as the WYSIWYG ren-
dering of user controls and basic server controls; but for more complex scenarios, ASP.NET includes a
number of tools that give the developer an outstanding design-time experience.
When you write server controls, a priority should be to give the developer a design-time experience
that closely replicates the runtime experience. This means altering the appearance of the control on the
design surface in response to changes in control properties and the introduction of other server controls
onto the design surface. Three main components are involved in creating the design-time behaviors of a
server control:

Type Converters

Designers

UI Type Editors
Because a chapter can be written for each one of these topics, in this section I attempt to give you only

an overview of each, how they tie into a control’s design-time behavior, and some simple examples of
their use.
Type Converters
TypeConverter
is a class that allows you to perform conversions between one type and another. Visual
Studio uses type converters at design time to convert object property values to String types so that they
1254
Evjen c26.tex V2 - 01/28/2008 3:48pm Page 1255
Chapter 26: User and Server Controls
can be displayed on the Property Browser, and it returns them to their original types when the developer
changes the property.
ASP.NET includes a wide variety of type converters you can use when creating your control’s design-time
behavior. These range from converters that allow you to convert most number types, to converters that
let you convert Fonts, Colors, DataTimes, and Guids. The easiest way to see what type converters are
available to you in the .NET Framework is to search for types in the framework that derive from the
TypeConverter
class using the MSDN Library help.
After you have found a type converter that you want to use on a control property, mark the property
with a
TypeConverter
attribute, as shown in Listing 26-39.
Listing 26-39: Applying the TypeConverter attribute to a property
VB
<Bindable(True)> _
<Category("Appearance")> _
<DefaultValue("")> _
<TypeConverter(GetType(GuidConverter))> _
Property BookId() As System.Guid
Get
Return _bookid

End Get
Set(ByVal Value As System.Guid)
_bookid = Value
End Set
End Property
C#
[Bindable(true)]
[Category("Appearance")]
[DefaultValue("")]
[TypeConverter(typeof(GuidConverter))]
public Guid BookId
{
get
{
return _bookid;
}
set
{
_bookid = value;
}
}
In this example, a property is exposed that accepts and returns an object of type Guid. The Property
Browser cannot natively display a Guid object, so you convert the value to a string so that it can be
displayed properly in the property browser. Marking the property with the
TypeConverter
attribute
and, in this case, specifying the GuidConverter as the type converter you want to use, allows complex
objects like a Guid to display properly in the Property Browser.
1255
Evjen c26.tex V2 - 01/28/2008 3:48pm Page 1256

Chapter 26: User and Server Controls
Custom Type Converters
It is also possible to create your own custom type converters if none of the in-box converters fit into your
scenario. Type converters derive from the
System.ComponentModel.TypeConverter
class.
Listing 26-40 shows a custom type converter that converts a custom object called
Name
to and from
astring.
Listing 26-40: Creating a custom type converter
VB
Imports System
Imports System.ComponentModel
Imports System.Globalization
Public Class Name
Private _first As String
Private _last As String
Public Sub New(ByVal first As String, ByVal last As String)
_first = first
_last = last
End Sub
Public Property First() As String
Get
Return _first
End Get
Set(ByVal value As String)
_first = value
End Set
End Property

Public Property Last() As String
Get
Return _last
End Get
Set(ByVal value As String)
_last = value
End Set
End Property
End Class
Public Class NameConverter
Inherits TypeConverter
Public Overrides Function CanConvertFrom(ByVal context As _
ITypeDescriptorContext, ByVal sourceType As Type) As Boolean
If (sourceType Is GetType(String)) Then
Return True
End If
Return MyBase.CanConvertFrom(context, sourceType)
End Function
1256
Evjen c26.tex V2 - 01/28/2008 3:48pm Page 1257
Chapter 26: User and Server Controls
Public Overrides Function ConvertFrom( _
ByVal context As ITypeDescriptorContext, _
ByVal culture As CultureInfo, ByVal value As Object) As Object
If (value Is GetType(String)) Then
Dim v As String() = (CStr(value).Split(New [Char]() {" "c}))
Return New Name(v(0), v(1))
End If
Return MyBase.ConvertFrom(context, culture, value)
End Function

Public Overrides Function ConvertTo( _
ByVal context As ITypeDescriptorContext, _
ByVal culture As CultureInfo, ByVal value As Object, _
ByVal destinationType As Type) As Object
If (destinationType Is GetType(String)) Then
Return (CType(value, Name).First + " " + (CType(value, Name).Last))
End If
Return MyBase.ConvertTo(context, culture, value, destinationType)
End Function
End Class
C#
using System;
using System.ComponentModel;
using System.Globalization;
public class Name
{
private string _first;
private string _last;
public Name(string first, string last)
{
_first=first;
_last=last;
}
public string First
{
get{ return _first;}
set { _first = value;}
}
public string Last
{

get { return _last;}
set { _last = value;}
}
}
public class NameConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context,
Type sourceType) {
1257
Evjen c26.tex V2 - 01/28/2008 3:48pm Page 1258
Chapter 26: User and Server Controls
if (sourceType == typeof(string)) {
return true;
}
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context,
CultureInfo culture, object value) {
if (value is string) {
string[] v = ((string)value).Split(new char[] {’ ’});
return new Name(v[0],v[1]);
}
return base.ConvertFrom(context, culture, value);
}
public override object ConvertTo(ITypeDescriptorContext context,
CultureInfo culture, object value, Type destinationType) {
if (destinationType == typeof(string)) {
return ((Name)value).First + " " + ((Name)value).Last;
}
return base.ConvertTo(context, culture, value, destinationType);

}
}
The
NameConverter
classoverridesthreemethods,
CanConvertFrom
,
ConvertFrom
,and
ConvertTo
.The
CanConvertFrom
method allows you to control what types the converter can convert
from. The
ConvertFrom
method converts the string representation back into a
Name
object, and
ConvertTo
converts the
Name
object into a string representation.
After you have built your type converter, you can use it to mark properties in your control with the
TypeConverter
attribute, as you saw in Listing 26-35.
Control Designers
Controls that live on the Visual Studio design surface depend on control designers to create the design-time
experience for the end user. Control designers, for both WinForms and ASP.NET, are classes that derive
from the
System.ComponentModel.Design.ComponentDesigner

class. .NET provides an abstracted
base class specifically for creating ASP.NET control designers called the
System.Web.UI.Design
.ControlDesigner
. In order to access these classes you will need to add a reference to the System
.Design.dll assembly to your project.
.NET includes a number of in-box control designer classes that you can use when creating a custom
control; but as you develop server controls, you see that .NET automatically applies a default designer.
The designer it applies is based on the type of control you are creating. For instance, when you created
your first TextBox control, Visual Studio used the
ControlDesigner
class to achieve the WYSIWYG
design-time rendering of the text box. If you develop a server control derived from the
ControlContainer
class, .NET automatically use the
ControlContainerDesigner
class as the designer.
1258
Evjen c26.tex V2 - 01/28/2008 3:48pm Page 1259
Chapter 26: User and Server Controls
You can also explicitly specify the designer you want to use to render your control at design time using
the
Designer
attribute on your control’s class, as shown in Listing 26-41.
Listing 26-41: Adding a Designer attribute to a control class
VB
<DefaultProperty("Text")> _
<ToolboxData("<{0}:WebCustomControl1 runat=server></{0}:WebCustomControl1>")> _
<Designer(GetType(System.Web.UI.Design.ControlDesigner))> _
Public Class WebCustomControl1

Inherits System.Web.UI.WebControls.WebControl
C#
[DefaultProperty("Text")]
[ToolboxData("<{0}:WebCustomControl1 runat=server></{0}:WebCustomControl1>")]
[Designer(typeof(System.Web.UI.Design.ControlDesigner))]
public class WebCustomControl1 : WebControl
Notice that the
Designer
attribute has been added to the
WebCustomControl1
class. You have specified
that the control should use the
ControlDesigner
class as its designer. Other in-box designers you could
have specified are

CompositeControlDesigner

TemplatedControlDesigner

DataSourceDesigner
Each designer provides a specific design-time behavior for the control, and you can select one that is
appropriate for the type of control you are creating.
Design-Time Regions
As you saw earlier, ASP.NET allows you to create server controls that consist of other server controls
and text. In ASP.NET 1.0, a server control developer could use the
ReadWriteControlDesigner
class
to enable the user of the server control to enter text or drop other server controls into a custom server
control at design time. An example of this is the ASP.NET Panel control, which enables developers to

add content to the panel at design time.
Since ASP.NET 2.0, however, creating a control with this functionality has changed. The
ReadWrite
ControlDesigner
class was marked as obsolete, and a new and improved way was included to
allow the developer to create server controls that have design-time editable portions. The new technique,
called designer regions, is an improvement over the
ReadWriteControlDesigner
in several ways. First,
unlike the
ReadWriteControlDesigner
class, which allowed only a single editable area, designer regions
enable you to create multiple, independent regions defined within a single control. Second, designer
classes can now respond to events raised by a design region. This might be the designer drawing a
control on the design surface or the user clicking an area of the control or entering or exiting a template
edit mode.
1259
Evjen c26.tex V2 - 01/28/2008 3:48pm Page 1260
Chapter 26: User and Server Controls
To show how you can use designer regions, create a container control to which you can apply a custom
control designer (as shown in Listing 26-42).
Listing 26-42: Creating a c omposite control with designer regions
VB
<Designer(GetType(MultiRegionControlDesigner))> _
<ToolboxData("<{0}:MultiRegionControl runat=server width=100%>" & _
"</{0}:MultiRegionControl
>
")
>
_

Public Class MultiRegionControl
Inherits CompositeControl
’ Define the templates that represent 2 views on the control
Private _view1 As ITemplate
Private _view2 As ITemplate
’ These properties are inner properties
<PersistenceMode(PersistenceMode.InnerProperty), DefaultValue("")
>
_
Public Overridable Property View1() As ITemplate
Get
Return _view1
End Get
Set(ByVal value As ITemplate)
_view1 = value
End Set
End Property
<PersistenceMode(PersistenceMode.InnerProperty), DefaultValue("")
>
_
Public Overridable Property View2() As ITemplate
Get
Return _view2
End Get
Set(ByVal value As ITemplate)
_view2 = value
End Set
End Property
’ The current view on the control; 0= view1, 1=view2, 2=all views
Private _currentView As Int32 = 0

Public Property CurrentView() As Int32
Get
Return _currentView
End Get
Set(ByVal value As Int32)
_currentView = value
End Set
End Property
Protected Overrides Sub CreateChildControls()
MyBase.CreateChildControls()
Controls.Clear()
Dim template As ITemplate = View1
1260

×