|
NOTE: This list of posts will only list the 15 latest posts,
to see the rest, select from the Archives located in the menu to the left. The
RSS will only list the 10 lastest posts.
|
Create your own NavigationBar (tabs) witin ASP.Net 2.0 by using the SiteMapDataSource.
|
Thursday, December 09, 2004 |
|
One
control that people have requested for ASP.Net 2.0, or rather asked if
it exists, is a navigation bar control (tabs). There is no NavigatonBar
controls shipped with ASP.Net 2.0, you have to build your own. I have
put together a very easy page, where I use the DataLsit control and
associate it with the SiteMapDataSoruce to display tabs. You can find
the code below. You can take the code and add it to a user control, if
you need to reuse it on several pages with you probably want to do, or
add it to a MasterPage. Remember that I put together this code within 10
minutes, so it’s not a complete solution, so you need to modify it so
suite your needs.
Here
is an example I put together quickly to show you how you can create
your own Tabs by using the DataList control and the SiteMapDataSource:
<%@ Page Language="C#" %>
<script runat=server>
protected void Page_Load()
{
int index = -1;
Int32.TryParse(Request.QueryString["id"], out index);
Tabs.SelectedIndex = index;
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
<style>
a
{
color: #000000;
text-decoration: none;
}
.myTab
{
background: #6666ff;
padding: 4px;
}
.myTabSelected
{
background: #ff00ff;
padding: 4px;
}
</style>
</head>
<body>
<form id="form1" runat="server">
<div>
<table>
<asp:DataList RepeatDirection=Horizontal ID="Tabs" runat="server" DataSourceID="SiteMapDataSource1">
<ItemTemplate>
<td width="4" height="20" valign="top" nowrap class="myTab">
<a href='<%# Eval("Url") %>'><%# Eval("Title") %></a>
</td>
</ItemTemplate>
<SelectedItemTemplate>
<td width="4" height="20" valign="top" nowrap class="myTabSelected">
<a href='<%# Eval("Url") %>'><%# Eval("Title") %></a>
</td>
</SelectedItemTemplate>
</asp:DataList>
</table>
<asp:SiteMapDataSource ShowStartingNode=false ID="SiteMapDataSource1" runat="server" />
</div>
</form>
</body>
</html>
The code above will get
the child nodes of the root node from the web.sitemap file, and will
add tabs to the page. When a tab is selected, the page will redirect to
the url specified within the web.sitemap file for the current node. The
tab will be marked as selected by using the “id” query string specified
within the web.sitemap file for the url, to know which tab is should
select. The following is the web.sitemap file:
<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
<siteMapNode url="https://#" title="Home">
<siteMapNode url="https://#" title="Information"/>
<siteMapNode url="https://#" title="Customer"/>
</siteMapNode>
</siteMap>
|
Posted:
9:26 PM
GMT+1
Feedback (1)
|
|
|
Defensive programming and Design by Contract on a routine level
|
Thursday, December 09, 2004 |
|
|
“When quality is pursued, productivity follows” – K. Fujino, Vice President of NEC Cooperation’s C&C Software Development Group.
People
are talking about how to create tools (Software Factories) that will
increase productivity. The view of productivity has been important. With
good productivity, we could build software rapidly and that will
probably save us a lot of money. But will it in the end deliver software
with high quality? The productivity could make the quality be left
behind, in some organizations, productivity tend to be more important
than quality. It’s important to know that quality on a product (or
service) is its benefit to satisfy, or preferably exceed the customers’
requirements and expectations. By putting productivity before quality,
will in short term goals save money, but will in long term cost more
money.
A
major component of quality in software is reliability. The software
will be reliable when it performs its job according to the specification
(correctness) and to handle abnormal situations (robustness).
If
we look at correctness vs robustness from a developer’s perspective,
correctness is met when a developer never returns an inaccurate result.
It’s better to return no results than returning an inaccurate result.
With robustness a developer always tries to do something that allows the
software to keep operating, even if a result could be inaccurate
sometimes. For safety-critical applications, correctness is more
important than robustness. For example, it’s more important that
software used in a chemical plant returns no results than wrong results.
For a consumer application, it could be more important to flavor
robustness than correctness. It’s better to return any result than
shutting down a system, for example. It is better if a program closed
down instead than showing a blue screen in Windows ;)
Reliable
software has no bugs (reliability is the absence of bugs), or has
it? Can software be completely free from bugs? Defensive
programming and Design by Contracts will be a help to create reliable
software with good correctness and/or robustness.
Defensive Programming
Defensive
programming is about protecting yourself from being hurt by something
dangerous (If bad data is sent to a routine, it will not hurt the
routine). By writing code that will protect yourself from bad data,
unexpected events, and other programmers mistakes, will in most case
reduce bugs and create a high quality software.
Good
programmers will not let bad data through. It’s important to validate
input parameters to not let the garbage in. It’s also important to make
sure that if garbage does come in, noting will goes out or an exception
will be thrown. The following example is a result from defensive driven
programming:
public Customer GetCustomerByName(string customerName)
{
//validate so no garbage is getting through
if (String.IsNullOrEmpty(customerName))
throw new ArgumentException("The name of the customer is empty");
//Get the customer from a data source
try
{
//Get customer from data source
Customer customer = ....
//Make sure the customer is not null before the use
//of its members in the compare method.
if (customer != null && String.Compare(customer.CustomerName, customerName) == 0)
return customer;
else
return null;
}
catch
{
throw;
}
}
In
the code above the input argument is first validated, if the validation
failed, an exception is thrown. If the validation passes, the routine
will try to get a customer from a data source by using the value from
the customerName argument. If the customer was not found, nothing will
out (returns null). If the routine fails to get the customer from the
data source an exception will be thrown. In the example, correctness is
more important than robustness. If robustness was more important, we
could return an instance of an empty customer or “introducing Null
object“ [Martin Fowler].
It’s
important to both validate the input data, data returned from a data
source and also use error handling to make sure a routine will not
return any bad data if it fails. If the try and catch block are used,
make sure an exception is passed on (thrown) and not only be catch
within the catch block and then ignored, it could make the code more
robustness but will violet correctness.
When
using a defensive driven programming too much, it could create
problems. Too much validation codes could make the code less readable
and affect performance, so don’t overuse defense driven programming.
Defensive programming will make error easier to find, easier to fix, and
less damage to production code. The result of using defense driven
programming will in return increase the quality of the software.
Design by Contract
Design
by Contract is about is about creating a contract between the client
and the supplier. The idea of the Design by Contract theory is to
associate a specification with every software elements. This
specification will define a contract between client and the supplier.
When a supplier writes a contract with the client, it should document
the obligations and benefits. When a client makes a call to the
supplier, the supplier makes sure that the client follows the contract,
this could be done by using precondition within a software routine
(Client’s obligations to the code it calls) and postcondition
(supplier’s obligations to the code that uses it). The following tabular
form shows a contract between a client and its supplier, where an
element is inserted into a dictionary:
Obligations
Benefits
(Must ensure precondition) (May benefit from postcondition)
Client
Make sure dictionary is not full Get the updated dictionary with the given
and the element is not empty and element.
don’t already exists.
(Must ensure postcondition) (May assume precondition)
Supplier
Add the given element to the Throw an exception if the dictionary is full
dictionary. or if the element is null.
C#
doesn’t have support of specifying specifications (contracts). To use
Design by Contract with C# we have to simulate it. A language that uses
speciation for Design By Contract, is Eiffel. The following is an
example in C# where post- and precondition is used (The contract
simulated in this example, is the one specified in the tabular form
above):
public void Add(object element) { //precondition (require) if (element==null) throw new ArgumentNullException("element");
if (this.IsFull) throw new Exception("The dictionary is full");
if (this[element] == element) throw new Exception("The element already exists within the dictionary.");
//do this._dictionary.Add(element);
//postcondition (ensure) If (!this._dictionary.Contains(element)) throw new Exception("The element is not added to the dictionary."); }
If
a client and a supplier have agreed over a contract where the supplier
promises to return a result, the supplier should ensure to fulfill its
demand, and the client will know that it will have a result and don’t
need to check if it’s true. If the supplier failed to fulfill its demand
at the first attempt, it should retry to fulfill its demand (rescue).
The following example show an example of the previous code, but where
the routines will retry to add an element even if the dictionary is full
(Note: this is only pseudocode used by only showing the concept, that
is why the code will try to add an element to the dictionary even if
each loop will probably result in the same result):
public void Add(object element) {
//precondition (require) if (element==null) throw new ArugmentNullException(element);
if (this[element] == element) throw new Exception("The element already exists within the dictionary.");
bool fail;
//rescue and retry for( int i = 0; i<100; i++) { try { this._dictionary.Add(element); fail = false; } catch { fail = true; }
//retry }
if (fail) throw new Exception("Can’t add element because the dictionary is full");
//postcondition (ensure) if (!this._dictionary.Contains(element)) throw new Exception("The element is not added to the dictionary."); }
If
C# should have support of adding specification to software elements, no
validation code should need to be written in the way the example shows
in the previous example, instead a contract should have been written. It
could look something like this, where "requires" is the precondition and the "ensures" is the postcondition:
public void Add(object element)
requires element != null
otherwise ArgumentNullException;
requires this[element] != element
otherwise Exception;
requires !IsFull
otherwise Exception;
ensures this[element] == element
otherwise Exception;
{
this._dictionary.Add(element);
}
The
Microsoft Research has created and overview of a new language called
Spec# (similar to C# but uses contract "Method Contracts"). Information about Spec#.
I hope this post has given you some basic understanding about what Defense Programming and Design by Contracts is all about. |
Posted:
10:29 AM
GMT+1
Feedback (3)
|
|
|
How to use the Profile feature outside an ASP.Net 2.0 application
|
Tuesday, December 07, 2004 |
|
The
Membership, Roles and Profile feature also work in a non-web
application. Many of you already know this and some doesn’t. In this
post I will explain how you can use the Profile feature within a non-web
application.
First
of all you need to add a reference to the System.Web.dll. The Profile
feature’s classes are located within the System.Web.dll.
To
use the Profile feature you have to add the profile section to the
configuration file of you app, and add the profile provider you want to
use. You need to add a provider if you should change the ApplicationName
attribute to the name of your application. If you already have a web
application, and want to get the profile information stored within the
web app, you have to specify the ApplicationName you used for your web
app in the configuration file. If you don’t do that, you will not get
the data stored within the web app.
Note:
ApplicationName is used to make it possible to share roles, users and
profiles etc between different applications, or to use the same data
source for the features but for different applications.
The following is an example of how you can add the profile section to an app.config file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<connectionStrings>
<add name="AspNetDBConString" connectionString="data
source=127.0.0.1;Initial Catalog=aspnetdb"
providerName="System.Data.SqlClient" /> </connectionStrings>
<system.web>
<profile defaultProvider="AspNetSqlProvider">
<providers>
<clear/>
<add
connectionStringName="AspNetDBConString"
applicationName="/
MyAppName"
description=""
name="AspNetSqlProvider"
type="System.Web.Profile.SqlProfileProvider, System.Web,
Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
/>
</providers>
<properties>
<add name="FirstName"/> <add name="LastName"/>
</properties> </profile>
</system.web>
</configuration>
In the configuration file above, there are two properties specified, FirstName and LastName.
Note:
If you already have another application and want to get the profile
data from that application, you have to specify the same properties you
have specified for the other application for you new application, if not
there will be a mismatch.
To create a new Profile you can use the ProfileBase class Create method:
ProfileBase profile = ProfileBase.Create("myUser", true);
The
Create method will create a new profile for the specified user. The
ProfileBase class exists in the System.Web.Profile namespace. In Beta 1
the name of the ProfileBase class would be HttpProfileBase:
HttpProfileBase profile = HttpProfileBase.Create("John", true);
In
ASP.Net 2.0, there is a dynamic compilation feature that autogenerates
the typed Profile class. Outside ASP.Net 2.0, the application is not
hooked up into this compilation system, so you have no typed access to
the Profile class. Instead you have to use the loosely typed syntax
instead. This is done by using the indexer of the ProfileBase class.
The
following code will use the indexer of the ProfileBase class to set the
FirstName and LastName property, and save the settings:
profile["FirstName"] = "John";
profile["LastName"] = "Doe";
profile.Save();
Note:
If you use the Profile feature within a web application, you don’t need
to call the Save method to save the profile. It’s automatically handled
by the profile features module when the EndRequest event is trigged.
The following code will get the profile value from the user “John”:
ProfileBase profile = ProfileBase.Create("John",true); Console.WriteLine((string)profile["FirstName"]);
|
Posted:
11:07 AM
GMT+1
Feedback (5)
|
|
|
SqlDataSource control and its sorting feature.
|
Monday, December 06, 2004 |
|
For
some weeks ago I wrote a post about how to use the sorting feature of
the ObjectDataSource. There has been some question regarding how the
sorting works for the SqlDataSource control, so I decided to write a
short post about his.
The SqlDataSource will use the Sort property of the DataView of the DataSet returned from the specified SelectCommand.
If
you use a Stored Procedure and set the SortParamterName property of the
SqlDataSource control, you can use your own parameter to make the Sql
server do the sorting, this will increase performance.
If you want the SqlDataSOurce to return a SqlDataReader instead of a DataSet, the sort feature will be disabled and not work.
If
you have a doubt if you should use the SqlDataSource or the
ObjectDataSource, then the following post on my blog could be useful SqlDataSource, ObjectDataSource and DataComponent in a heads up challenge |
Posted:
11:20 AM
GMT+1
Feedback (2)
|
|
|
How to name a method.
|
Sunday, December 05, 2004 |
|
[UPDATED]
For the moment I'm reading the book Code Complete 2:ed written by Steve McConnel, great book by the way.
I was passing through the chapter about naming methods and decided to write down what he suggests based on facts and experience.
Naming a method
The
name of the method should describe what it does, and should explain
everything it will do. If you use a bad method name it could be
difficult for someone to know when the method should be used and what
the method does, for example OutputUser(), Calculation() and ExecuteOperation() etc, don’t tell you what the method does, they could do almost everything.
The following is an example uses a bad method name:
public void OutputUser(int userId)
{
User user = UserRepository.GetById(userId);
if (user==null)
throw new Exception(String.Format("Can't find user with the id '{0}'", userId));
string serializedUser = SerializeUserToXml(user);
PrintUserInformation(serializedUser);
}
The
method above will get a user from the user’s repository and serialize
the user object into XML and send the XML output to a printer. A better
name for the method could be PrintSerializedUser(int userId).
The new name of the method will explain what the method does. A method
name should make the method self explained. With a good name, you can
also reduce the documentation of the method, because the name of the
method could be the documentation instead. In the book Complete Code,
Steve McConnel have another example where he have replaced a method with
the name HandleOutput() with FormatAndPrintOutput() to use a name that tells what the method does.
I
have heard someone said, that "And" and "Or" should not be used in a
method name. Mostly a method should only do one operation, not two or
more operations, but in the case where a method needs to do more than
one operation, the "And" and "Or" could be used, but only if you need
them for making the name of the method more understandable.
If you create a method, don’t use numbers to separate methods from each other, for example, CreateUser1(), CreateUser2()
etc. It will not tell you anything of the differences between those
methods. Could you figure out what the following methods will do by only
looking at the code?
public void InsertUser(User user)
{
if (user == null)
throw new ArgumentNullException("user");
if (string.IsNullOrEmpty(user.Password))
CreateUser1(user);
else
CreateUser2(user);
}
You
can probably figure out, that the different between the two CreateUser
methods has something to do if the Password property of the user is
empty or not, but you can’t know what the method really do by only
looking at the code for the method.
It’s
not only numbers that will make a method less understandable,
abbreviation could also make the code less understandable. Try to avoid
abbreviations in names. If you need to use an abbreviation to make the
name of the method short, try to only use abbreviation that others
understated. The following is an example of a method that uses
abbreviation:
public void CteNewUsrRptASndItToPrtr()
Try
to avoid method names like the one above, even if the method name will
be long, it’s better than make it not understandable. The above method
name could instead be something like the following name:
public void CreateNewUserReportAndSendToPrinter()
A
method name without abbreviation could sometime be very long. But how
long should a method name be? It depends; an average name is about 9 to
15 characters. But the important thing is to make the name self
explained and it should also fit into the screen, so we don’t need to
scroll the code to see the whole name.
Try
to use pascal casing when you name a method, the first character in
each word should be upper cased. If you have a method with a long name
and with several of words, the method will hard to read if you don’t use
pascal casing, for example, createuserandserializeuserandsendittoprinter() will be difficult to read but if you use pascal casing it will be easer to read, CreateUserAndSerializeUserAndSentItToPrinter() (I should never used a name like this, I only use it as an example.).
When
you create method that will return an object, try to have it named for
the value it returns. For example if you should get a customer number,
you can use a name like GetCustomerNumber(). The GetCustomerNumber()
method will tell you that the method will return a customer number. Try
to not name a method with the name of the class where the belongs to,
for example, User.CreateUser() should instead be Create() because the name of the class will in this case help you to understand what the method does, and by adding User into the method name it will only repeat the name.
If
you create two methods that should do the opposite operation, try to
use the right opposite names. For example if you have a method that
should open a door and one that should close a door, you should not use
the names OpenDoor() and Close(), they are not commonly understood. Instead use OpenDoor() and CloseDoor().
This
is was my guidelines of naming a method, if you have any comment or
more things that should be added that I have missed, please add a
comment, and share it with us all. |
Posted:
7:19 PM
GMT+1
Feedback (3)
|
|
|
Add custom attributes to the siteMapNode
|
Wednesday, December 01, 2004 |
|
In my previous post
I show you how you can use the security trimming to display SiteMap
nodes based on roles and permission. This time I will show you how you
can add an attributes to the siteManpNode where you can specify a
permission that the custom provider in the previous post
uses, to check if a user has the right permission to display a node.
The following is a web.sitemap that has a permission attribute for the
Help node:
<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
<siteMapNode url="https://#" title="Home">
<siteMapNode url="welcome.aspx" title="Welcome"/>
<siteMapNode url="help.aspx" title="Help" permission="View">
<siteMapNode url="https://#" title="About"/>
</siteMapNode>
</siteMapNode>
</siteMap>
To
get the value form the permission attribute we can use the indexer of
the SiteMapNode class. There is an Attributes property that are
protected, but I hope Microsoft will make it public, it’s more obvious
to get extra attributes from a Attributes property than the indexer, but
that is what I think.
The following code is the IsAccessibleToUser method form the customer provider in my previous post,
that will get the permission attribute and use it go check if a user
has the right permission to a node. If there is no permission attribute
added to a node, the node will be displayed:
public override bool IsAccessibleToUser(HttpContext context, SiteMapNode node)
{
if (!base.SecurityTrimmingEnabled)
return true;
if (node["permission"] != null)
return PermissionManager.HasCurrentUserPermission(node.Url, node["permission"]);
else
return true;
}
I want to thanks Stefan Schackow from showing me the SiteMapNode indexer. |
Posted:
6:31 AM
GMT+1
Feedback (1)
|
|
|
Display Menu nodes based on a roles and based on permission.
|
Tuesday, November 30, 2004 |
|
In
this post I will both describe how to enable security trimming for the
SiteMap feature for displaying nodes based on roles, and also how to
create a site map provider that will use the Permission Manager for displaying nodes based on permissions.
If
you use the TreeView, Menu or SiteMapPath control, you can associate
them whit a SiteMap data source control. The SiteMap data source
controls will get the data for a control, by using a SiteMap provider.
The default SiteMap provider is the XmlSiteMapProvider. The
XmlSiteMapProvider will get its data from the Web.sitemap file that
should by default be located in the web applications root folder. The
following code is an example of a Web.sitemap file:
<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
<siteMapNode url="https://#" title="Home">
<siteMapNode url="welcome.aspx" title="Welcome"/>
<siteMapNode url="help.aspx" title="Help">
<siteMapNode url="https://#" title="About"/>
</siteMapNode>
</siteMapNode>
</siteMap>
The
SiteMap file above has one root node (Home) which has two child nodes,
Welcome and Help and the Help node has one child node, About.
The following example will use a Menu control to display the nodes located in the Web.sitemap file for a user:
<%@ Page %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Menu ID="Menu1" runat="server" DataSourceID="SiteMapDataSource1"></asp:Menu>
<asp:SiteMapDataSource ID="SiteMapDataSource1" runat="server" />
</div>
</form>
</body>
</html>
By
default all nodes will be displayed for the users that visit the page.
If you want to hide nodes based on roles, you can turn on security
trimming for the SiteMap feature. To make sure that only specified roles
will be allowed to see a node, you can add the Roles attribute to a
site map node in the Web.sitemap file:
<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
<siteMapNode url="https://#" title="Home">
<siteMapNode url="welcome.aspx" title="Welcome"/>
<siteMapNode url="help.aspx" title="Help" roles="Customer">
<siteMapNode url="https://#" title="About"/>
</siteMapNode>
</siteMapNode>
</siteMap>
The code above will only let users that belong to the Customer role to see the Help node.
Before
the security trimming will work, you have to enable it. You can for
example enable security trimming in the web.config file by setting the
securityTrimmingEnabled attribute of the provider you use to true:
<siteMap defaultProvider="MySiteMapProvider">
<providers>
<clear/>
<add name="MySiteMapProvider" securityTrimmingEnabled="true" ...></add>
</providers>
</siteMap>
After
the security trimming are enable, the Welcome node will be hidden if
the current user of the page doesn’t belongs to the role Customer.
The
SiteMap provider used by the SiteMapDataSource is responsible to check
if the user has access to the node or not. The security check is made in
the provider’s IsAccessibleToUser method.
To
use the Permission Manager to check if a user has the permission to
view a node, you can create a new SiteMap provider and override the
IsAccessibleToUser method. The following code is an example of a custom
provider that inherits the XmlSiteMapProvider. The custom provider
overrides the IsAccessibleToUser method, and use the Permission Manager
to check if a user has permission to view a node:
public class MySiteMapProvider : XmlSiteMapProvider
{
public MySiteMapProvider()
{
}
public override bool IsAccessibleToUser(HttpContext context, SiteMapNode node)
{
if (!base.SecurityTrimmingEnabled)
return true;
return PermissionManager.HasCurrentUserPermission(node.Url, "View");
}
}
Note:
For each node, the Permission Manager’s HasCurrentUserPermission will
be executed and based on the provider the Permission Manager is using,
performance could be affected negatively.
To
use the MySiteMapProvider above, you only need to copy the code into
the app_code (\Code in Beta 1 and application_code in the October CTP)
folder and add the provider to the SiteMap section in the configuration
file and configure the Permission Manager:
<configSections>
<section name="permissionManager"
type="Nsquared2.Web.Configuration.PermissionManagerSection,
Nsquared2.Web, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null"/>
</configSections>
...
<permissionManager>
<permissions>
<group>
<permission name="View" description="View a node"/>
</group>
</permissions>
</permissionManager>
<system.web>
<siteMap defaultProvider="MySiteMapProvider">
<providers>
<clear/>
<add
name="MySiteMapProvider" type="MySiteMapProvider"
siteMapFile="Web.sitemap" securityTrimmingEnabled="true"></add>
</providers>
</siteMap>
....
</system.web>
Before
any nodes will be displayed, you have to give a user the permission to
view a node. It could be done by using the SetPermissionForUser method
of the PermissionManager class:
PermissionManager.SetPermissionForUser(“username”, Request.ApplicationPath + "/Welcome.aspx", "View");
The code above will give the user the permission to view the Welcome node.
Note: You only need to set the permission once, if you do it twice an exception will be thrown from the PermissionManager.
Please let me know if you have any question regarding this post. |
Posted:
3:25 PM
GMT+1
Feedback (5)
|
|
|
|
|
Create a Hierarchical Data Source for the TreeView and Menu control.
|
Friday, November 26, 2004 |
|
In
this post, I will show you how you can create your own data source
control for the TreeView and Menu control shipped with ASP.Net 2.0.
A
regular data source control used together with data-bound controls, for
example with the GridView or DetailView etc, returns a non hierarchy
data structure. Because the data source control do not return a
hierarchical data structure, the TreeView and Menu controls that are
used to display hierarchical data, can’t use the data source controls.
Instead they need to use a hierarchical data source control. A
hierarchical data source control has the same purpose as the data source
control, but will instead return a hierarchical data structure.
To create a hierarchical data source control you need to use two classes, HierarchicalDataSourceControl
and HierarchicalDataSourceView, and two interfaces, IHierarchicalEnumerable and IHierarchyData.
The
HierarchicalDataSourceControl is the main class that represents the
hierarchical data source control. This control has the responsibility to
return a HierarchicalDataSourceView. The class that inherits the
HierarchicalDataSourceControl needs to override the GetHierarchicalView
method, which has the responsibility to return the
HierarchicalDataSourceView. The HierarchicalDataSourceView is
responsible to collect data from a data source. Another method that you
can override is the CreateControlCollection method, is should return an
instance of a collection that holds the child controls. The following
code is an example of a HierarchicalDataSourceControl:
public class FileSystemDataSource : HierarchicalDataSourceControl
{
private FileSystemDataSourceView _view = null;
public FileSystemDataSource() : base() { }
protected override HierarchicalDataSourceView GetHierarchicalView(string viewPath)
{
if (null == this._view)
this._view = new FileSystemDataSourceView(viewPath);
return this._view;
}
protected override ControlCollection CreateControlCollection()
{
return new ControlCollection(this);
}
}
The
GetHierarchicalView method above will return a FileSystemDataSourceView
class that inherits the HierarchicalDataSourceView. The
HierarchicalDataSourceView represents a data view that will collect the
hierarchical data from a data source. Classes that inherit the
HierarchicalDataSourceView need to override the Select method that
should return the hierarchical data structure. The following code is the
implementation of a HierarchicalDataSourceView
(FileSystemDataSourceView) that the previous FileSystemDataSource class
returns from the GetHierarchicalView method:
public class FileSystemDataSourceView : HierarchicalDataSourceView
{
public FileSystemDataSourceView(string viewPath) {}
public override IHierarchicalEnumerable Select()
{
HttpRequest currentRequest = HttpContext.Current.Request;
string rootPath = HttpContext.Current.Request.MapPath(currentRequest.ApplicationPath);
DirectoryInfo rootDirectory = new DirectoryInfo(rootPath);
FileSystemHierarchicalEnumerable fshe = new FileSystemHierarchicalEnumerable();
foreach (FileSystemInfo fsi in rootDirectory.GetFileSystemInfos())
fshe.Add(new FileSystemHierarchyData(fsi));
return fshe;
}
}
The
Select method above will return a hierarchical data structure with
information about files and directories located in the web application’s
root folder. The FileSystemHierarchicalEnumerable class will return the
hierarchical data structure, the FileSystemHierarchicalEnumerable class
implements the IHierarchicalEnumerable interface.
The
IHierarchicalEnumerable represents a hierarchical collection that can
be enumerated with an IEnumerator. The classes that implement the
IHierarchicalEnumerable interface must implement the GetHierarchyData
method, which is a method used to retrieve an IHierarchyData object from
an enumerated item, in addition to the GetEnumerator method. The
following is an example of the FileSystemHierarchicalEnumerable class
that extends an ArrayList and implements the IHierarchicalEnumerable
interface. The FileSystemHierarchicalEnumerable is returned from the
FileSystemDataSourceView class’s Select method as you can see in the
example above.
public class FileSystemHierarchicalEnumerable : ArrayList, IHierarchicalEnumerable
{
public FileSystemHierarchicalEnumerable() : base() {}
public IHierarchyData GetHierarchyData(object enumeratedItem)
{
return enumeratedItem as IHierarchyData;
}
}
In
the previous example for the FileSystemDataSourceView items of type
FileSystemHierarchyData is added to the FileSystemHierarchicalEnumerable
class. The FileSystemHierarchyData represents the item that is added to
the hierarchy data structure. In the example in this post, the
FileSystemHierarchyData holds information about a file or directory and a
reference to the Parent and Childs nodes of an item. Classes that
implement the IHierarchyData interface need to implement three methods,
HasChildren, GetChildren and GetParent. I don’t know if I need to
example the HasChildren method. I think you know what is purpose is. The
GetChildren will return an IHierarchicalEnumerable that represents all
children. The GetParent also returns an IHierarchicalEnumerable with
represents the Parent item. If the GetParent method returns null, the
current item will be treated as the root item in the hierarchy data
structure. The following code is the FileSystemHierarchyData class the
represents the item of a structure. This class holds information about a
file or a directory:
public class FileSystemHierarchyData : IHierarchyData
{
private FileSystemInfo _fileSystemObject = null;
public FileSystemHierarchyData(FileSystemInfo obj)
{
this._fileSystemObject = obj;
}
public override string ToString()
{
return this._fileSystemObject.Name;
}
public bool HasChildren
{
get
{
if (typeof(DirectoryInfo) == this._fileSystemObject.GetType())
{
DirectoryInfo temp = (DirectoryInfo)fileSystemObject;
return (temp.GetFileSystemInfos().Length > 0);
}
else
return false;
}
}
public string Path
{
get { return this._fileSystemObject.ToString(); }
}
public object Item
{
get { return this._fileSystemObject; }
}
public string Type
{
get { return "FileSystemData"; }
}
public IHierarchicalEnumerable GetChildren()
{
FileSystemHierarchicalEnumerable children = new FileSystemHierarchicalEnumerable();
if (typeof(DirectoryInfo) == this._fileSystemObject.GetType())
{
DirectoryInfo temp = (DirectoryInfo)this._fileSystemObject;
foreach (FileSystemInfo fsi in temp.GetFileSystemInfos())
children.Add(new FileSystemHierarchyData(fsi));
return children;
}
}
public IHierarchicalEnumerable GetParent()
{
FileSystemHierarchicalEnumerable parentContainer = new FileSystemHierarchicalEnumerable();
if (typeof(DirectoryInfo) == this._fileSystemObject.GetType())
{
DirectoryInfo temp = (DirectoryInfo)this._fileSystemObject;
parentContainer.Add(new FileSystemHierarchyData(temp.Parent));
}
else if (typeof(FileInfo) == fileSystemObject.GetType())
{
FileInfo temp = (FileInfo)fileSystemObject;
parentContainer.Add(new FileSystemHierarchyData(temp.Directory));
}
return parentContainer;
}
}
You
can find the above classes in the MSDN Library shipped with VS 2005. I
have done some modification of the original code so it will not take up
to much space in this post.
The
following example is how the above hierarchical data source could be
associated to a TreeView control, and will display the folders and
directory located in the web applications root folder:
<%@ Page Language="C#" %>
<%@ Register Namespace="Nsquared2.Web" TagPrefix="Nsquared2" %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:TreeView ID="TreeView1" runat="server" DataSourceID="test">
</asp:TreeView>
<Nsquared2:FileSystemDataSource runat="server" id="test"></Nsquared2:FileSystemDataSource>
</div>
</form>
</body>
</html>
If
you copy the code the classes in this post and give them the namespace
Nsquared2.Web and put them into the app_code (\Code in Beta 1) folder,
you will have a working example where the TreeView control will display
the directory and files located in the root folder of the web
application. |
Posted:
2:57 PM
GMT+1
Feedback (1)
|
|
|
Change the style of a column within a GridView control based on its value
|
Wednesday, November 24, 2004 |
|
I
have seen questions about how to change the background or text color of
a column within a GridView control’s row when a value is either null or
empty or a value is greater than another value etc. So I decided to
blog about how to change the style of a column.
The
best way of changing the style of a column based on a columns value, is
by hook up to the GridView’s RowDataBound event. The RowDataBound event
will be executed directly after the row is data bounded. The
RowDataBound event uses the GridViewRowEventArgs class as the event
argument. With the event’s argument you can get the current row that has
been data bound by using the GridViewRowEventArgs’s Row property:
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
GridViewRow row = e.Row;
}
The Row property will return a GridViewRow object that inherits the TableRow class.
There
are different types of rows that could be data bound such as, DataRow,
EmptyDataRow, Footer, Header, Pager and Separator. To know which type
that is currently data bounded, you can use the GridViewRow’s RowType
property. The RowType property is an enum of type DataControlRowType.
The following code checks if the type of the Row is a standard row that
will be listed in the GridView:
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
//Do something!
}
}
To
get the cells of the row, you can use the Cells property that will
return a TableCellCollection object. To get a specific cell, you can use
the TableCellCollection indexer. The TableCellColleciton indexer will
return a TabelCell object, which will represent a cell in the row:
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
TableCell cell = e.Row.Cells[0];
}
}
To get the value from a TableCell, you can use the TableCell’s Text property:
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
string value = e.Row.Cells[0].Text;
}
}
Now
when you got the basic understanding of getting the value from a cell
within a row, you can easy change the style of a TabelCell based on the
cell’s value. The following code will query the “Alphabetical list of
products“ table from the Northwind database, and change the background
color to red for a column when the fourth cell (That will be the
“UnitPrice” column) is greater than 10:
<%@ Page Language="C#"%>
<%@ Import Namespace="System.Drawing" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<script runat="server">
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
if (Decimal.Parse(e.Row.Cells[3].Text) > 10)
e.Row.Cells[3].BackColor = Color.Red;
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" AutoGenerateColumns="False"
DataKeyNames="ProductID" OnRowDataBound="GridView1_RowDataBound">
<Columns>
<asp:BoundField ReadOnly="True" HeaderText="ProductID" InsertVisible="False" DataField="ProductID"
SortExpression="ProductID" />
<asp:BoundField HeaderText="ProductName" DataField="ProductName" SortExpression="ProductName" />
<asp:BoundField HeaderText="QuantityPerUnit" DataField="QuantityPerUnit" SortExpression="QuantityPerUnit" />
<asp:BoundField HeaderText="UnitPrice" DataField="UnitPrice" SortExpression="UnitPrice" />
</Columns>
</asp:GridView>
<asp:SqlDataSource
ID="SqlDataSource1" runat="server" SelectCommand="SELECT [ProductID],
[ProductName], [QuantityPerUnit], [UnitPrice] FROM [Alphabetical list of
products]"
ConnectionString="<%$ ConnectionStrings:AppConnectionString1 %>" />
</div>
</form>
</body>
</html> |
Posted:
9:03 AM
GMT+1
Feedback (0)
|
|
|
Hide items in a ListBox or DropDownList control.
|
Tuesday, November 23, 2004 |
|
In
ASP.Net 2.0 you can hide individual items from a ListBox and
DropDownList control. To hide an item you use the Enabled property of
the ListItem class. The following example uses the constructor to hide
the item:
ListItem item = new ListItem(text, value, enabled);
You can also use the Enabled property to hide an item:
item.Enabled = false;
If you hide an item, it will still be accessible from the server side code even if it will not be displayed in the list.
The
following code is an example where a ListBox control is filled with 5
items. The second item is disabled and will not appear in the ListBox.
When the Button control in the code is pressed, all the items in the
ListBox will be displayed:
<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
ListItem[] items = new ListItem[]
{
new ListItem("Item 1", "Value 1"),
new ListItem("Item 2", "Value 2", false),
new ListItem("Item 3", "Value 3"),
new ListItem("Item 4", "Value 4")
};
ListBox1.Items.AddRange(items);
}
}
protected void Button1_Click(object sender, EventArgs e)
{
foreach (ListItem item in ListBox1.Items)
{
Response.Write(item.Text + " enabled=" + item.Enabled + "<br>");
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ListBox ID="ListBox1" runat="server" />
<asp:Button ID="Button1" runat="server" Text="Button" OnClick="Button1_Click" /></div>
</form>
</body>
</html>
Instead of adding the items dynamically you can of course declare them on the page by adding the ListeItem to the ListBox:
<asp:ListBox ID="ListBox1" runat="server">
<asp:ListItem Text="Item1"></asp:ListItem>
<asp:ListItem Enabled="false" Text="Item2"></asp:ListItem>
<asp:ListItem Text="Item3"></asp:ListItem>
<asp:ListItem Text="Item4"></asp:ListItem>
<asp:ListItem Text="Item1"></asp:ListItem>
</asp:ListBox>
I’m sorry that I didn’t write about something more exiting feature, but some of you have probably not seen this feature yet. |
Posted:
10:45 AM
GMT+1
Feedback (4)
|
|
|
|
|
New version of the Permission Manager for .Net 2.0
|
Monday, November 22, 2004 |
|
During
this weekend I have made a new version of the Permission Manager. There
are several changes and the new version will only be available for the
Visual Studio 2005 October CTP, it will not work for the Beta 1. Some of
the changes are:
1) Permissions
are now added to the configuration file. It will make it easier to
deploy an application that uses the Permission Manager.
2) There are new methods for updating and removing permissions from users and roles.
3) It’s now possible to give a user or role a specific permission without setting the permission on a source (object).
4) The
CreatePermission method is removed, instead there is a Permissions
method and a Groups property added to the PermissionProvider. Those
members will return the permissions added to the configuration file.
The following is a simple demonstration of how you can use the Permission Manager.
When
you use the Permission Manager you first have to specify the
permissions that should be used. This is done by adding the
<permissions> element to the <permissionManager> section in
the configuration file:
<configSections>
<section
name="permissionManager"
type="Nsquared2.Web.Configuration.PermissionManagerSection,
Nsquared2.Web, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null"/>
</configSections>
<permissionManager enabled="true">
<permissions>
<group>
<permission name="Read" description=""/>
<permission name="Write" description=""/>
<permission name="Modify" description=""/>
</group>
<group name="News">
<permission name="Read" description="Allows to read news"/>
<permission name="Write" description="Allow to create news"/>
</group>
</permissions>
</permissionManager>
Permissions
are grouped into groups by using the <group> element. By using
the <group> element, you can specify the permissions that will
only be available for a specific kind of object. In the code above there
are two groups, one global and a News group. With the global group you
can grant a user or role a global permission (permissions that belongs
to the user and not to an specific object), for example, user John Doe
should have Read permission:
PermissionManager.SetPermissionForUser("John Doe", "Read");
You
don’t need to set this permission to a source. The source will instead
be the application. So if you should check if a user has the “Read”
permission you can use the HasUserPermission method:
if (PermissionManager.HasUserPermission("John Doe", "Read"))
Response.Write("User has permission to read..");
You
can’t use the global permissions for a source that belongs to another
group. Every source (object) belongs to a specific group. The reason
why, is because you should not have the possibility to specify other
permissions than the ones allowed to be set for an object. For example
if you have a News object, you maybe only want to use a Read and Write
permission, nothing else. So if you have given a user or a role the Read
permission globally you can’t check if the user has the Read permission
for an object that belongs to another group:
if (PermissionManager.HasUserPermission("John Doe","News","MyNews","Read"))
Response.Write("User has permission to read..");
The
code above will return false. If you have given the user the Read
permission for a specific source, you can then check if a user has the
permission to read the source:
PermissionManager.SetPermissionForUser("John Doe", "News", "MyNews", "Read");
if (PermissionManager.HasUserPermission("John Doe","News","MyNews","Read"))
Response.Write("User has permission to read..");
In
the code above the “News” is the name if the group that are specified
in the configuration file, it’s the group where the “MyNews” source
belongs to:
<group name="News">
<permission name="Read" description="Allows to read news"/>
<permission name="Write" description="Allow to create news"/>
</group>
Note: If you try to grant a user or s role permission that does not belong to the “News” group, an exception will be thrown.
Every
object you can set permissions for must belong to a group and have a
unique identifier. Instead of passing the name of the group and a unique
identifier to the SetPermissionForUser method etc, you can create an
object that you pass as an argument. To create a custom object you have
to implement the IAccessObject interface.
public interface IAccessObject
{
string SourceGroup
{
get;
set;
}
string SourceId
{
get;
set;
}
}
The
SourceGroup property returns the name of the group the object belongs
to and the SourceId the unique identifier that will unique identify the
instance of the object. The following is a News object that uses the
IAccessObject interface:
public class News : IAccessObject
{
private string _group = “News”;
private int _id;
public string SourceGroup
{
get { return this._group; }
set { throw new NotSupportedException(); }
}
public string SourceId
{
get { return this._id.ToString(); }
set { throw new NotSupportedException() };
}
public int Id
{
get { return this._id; }
set { this._id = value; }
}
...
}
The SourceId in the code above will return a string that will represents the unique id of News object.
The following example will give the role “Everyone” permission to modify the News object:
News news = new News();
news.ID = 10;
PermissionManager.SetPermissionForRole("Everyone", news, "Write");
To
check if the role “Everyone” has been granted the “Write” permission
for the News object you use the HasRolePermission method:
if (PermissionManager.HasRolePermission("Everyone", news, "Write"))
Response.Write("Everyone has the permission to modify the news");
Note: You can only set permission for a role if the .Net 2.0 Roles feature is used, if not an exception will be thrown.
When
you use the HasUserPermission, the method will first check if the roles
a user belong to have the specified permission. If one of the roles a
user belongs to have the specified permission, the method will return
true, but only if the user don’t have the permission denied. The
following example have granted the “Everyone” the permission to read the
News object, assume that the user “John Doe” belongs to the “Everyone”
role, the example will also check if “John Doe” has the permission to
read the news object:
PermissionManager.SetPermissionForRole("Everyone", news, "Read");
//This method will return true
if (PermissionManager.HasUserPermission("John Doe", news, "Read"))
Response.Write(“John Doe can read the news”);
You
can also allow or deny permission for a user or role. Denied permission
has the highest priority, so even if you have allowed a specific
permission for a user, but one of its roles has the same permission set
to deny, the user will not have the permission allowed.
If
you have any questions or if there are methods that you wanted to be
added to the Permission manager, please let me know. You can find the
latest version of the Permission Manager on the Permission Manager workspace. |
Posted:
9:59 AM
GMT+1
Feedback (0)
|
|
|
Configuration section handler
|
Wednesday, November 17, 2004 |
|
In
this post I will write something about the new section handler feature.
I have written about the new way of creating section handler before.
But this time I will focus on how to create a collection of adding
child elements by using the ConfigurationElementCollection class. The
code examples in this post will create a section handler that will get
the following section automatically, without needing to use XmlDom to
get the attributes or elements by our self:
<configSections>
<section
name="group" type="Nsquared2.Web.Configuration.GroupSection,
Nsquared2.Web, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null"/>
</configSections>
<group name="Group1">
<permission name="Read" description=""/>
<permission name="Write" description=""/>
<permission name="Modify" description=""/>
</group>
The
new section handler uses classes where the root element in a section
inherits the ConfigurationSection class and each child elements are
represented by a classes that inherits the ConfigurationElelement
class, and the element’s attributes are created as properties marked
with the ConfigurationPropertyAttribute uses to map a property to an
attribute. If the element has child controls like the group has in the
example above, there is a special class created to add elements like the
“permission” elements into a collection. Each collection is created as
an own class that inherits the ConfigurationElementCollection class. The
following example is the class for the “group” element where the “name”
attribute is created as a property and where the “permission” elements
are added to the PermissionElementCollection and can be collected from
the Permissions property:
public class GroupSection : ConfigurationSection
{
[ConfigurationProperty("name", RequiredValue=true)]
public string Name
{
get { return (string)base["name"]; }
set { base["name"] = value; }
}
[ConfigurationProperty("permissions", DefaultCollectionProperty=true, RequiredValue=true)]
public PermissionElementCollection Permissions
{
get { return ((PermissionElementCollection)base["permissions"]); }
}
}
As
you can see in the code above the Name property are marked with the
ConfigurationPropetyAttribute where the “name” of the attribute is
passed as an argument to the constructor and the attribute’s
RequiredValue is set to true. The RequiredValue is used to specify that
the attribute must be specified, if not an exception will be thrown. The
Name property will add the mapped attribute’s value to the base class ConfigurationValues
collection. The Permission property of the GroupSection class above
will return a collection with PermissionElement objects. The
DefaultCollectionProperty specify if the property represents the default
collection of the “group” element. In this case we only have one
collection and it’s the default collection. The “permissions” value
passed to the ConfigurationPropertyAttribute’s constructor will not be
an attribute or element that should be added to the “group” section in
the configuration file. To get a value added to the “group” section, you
can use the GetSection method of the ConfigurationSettings class:
GroupSection config = ConfigurationSettings.GetSection("group") as GroupSection;
When
the GetSection method is executed, it will locate the “group” section
in the <configSection> element and instantiate the specified type,
and use reflection to find the specified ConfigurationProperties in the
instantiated class, and automatically get elements and attributes
values from the “group” section in the configuration file. The
“permission” elements will also be collected and added to the
PermissionElementCollection (I will soon show you the implementation of
the collection).
Note:
You don’t need to use a Create method as before when you implemented
the IConfigurationSectionHandler, and you don’t need to use XmlDom to
get the data from the XmlNode passed as an argument to the Create
method.
Before
I show you the implementation of the PermissionElementCollection, let’s
take a look at the classes that will hold the information about the
“permission” element and that will be added to the collection when the
“group” section is requested from the GetSection method:
public class PermissionElement : ConfigurationElement
{
internal string _ElementName = "permission";
public PermissionElement()
{
}
protected override bool SerializeElement(XmlWriter writer, bool serializeCollectionKey)
{
bool retVal = false;
if (!base.SerializeElement(null, false))
return false;
if (writer != null)
{
writer.WriteStartElement(this._ElementName);
retVal |= base.SerializeElement(writer, false);
writer.WriteEndElement();
return retVal;
}
return (retVal | base.SerializeElement(writer, false));
}
[ConfigurationProperty("name", RequiredValue=true)]
public string Name
{
get { return ((string)base["name"]); }
set { base["name"] = value; }
}
[ConfigurationProperty("description")]
public string Description
{
get { return ((string)base["description"]); }
set { base["description"] = value; }
}
}
The
code above has two properties, Name and Description; both are mapped to
its own attributes added to the “permission” element. The base class’s
SerializeElement method most be overridden if we want to make it
possible to add elements to the configuration file programmatically. As I
wrote before, the GetSection method of the ConfiguartionSettings class
will insatiate the PermissionElement class for each “permission” element
added as child elements to the “group” section. Each PermissionElement
will be added to the PermissionElementCollection. Let’s take a look at
the implementation of the PermissionElementCollection class:
public class PermissionElementCollection : ConfigurationElementCollection
{
public PermissionGroupElement this[int index]
{
get
{
return (PermissionGroupElement)base.BaseGet(index);
}
set
{
if (base.BaseGet(index) != null)
base.BaseRemoveAt(index);
this.BaseAdd(index, value);
}
}
protected override bool IsElementName(string elementName)
{
if ((string.IsNullOrEmpty(elementName)) || (elementName != "group"))
return false;
return true;
}
protected override ConfigurationElementCollectionType CollectionType
{
get { return ConfigurationElementCollectionType.BasicMapAlternate; }
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((PermissionGroupElement)element)._ElementName;
}
protected override ConfigurationElement CreateNewElement()
{
return new PermissionGroupElement();
}
}
When
you create a collection class that inherits the
ConfigurationElementCollection, the CollectionType property that decide
which type of collection you are going to use, will by default only
support <add>, <remove> and the <clear>
element. To support your own elements, you have to override the
CollectionType property and make sure to return the collection type
BasicMap or BasicMapAlternate:
BasicMap
“Collections
of this type contain elements that apply to the level at which they are
specified, and to all child levels. A child level cannot modify the
properties specified by a parent element of this type.” – MSDN Lib
BasicMapAlternate
“Same
as BasicMap, except that this type causes the
ConfigurationElementCollection object to sort its contents such that
inherited elements are listed last.” – MSDN Lib
protected override ConfigurationElementCollectionType CollectionType
{
get { return ConfigurationElementCollectionType.BasicMapAlternate; }
}
When
you use a BasicMap or BasicMapAlrernate collection type, you have to
override the IsElementName and make sure to return true if the
elemenetName argument of the IsElemntName is the name of your child
element:
protected override bool IsElementName(string elementName)
{
if ((string.IsNullOrEmpty(elementName)) || (elementName != "permission"))
return false;
return true;
}
You
also have to add an indexer to your collection class. If no indexer is
created an exception will be thrown when you use the GetSection method
to get the section.
public PermissionGroupElement this[int index]
{
get
{
return (PermissionGroupElement)base.BaseGet(index);
}
set
{
if (base.BaseGet(index) != null)
base.BaseRemoveAt(index);
this.BaseAdd(index, value);
} }
The
two last methods you most override are the GetElementKey and the
CreateNewEelement. The GetElementKey will return the key of the
child element and the CreateNewElement will return a new instance of the
child element’s class:
protected override object GetElementKey(ConfigurationElement element)
{
return ((PermissionGroupElement)element).Name;
}
protected override ConfigurationElement CreateNewElement()
{
return new PermissionGroupElement();
}
I
only wrote this class to give your some more information about the new
section handler feature. You can find a lot of useful information in the
VS 2005 MSDN Library.
|
Posted:
11:13 PM
GMT+1
Feedback (0)
|
|
|
ASP.NET 2.0 product design changes between Beta 1 and Beta 2
|
Tuesday, November 16, 2004 |
|
One
change that probably most of you are going to like is that the
compilation model is more like ASP.Net 1.x, where the .aspx file will
now by default remain separated from the code-behind binary when the
code is pre-compiled. In beta 1, the pre-compilation compiled even the
.aspx pages together with the code-behind.
The reserved application_xxxx folder will now use the "app_" prefix. For example:
Beta
1
October
CTP
Beta 2
\Code
\Application_Code
\app_code
There will be a new CTP version probably released in November with those changes.
Read more about the ASP.NET 2.0 product design changes between Beta 1 and Beta 2 |
Posted:
6:30 AM
GMT+1
Feedback (2)
|
|
|