My two previous posts described how to extend a basic behavior of controls without changing them. Ajax control extenders add Ajax/JavaScript behavior to controls and control adapters can provide different rendering for controls without changing them. One of the disadvantages of control extenders is that they can extend only 1 control, so there is 10 controls to extend, 10 extenders must be added to a page. And if we want to add an extender to each control (we want to extend) in the whole ASP.NET application, we would have to go through all pages and add a lot of code. That would be definitely tedious and could take a lot of time if an application has many pages. But that's where control adapters come in handy. We would need to create an adapter for the control being extended and override CreateChildControls method to generate ControlExtender markup and add it to as a child the control. Make sure that if the adapter overrides the Render and RenderChildren method, it calls the RenderChildren method from an override of the Render method. Of course it is not very common to add extenders to every single control type within a Web application (especially because Ajax Control Extenders require Ajax ScriptManager on a page so we would need to make sure that one is present on the page that contains a control to which an extender will be added), but there might be situations that it would be desirable. For developers who are not aware of this solution that would mean a lot of tedious work so most probable that task would either not be accomplished as someone would decide that it wouldn't be worth so much resources (time and money) or would take a lot of time (and money). In situations like that you can really see the difference between a developer for whom developing is a mere means of earning money and a thriving developer for whom it is also a hobby and excitement.
It was last week when I took the 70-564 exam and I passed with the score 985/1000. It looks like I got 1 question wrong. Nevertheless, I have earned Microsoft Certified Professional ASP.NET 3.5 Developer title.
Here is my experience with the exam:
- there is 50 (!) questions (70-503 (WCF) exam also has 50 questions but on the other hand 70-505 (Windows Forms 3.5) exam has only 40 questions) and 120 minutes
- questions are not harder than those for 70-562 exam, so if you can pass that exam, you can pass this one
- there is very few questions on Ajax - MCTS ASP.NET 3.5 exam (70-562) had about 10 questions on Ajax but this one maybe 3 or 4. Also I think 1 question was exactly the same as I had at 70-562 exam
- there is a lot of questions on state management, which is in line with the exam objectives (Manage state of controls and Design a state management strategy).
- main focus of questions is to test the ability to identify appropriate usage, so in oder words 70-562 exam requires a tester to know how to use something whereas 70-564 exam requires to know when to use something or which one to use in this scenario.
- the most crucial aspect at the exam is to read questions very carefully - most questions will contain a list of requirements that almost all answers will meet but there will, of course, only 1 answer (unless it is multiple choice question) that will meet every single requirement and every single word in all the requirements.
- very often requirement(s) will contain a comma separated list ie.types of files or activities so make sure that answers will satisfy all elements from the list(s).
Next exam on path to MCPD Enterprise Application Developer 3.5 is MCTS 70-561 ADO.NET 3.5, which will be the last one before the final 70-565 exam.
When reviewing for the 70-564 exam I did some practice with Control Adapters and more specifically ASP.NET Web Control Adapters and Page Adapters. It reminded me that not many .NET Web developers are aware of their existence, even though control adapters have been available since .NET 2.0. Of course it doesn't mean that every Web application must utilize a control adapter but in certain scenarios they might save a lot of time required to accomplish the same task without using control adapters. Simply, control adapters are controls that inherit from System.Web.UI.Adapters.ControlAdapter base class. Moreover, control adapters for ASP.NET Web Controls inherit from System.Web.UI.WebControls.Adapters.WebControlAdapter base class and ASP.NET page control adapters inherit from System.Web.UI.Adapters.PageAdapter base class. They are all abstract classes so to create a control adapter, one must inherit them. Control adapters adapt or modifies behavior at key points in the life cycle of the control for which a control adapter has been created. At each stage in the life cycle, where a call to a life cycle method is made, the ASP.NET page framework checks to see if there is an associated adapter for the control and calls on the adapter's associated method instead of the control's method. In many cases, the adapter method may simply defer back to the control's method. An exception to this behavior are adapters for state management in that the adaptive behavior is additive to the control's state. Control adapters are most commonly used to provide target-specific processing during a specific stage of the control life cycle or to customize target-specific rendering. WebControlAdapter class have additional several methods that are specific to rendering tags. PageAdapter class is the starting point for rendering a Web page adaptively. Additionally, the PageAdapter class defines properties and methods that enable adaptive rendering in the context of typical page-level tasks, such as caching or managing page-state persistence. So simply control adapters are a way of providing different renderings for controls without actually modifying the controls themselves. Because control adapters are designed to provide alternate renderings for different clients, you specify control adapter mappings in a .browser file, which is where associations between User Agent strings and browser capabilities are defined. Browser files can be added either in the local App_Browsers directory of an ASP.NET Web application or in the machine-wide %SYSTEM%\Microsoft.NET\Framework\v2.0.50727\Config\Browsers directory. Even though control adapters are associated with a specific browser type, they can be added to the Default.browser file and hence be applied to all clients. Below is an example of creating and using a page adapter, but the same rules apply for creating any control adapter.
ViewStatePageAdapter.cs
using System.Web.UI.Adapters;
using System.Web.UI;
public class ViewStatePageAdapter:PageAdapter
{
public override PageStatePersister GetStatePersister()
{
return new SessionPageStatePersister(this.Page);
}
protected override void Render(HtmlTextWriter writer)
{
base.Render(writer);
writer.RenderBeginTag(HtmlTextWriterTag.Div);
writer.Write("Copyright 2009");
writer.RenderEndTag();
}
}
<browsers>
<browser refID="Default">
<controlAdapters>
<adapter controlType="System.Web.UI.Page"
adapterType="ViewStatePageAdapter" />
</controlAdapters>
</browser>
</browsers>
public partial class _Default : System.Web.UI.Page
{
protected override void OnPreRenderComplete(EventArgs e)
{
this.ClientScript.RegisterClientScriptBlock(
this.GetType(),
"DisplayViewState",
"<script>window.status=('ViewState: ' + document.forms[0]['__VIEWSTATE'].value.length + ' bytes');</script>");
}
}
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>View State Page Adapter Test</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:GridView ID="GridView1" runat="server" DataSourceID="AdvantureWorks"
AllowPaging="True" AutoGenerateColumns="False" DataKeyNames="EmployeeID"
PageSize="50" CellPadding="4" ForeColor="#333333" GridLines="None">
<RowStyle BackColor="#EFF3FB" />
<Columns>
<asp:BoundField DataField="EmployeeID" HeaderText="EmployeeID"
InsertVisible="False" ReadOnly="True" SortExpression="EmployeeID" />
<asp:BoundField DataField="NationalIDNumber" HeaderText="NationalIDNumber"
SortExpression="NationalIDNumber" />
<asp:BoundField DataField="ContactID" HeaderText="ContactID"
SortExpression="ContactID" />
<asp:BoundField DataField="LoginID" HeaderText="LoginID"
SortExpression="LoginID" />
<asp:BoundField DataField="ManagerID" HeaderText="ManagerID"
SortExpression="ManagerID" />
<asp:BoundField DataField="Title" HeaderText="Title" SortExpression="Title" />
<asp:BoundField DataField="BirthDate" HeaderText="BirthDate"
SortExpression="BirthDate" />
<asp:BoundField DataField="MaritalStatus" HeaderText="MaritalStatus"
SortExpression="MaritalStatus" />
<asp:BoundField DataField="Gender" HeaderText="Gender"
SortExpression="Gender" />
<asp:BoundField DataField="HireDate" HeaderText="HireDate"
SortExpression="HireDate" />
<asp:CheckBoxField DataField="SalariedFlag" HeaderText="SalariedFlag"
SortExpression="SalariedFlag" />
<asp:BoundField DataField="VacationHours" HeaderText="VacationHours"
SortExpression="VacationHours" />
<asp:BoundField DataField="SickLeaveHours" HeaderText="SickLeaveHours"
SortExpression="SickLeaveHours" />
<asp:CheckBoxField DataField="CurrentFlag" HeaderText="CurrentFlag"
SortExpression="CurrentFlag" />
<asp:BoundField DataField="rowguid" HeaderText="rowguid"
SortExpression="rowguid" />
<asp:BoundField DataField="ModifiedDate" HeaderText="ModifiedDate"
SortExpression="ModifiedDate" />
</Columns>
<FooterStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
<PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign="Center" />
<SelectedRowStyle BackColor="#D1DDF1" Font-Bold="True" ForeColor="#333333" />
<HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
<EditRowStyle BackColor="#2461BF" />
<AlternatingRowStyle BackColor="White" />
</asp:GridView>
<asp:SqlDataSource ID="AdvantureWorks" runat="server"
ConnectionString="<%$ ConnectionStrings:AdventureWorks %>"
SelectCommand="SELECT HumanResources.Employee.* FROM HumanResources.Employee">
</asp:SqlDataSource>
</div>
</form>
</body>
</html>
This sample Web Site uses a custom page adapter to store View State in a Web Server Session State. Also it adds to a page a div element with "Copyright 2009" text. A page adds in PreRenderComplete method a javascript that displays page ViewState size in client's browser status bar in order to see the difference of ViewState size with and without the ViewStatePageAdapter (to see the information on the view state size in IE7 and IE8 the following option must be enabled "Allow status bar updates via script").
The ViewStatePageAdapter has removed 25kB of View State infromation from a Web page, which means that 25kB less will be transfered to a client. Also the text we added to one control (PageAdapter) will be displayed in all pages. If a custom control or page adapter has been added go GAC and to machine-wide Default.browser file, it could affect all ASP.NET applications without really making any changes to those applications, so for example instead of displaying a regular page content it could display information about "Maintenance Break". The fact that control adapters can provide different rendering without actually modifying the controls themselves has been used to to create a suite of control adapters that change the rendering of several common ASP.NET controls to take better advantage of CSS (CSS-Friendly ASP.NET 2.0 Control Adapters). That made possible to have GridView control render using html div elements with css styles rather than html table. By looking at the huge power of control adapters, it is very obvious that any thriving ASP.NET developer should have a very good understanding of them and should not be afraid to use them only because there are other developers around that don't have any experience in using them, which is what I face from time to time and what I want to change through my posts.
Before .NET 3.5 to extend a control meant usually to inherit from a base control and add extra functionality or create a custom control that would contain a control to extend with additional items ie. javascript. But .NET 3.5 introduced another way to extend a control, which is through Ajax Control Extender, but I have experienced that still many developers are not aware of that fact and still use either inheritance (in java keyword extends is equivalent of keyword inherits in VB.NET or ":" in C#) or create a custom control where they could take advantage of ExtenderControl class. That class enables you to programmatically add AJAX functionality (or simple javascript) to an ASP.NET server control. Since it's not based on inheritance, there is no limitation of extending only unsealed controls.Also one extender may extend different types of controls (TextBox, Button, MyTextBox, etc.) - before it would require creating custom class for each type of controls being extended. I believe that's why AjaxControlToolkit contains so many control extenders ie. SliderExtender, RoundedCornersExtender or ModalPopupExtender. But even though there always will be a business need (or rather functional requirement) to create a custom control extender ie. FocusExtender that will apply different css style whenever a target control gets focus and reverts it back to the default css style when a target control has lost focus. Plus target control can be any type of control - TextBox, Button, DropDownList, etc.
Control extender is created by creating a new project of type: ASP.NET Ajax Server Control Extender.
FocusExtender.cs
using System;
using System.Collections.Generic;
using System.Web.UI;
namespace Extenders
{
[TargetControlType(typeof(Control))]
public class FocusExtender : ExtenderControl
{
protected override IEnumerable<ScriptDescriptor>
GetScriptDescriptors(Control targetControl)
{
ScriptBehaviorDescriptor desc = new ScriptBehaviorDescriptor(
"Extenders.FocusBehavior", targetControl.ClientID);
desc.AddProperty("onFocusCssClass", OnFocusCssClass);
desc.AddProperty("defaultCssClass", DefaultCssClass);
yield return desc;
}
protected override IEnumerable<ScriptReference>
GetScriptReferences()
{
ScriptReference sRef = new ScriptReference();
sRef.Name = "Extenders.FocusBehavior.js";
sRef.Assembly = this.GetType().Assembly.FullName;
yield return sRef;
}
public string OnFocusCssClass
{
get
{
String s = (ViewState["OnFocusCssClass"] as String);
return s ?? "";
}
set
{
ViewState["OnFocusCssClass"] = value;
}
}
public string DefaultCssClass
{
get
{
String s = (ViewState["DefaultCssClass"] as String);
return s ?? "";
}
set
{
ViewState["DefaultCssClass"] = value;
}
}
}
}
Type.registerNamespace('Extenders');
Extenders.FocusBehavior = function(element)
{
Extenders.FocusBehavior.initializeBase(this, [element]);
this._onFocusCssClass = null;
this._defaultCssClass = null;
this._focusHandler = null;
this._blurHandler = null;
}
Extenders.FocusBehavior.prototype =
{
initialize : function()
{
Extenders.FocusBehavior.callBaseMethod(this,
'initialize');
var e = this.get_element();
this._focusHandler = Function.createDelegate(
this, this._onFocus);
this._blurHandler = Function.createDelegate(
this, this._onBlur);
$addHandler(e, 'focus', this._focusHandler);
$addHandler(e, 'blur', this._blurHandler);
},
dispose : function()
{
var e = this.get_element();
if (this._focusHandler)
{
$removeHandler(e, 'focus', this._focusHandler);
this._focusHandler = null;
}
if (this._blurHandler)
{
$removeHandler(e, 'blur', this._blurHandler);
this._blurHandler = null;
}
Extenders.FocusBehavior.callBaseMethod(this, 'dispose');
},
_onFocus : function(evt)
{
var e = this.get_element();
if (e != null)
{
e.className = this._onFocusCssClass;
}
},
_onBlur : function(evt)
{
var e = this.get_element();
if (e != null)
{
e.className = this._defaultCssClass;
}
},
get_defaultCssClass : function()
{
return this._defaultCssClass;
},
set_defaultCssClass : function(value)
{
this._defaultCssClass = value;
this.raisePropertyChanged('defaultCssClass');
},
get_onFocusCssClass : function()
{
return this._onFocusCssClass;
},
set_onFocusCssClass : function(value)
{
this._onFocusCssClass = value;
this.raisePropertyChanged('onFocusCssClass');
}
}
Extenders.FocusBehavior.registerClass('Extenders.FocusBehavior', Sys.UI.Behavior);
Next, Extenders.dll can be added to any Web Site project and any control can gain new behavior, which will allow to change its color when it got focus.
Default.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register Assembly="Extenders" Namespace="Extenders" TagPrefix="MI" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Untitled Page</title>
<style type="text/css">
.Normal
{
background-color: White;
color:Black;
font-weight:normal;
}
.Highlight
{
background-color: Blue;
color:White;
font-weight:bold;
}
.HighlightButton
{
background-color: Yellow;
color:Red;
font-weight:bold;
}
</style>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ScriptManager ID="ScriptManager1" runat="server" />
Username:
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
Password:
<asp:TextBox ID="TextBox2" runat="server" TextMode="Password"></asp:TextBox>
<asp:DropDownList ID="DropDownList1" runat="server">
<asp:ListItem>Value1</asp:ListItem>
<asp:ListItem>Value2</asp:ListItem>
</asp:DropDownList>
<asp:Button ID="Button1" runat="server" Text="Button1" />
<MI:FocusExtender ID="focusExtender1" runat="server" TargetControlID="TextBox1" OnFocusCssClass="Highlight"
DefaultCssClass="Normal" />
<MI:FocusExtender ID="focusExtender2" runat="server" TargetControlID="TextBox2" OnFocusCssClass="Highlight"
DefaultCssClass="Normal" />
<MI:FocusExtender ID="focusExtender3" runat="server" TargetControlID="DropDownList1"
OnFocusCssClass="Highlight" DefaultCssClass="Normal" />
<MI:FocusExtender ID="focusExtender4" runat="server" TargetControlID="Button1" OnFocusCssClass="HighlightButton"
DefaultCssClass="Normal" />
</div>
</form>
</body>
</html>
And here are the screenshots:
This very simple example shows how one control extender can change behavior of many different types of controls or add some custom javascript to the page. Some developers who are not experienced in Ajax client-side library and creating Ajax behaviors might find writing control extenders a little complex but nowadays it's almost impossible to be a thriving professional .NET ASP.NET Web Developer without having a good understanding of Ajax client-side programming as he cannot take a full advantage of .NET 3.5 and Visual Studio 2008. And the problem is that Ajax will be getting more and more popular so the sooner you get familiar with all features of Ajax, the easier it will be to learn new features of its next versions and next versions of .NET and Visual Studio. I hope this will encourage everyone to get familiar with control extenders and use them to add extra functionality to the existing controls when next time you will have to extend a control.
One of the skills that 70-564 exam measures is the ability to choose appropriate controls based on business requirements. And when I look at the projects or Web applications I worked with, I don't recall any of them that wouldn't use some data-bound controls. Whether it was displaying shopping cart, list of managed assets, dashboard of CRM system, list of registered users in system, history of mortgage loan installment payments or simply search results based on some criteria, they all have been designed using some data-bound controls.Fortunately, the books do pretty good job describing available data-bound controls, but I haven't found yet a book that would present a simple comparison of those controls so instead of going through 50 - 100 pages, you could just simply look at some chart or 1 page and choose appropriate control based on the requirements. I have found that kind of summaries very helpful - I can better see the similarities and differences, which helps me a lot during exams but also at work when I can instantly propose the appropriate solution, which what every thriving developer should be able to do.
Choosing appropriate project templates is very crucial, yet many times developers will choose a template they are most accustomed to or sometimes they don't simply know that there's already a template that will meet their need. Very rarely do books describe available templates and developers usually too busy to explore that end up using either the wrong kind of template or create one from scratch, where they could save a lot of time by using something that has already been developed and tested and is ready to use out of the box. Personally I know a few .NET Web Developers that still don't really understand the difference between ASP.NET Web Site and ASP.NET Web Application. That's why I thought it would be good to go through at least Web templates as those are the subject of the 70-564 exam.
There are two kinds of Web templates - Web Site and Web Project, which they can be accessed for example from the context menu of the solution.
- ASP.NET Web Site - introduced in Visual Studio 2005; doesn't have a project file; all classes must be placed in App_Code folder; all classes by default don't contain namespaces; classes can be written in different languages as long as they are in separate subfolders within App_Code folder; to deploy everything must be copied to IIS folder; separate assembly can be created per page hence changes in 1 page will result in recompiling only 1 assembly; web references are added to special folder App_WebReferences; structure of the project is based on file system (folders represent physical system folders)
- ASP.NET Web Service – this template opens ASP.NET Web Site template and adds web service file (*.asmx) and puts code-behind file to App_Code folder
- Empty Web Site – this template creates blank ASP.NET Web Site project but without Default.aspx page, web.config file and without App_Data folder
- WCF Service – this template opens ASP.NET Web Site template and adds WCF service file (*.svc) and puts code-behind file to App_Code folder
- ASP.NET Reports Web Site – this template opens ASP.NET Web Site template and adds Default.aspx page with ReportViewer element and opens a wizard to create a new Report Data Source, which allows you to choose views or tables from which to retrieve data to strongly typed DataSet that is put in App_Code folder.Finally a local report (*.rpdl) is created
- ASP.NET Crystal Reports Web Site – this template opens ASP.NET Web Site template and adds Default.aspx page with CrystalReportViewer element and opens a wizard to create a new Crystal Report (*.rpt), which is being placed in the root folder
- Dynamic Data Entities Web Site – new template (requires .NET 3.5 SP1) provides a scaffolding framework that enables you to create a data-driven application within minutes using ASP.NET Web Site template, EntityDataSource and ADO.NET Entity Framework
- Dynamic Data Web Site – new template (requires .NET 3.5 SP1) provides a scaffolding framework that enables you to create a data-driven application within minutes using ASP.NET Web Site template, LinqDataSource and LINQ to SQL.
- ASP.NET Web Application - introduced in Visual Studio 2003; require project file, which defines project structure in application; contains bin folder with compiled assembly of the project; no special App_Code folder is required; all classes automatically get a namespace when added to the project; all classes must be in one language; only visual elements and compiled assemblies must be copied to IIS folder; you can add pre-build and post-build steps during compilation; web reference is added to folder Web References
- ASP.NET Web Service Application – this template opens ASP.NET Web Application template and adds web service file (*.asmx) with code-behind file to the root folder
- ASP.NET Ajax Server Control – this template creates a new ASP.NET custom server control that inherits from ScriptControl, which inherits from WebControl class (base class for all ASP.NET Web server controls) and implements IScriptControl (interface with methods to define JavaScript resources of the custom control)
- ASP.NET Ajax Server Control Extender – this template creates a new ASP.NET control extender that inherits from ExtenderControl, which inherits from Control class (class defines the properties, methods, and events that are shared by all ASP.NET server controls) and implements IExtenderControl (interface with methods to define JavaScript resources with the behavior for an extender control). This templates allows to add custom client behavior to the existing controls and extend their functionality. It can extend different types of controls (buttons, textboxes, etc.) at the same time, where normally you would have to create several custom controls that would inherit from the controls you want to extend.
- ASP.NET Server Control – this template, known in VS2005 as Web Control Library, creates ASP.NET Server control(s) – classes that inherit from WebControl class and can be added to Visual Studio Toolbox
- WCF Service Application – this template opens ASP.NET Web Application template and adds WCF service file (*.svc) and with its code-behind file in the root folder.
- Dynamic Data Entities Web Application – this is new template (requires .NET 3.5 SP1) provides a scaffolding framework that enables you to create a data-driven application within minutes using ASP.NET Web Application template, EntityDataSource and ADO.NET Entity Framework
- Dynamic Data Web Application – this is new template (requires .NET 3.5 SP1) provides a scaffolding framework that enables you to create a data-driven application within minutes using ASP.NET Web Application template, LinqDataSource and LINQ to SQL
I hope you've found this overview of Visual Studio 2008 templates useful and helpful in deciding what type of template to use when you're going to create a new project next time. Maybe you will need to create a data-driven web site and you will consider using Dynamic Data Entities Web Site template rather than creating your project from scratch - or maybe you will decide to create additional templates so next time you can take advantage of something you've already developed so you can write less code and play more golf.
After having passed 70-562 exam, I had only 2 more exams to take in order to become Microsoft Certified Professional Enterprise Application Developer. However, when I looked at the syllabus of 70-564 exam and read more about that exam, I decided to take 70-564 exam, which is Pro: Designing and Developing ASP.NET Applications Using the Microsoft .NET Framework 3.5. Passing it would complete all requirements to be recognized as Microsoft Certified Professional ASP.NET 3.5 Developer.
I started from readying the exam syllabus and then I compared it with exams 70-547 (MCPD ASP.NET 2.0), 70-562 (MCTS ASP.NET 3.5) and 70-565 (MCPD Enterprise Applications 3.5). And it seems that 70-564 exam is like 70-562 exam, where previous MCPD ASP.NET exam covered a lot more (technical envisioning and planning, stabilizing and releasing, evaluation of database designing, etc.). Now those objectives are covered by 70-565 exam, which makes exam 70-564 almost identical to 70-562.
Second thing I always to do before starting studying to an exam is reading other people's blogs and forums to learn their experience and look for some tips. Here are the links to some of the web sites I found with information on the exam:
http://www.accelerated-ideas.com/examinfo/default.aspx?id=11897
http://www.zoroja.com/blog/2009/10/11/how-to-study-for-the-ms-70-564-exam/
http://stackoverflow.com/questions/751051/how-to-study-for-microsoft-exam-70-564-asp-net-3-5
http://stackoverflow.com/questions/963998/studying-for-the-70-564-exam
http://blog.campbellgunn.com/2009/06/passed-exam-70-564-today/
http://www.ms-faqs.com/content/12/47/en/what-books-should-i-study-to-pass-exam-70_564.html
http://www.jumpadvance.com/resources
http://omgili.com/jmp/
That helped me established the following facts about the exam:
- The content is almost identical to 70-562 so if you can pass 70-562, you can pass 70-564
- The exam is very 3.5 centric, which means that extra time should be spent of things like WCF, Ajax, LINQ, ListView, DataPager, aspnet_merge, etc.
- There is no study book for the exam and the book for the previous exam (70-547) is only a waste of time. Generally, it seems that about four to five books are needed to cover the exam's material - a book for AJAX, LINQ or ADO.NET 3.5 that includes LINQ, ASP.NET custom control, designing including Master pages and a security book.
- You just needed to read the questions very carefully - I'm guessing that there might be more than 1 answer (maybe even all) that would solve the problem but the task is to find the one that meets all requirements or is the most efficient or is the easiest to implement or is simply the best practice.
I especially liked the idea that if you can pass 70-562, you can pass 70-564 exam. That was one of the reasons that I decided to start studying to this exam plus the fact that I have a voucher valid till December, 31st 2009 with 30% off on the exam. And of course the fact that I passed 70-562 a few days ago, so it seems I don't have to learn much for this exam.
The books I'm going to use are as follows:
Professional ASP.NET 3.5
Introducing Ajax
Professional Linq
ASP.NET 2.0 -Advanced Topics
ASP.NET 2.0 - Core Reference
ADO.NET 2.0 - Core Reference
Programming WCF
Learning WCF - Hands-on Guide
ASP.NET 3.5 Enterprise Application Development with Visual Studio 2008: Problem Design Solution
Building a Web 2.0 Portal with ASP.Net 3.5
Professional ASP.NET 2.0 Design: CSS, Themes, and Master Pages
Pro ASP.NET 2.0 E-Commerce in C# 2005
There is a lot of books and some of them contain information on ASP.NET 2.0 but they still contain a lot of very useful information and there is no 3.5 equivalent. And due to the fact that I have already read those books, they will be used mostly as a reference to read through examples with main focus how the proposed solutions have met the original requirements, which should help with choosing the best answer during the exam - but most of all during real life project when a developer is responsible for gathering requirements and proposing the best solution, which is probably one of the most crucial elements during SDLC, that has a huge impact on the overall success of a project. And I guess that's why this exam's syllabus is not to measure more knowledge but to measure skills to follow requirements and to know how to utilize the knowledge you have acquired. And those are the skills that every thriving developer should possess.
If you study the books I mentioned in my previous post you will find there how to design custom UserControl, WebControl, CompositeControl, CompositeDataBoundControl. You will learn there how to use some of their methods like EnsureChildren, CreateChildControls, ReCreateChildControls, PerformDataBinding, or EnsureDataBound. But when it comes to adding custom events to your custom control those books mention only 1 way - by declaring public event as a field in your class. And I think there's only a few developers that know the other way, and building custom controls is something that web developer does on a daily basis or at least very often. So what's the secret alternative? It is using System.ComponentModel.EventHandlerList class. And it is interesting that this class has been in .NET since version 1.1 and it's not very common - maybe because there is no examples in MSDN. So what's so interesting about this class? Let's declare 3 custom controls: without any events, with events are public fields and with EventHandlerList:
public class CustomDetailsView1 : WebControl
{
public override void RenderControl(System.Web.UI.HtmlTextWriter writer)
{
base.RenderControl(writer);
}
}
public class CustomDetailsView2 : WebControl
{
public event DetailsViewCommandEventHandler ItemCommand;
public event EventHandler ItemCreated;
public event DetailsViewDeletedEventHandler ItemDeleted;
public event DetailsViewDeletEventHandler ItemDeleting;
public event DetailsViewInsertedEventHandler ItemInserted;
public event DetailsViewInsertEventHandler ItemInserting;
public event DetailsViewUpdatedEventHandler ItemUpdated;
public event DetailsViewUpdateEventHandler ItemUpdating;
public event EventHandler ModeChanged;
public event DetailsViewModeEventHandler ModeChanging;
public event EventHandler PageIndexChanged;
public event DetailsViewPageEventHandler PageIndexChanging;
public override void RenderControl(System.Web.UI.HtmlTextWriter writer)
{
base.RenderControl(writer);
}
}
public class CustomDetailsView3 : WebControl
{
protected EventHandlerList _events;
public event DetailsViewCommandEventHandler ItemCommand
{
add
{
if (_events == null)
_events = new EventHandlerList();
_events.AddHandler("ItemCommand", value);
}
remove
{
if (_events != null)
_events.RemoveHandler("ItemCommand", value);
}
}
public event EventHandler ItemCreated
{
add
{
if (_events == null)
_events = new EventHandlerList();
_events.AddHandler("ItemCreated", value);
}
remove
{
if (_events != null)
_events.RemoveHandler("ItemCreated", value);
}
}
...
...
public override void RenderControl(System.Web.UI.HtmlTextWriter writer)
{
base.RenderControl(writer);
}
}
Here are my test scenarios:
static void CustomDetailsView1Test()
{
System.GC.Collect();
Console.WriteLine("Memory test of CustomDetailsView control with no custom events");
Console.WriteLine("Before instantiation: "+System.GC.GetTotalMemory(true));
CustomDetailsView1 ctl = new CustomDetailsView1();
System.GC.KeepAlive(ctl);
Console.WriteLine("After instantiation: "+System.GC.GetTotalMemory(true));
ctl.Dispose();
ctl = null;
}
static void CustomDetailsView2Test()
{
System.GC.Collect();
Console.WriteLine("Memory test of CustomDetailsView control with custom events as fields");
Console.WriteLine("Before instantiation: "+System.GC.GetTotalMemory(true));
CustomDetailsView2 ctl = new CustomDetailsView2();
System.GC.KeepAlive(ctl);
Console.WriteLine("After instantiation: "+System.GC.GetTotalMemory(true));
ctl.ItemCreated += new EventHandler(ctl_ItemCreated);
Console.WriteLine("After hooking up to ItemCreated event: "+System.GC.GetTotalMemory(true));
ctl.ItemDeleting += new DetailsViewDeletEventHandler(ctl_ItemDeleting);
Console.WriteLine("After hooking up to ItemDeleting event: "+System.GC.GetTotalMemory(true));
ctl.Dispose();
ctl = null;
}
static void CustomDetailsView3Test()
{
System.GC.Collect();
Console.WriteLine("Memory test of CustomDetailsView control with custom events as elements of EventHandlerList");
Console.WriteLine("Before instantiation: "+System.GC.GetTotalMemory(true));
CustomDetailsView3 ctl = new CustomDetailsView3();
System.GC.KeepAlive(ctl);
Console.WriteLine("After instantiation: "+System.GC.GetTotalMemory(true));
ctl.ItemCreated += new EventHandler(ctl_ItemCreated);
Console.WriteLine("After hooking up totemCreated event: "+System.GC.GetTotalMemory(true));
ctl.ItemDeleting += new DetailsViewDeletEventHandler(ctl_ItemDeleting);
Console.WriteLine("After hooking up to ItemDeleting event: "+System.GC.GetTotalMemory(true));
ctl.Dispose();
ctl = null;
}
The results are somehow interesting. Our custom control without events requires 80 bytes, with all events: 128 bytes and with EventHandlerList without any events hooked up - 84 bytes. Great! But what happens if we hook up to two events. For control with events as fields it required 32 bytes to do that but for control using EventHandlerList it was 96 bytes plus additional 12 bytes to instantiate EventHandlerList object. In my example I used constant strings as keys to add or remove handler from a list but usually the EventHandlerList is used as follows:
public class CustomDetailsView : WebControl
{
protected EventHandlerList listEventDelegates = new EventHandlerList();
static readonly object itemDeletingEventKey = new object();
public event DetailsViewDeleteEventHandler ItemDeleting
{
add { listEventDelegates.AddHandler(itemDeletingEventKey, value); }
remove { listEventDelegates.RemoveHandler(itemDeletingEventKey, value); }
}
public void Delete()
{
if (listEventDelegates[itemDeletingEventKey] != null)
(listEventDelegates[itemDeletingEventKey] as DetailsViewDeleteEventHandler)(this, EventArgs.Empty);
}
}