Selecting Categories
Note that the Edit, Update, Cancel and Delete options
should only be available for the owner(s) of the weblogs,
and not to the average visitor. This will be one of the
topics and additional features added next time (which
will also apply to ASP.NET development in general).
Right now, we’ve used them to fill the EcoSpace
with a few Categories. But now we should focus on the
average visitor, who wants to use these Categories to
select associated Posts.
We need to make some changes to the DataGrid now. I
still want the Name to be shown, but it should be a clickable
Name, resulting in a selection of that particular Category,
so we can display the associated Posts (and their Comments).
Go to the HTML Designer again, select the DataGrid,
and start the Property Builder dialog again. Go to the
Columns page, and add a HyperLink Column. Set its Header
text to Categories, it’s Text field to Name, its
URL field to ExternalId, and it’s URL format string
to
Blogs.aspx?RootId={0}
Note that I’m using Blogs.aspx here, the same
name as the page itself (so if you’ve renamed it
to something else, you have to use that URL instead).
The RootId is a hardcoded name that will be used in the
Page_Load code of the ECO ASP.NET Web Page to pre-initialise
the “current” object for the rhRoot ReferenceHandle
to the EcoSpace.
This new Column will be the only one that regular visitors
will be able to see. In order to secure our work for
next time, we should not delete the first Categories
column, or the Edit, Update, Cancel and Delete Columns,
but simply select them in the DataGrid Properties dialog,
and uncheck their Visible property checkbox.
When you close the DataGrid Properties dialog again,
the DataGrid will show only one column with clickable
Category Names.
To complete the transition, set the Visible property
of the btnNewCategory to False as well. It will still
be shown at design-time, but we won’t see it again
at run-time, which is good.
If you compile and run the Weblog ECO ASP.NET application
at this time, you will only see the DataGrid with the
categories. You can click on one of the category names,
say the first one, which will lead to the following slightly
surprising result:
In the URL field, you’ll see that I’ve clicked
on the first category object (with the “.NET Framework” name),
which has RootId equal to 2:1. The link itself is shown
in the DataGrid as well (as visited hyperlink), since
the first category that I clicked on is now the last
one.
If I click on another category, then the same thing
happens: the current selected category will be the last
one in the list. I’m sure there is an explanation
for this, but I’d like to keep the list of categories
sorted.
As you perhaps noticed in the previous screenshots,
I did my best to enter the categories sorted by name.
But in order to keep them sorted by name, I need to enforce
this. The best place is to use the Expression property
of the ehCategories ExpressionHandle component, since
that’s the input for the DataSource property of
the DataGrid.
Using the OCL expression editor, we can add an ->orderby(«<Any>»)
part to the Category.allInstances expression. However,
the «<Any>» will obviously lead to
an error message by the OCL parser, which we have to
replace by an actual expression. To sort the instances
by name, the solution is simple: replace «<Any>» by
Name, which turns the Expression property into the following
final value:
Category.allInstances->orderby(Name)
If we compile and run the application again, we can
now click on the category names in the DataGrid, and
while the newly clicked category will be the current
category, the order in which they are listed in the DataGrid
will not change.
Category and Posts
Time to add the Posts that belong to the selected Category.
For this, we need to add a new ExpressionHandle, call
it ehPosts, and set the AddExternalId property to True.
The RootHandle of ehPosts must be connected to the
rhRoot ReferenceHandle. By default rhRoot is pointing
to the entire EcoSpace. However, as soon as we click
on one of the items in the Categories DataGrid, we navigate
to our same Blogs.aspx page, passing a RootId in the
URL field. This RootId field is picked up in the Page_Load
code of the ECO web form, and used to point the rhRoot
ReferenceHandle to the selected object.
The only thing we need to do is tell the rhRoot that
it’s pointing to an EcoSpace object of type Category,
which is done in the StaticValueTypeName property of
rhRoot. This property needs to be set to Category (manually,
since the drop-down combobox for the StaticValueTypeName
property doesn’t show the available types yet).
As a result of all this, we can build an Expression
for the ehPosts ExpressionHandle that will show all Posts
related to the current selected Category.
Note that I’m
already making sure the Posts are sorted by their
date (the newest on top).
The result of this Expression can be displayed in a
DataGrid again, so place another one on the ECO web form.
I’ve placed the new DataGrid right next to the
first one, with a space in between them.
The DataSource property of the second DataGrid can
be connected to ehPosts, which will result in five columns
being shown: ExternalId, Author, Title, Posted and Contents.
We can set use the Auto Format dialog to set the scheme
of the new DataGrid to the same schema (or a different
one) as the one used in the first DataGrid.
Then, start the Property Builder dialog for the second
DataGrid. In the first page, specify that ExternalId
is the Data key field (as we also done with the first
DataGrid).
In the Columns page, uncheck the option to create columns
automatically at runtime, and add only the Author and
Posted columns. The Author field is optional – if
you only have one author who uses the weblog application,
you can leave it behind of course. The Posted column
is important. Set the Date formatting expression to {0:d}
if you only want to display the date of the Posted field
in the DataGrid, otherwise you get the date with the
time as well. Since I don’t expect to be making
several weblog posts per day anyway, the date value is
enough for my needs. But feel free to keep it in the
original way.
And what about the Title and Contents, you may ask?
Well, the title will be added as a HyperLink Column,
and the Contents will not be shown here, but in the Blog.aspx detail page (that we’re about to make).
For the Title field, add a HyperLink Column that you
can position before the Posted Column, set the Header
Text to Title, the Text field to Text, the URL field
to ExternalId, and the URL format string to the following:
Blog.aspx?RootId={0}
Note that I’m now navigating to Blog.aspx (for
one individual blog) instead of Blogs.aspx (for the categories
and blog titles).
Bootstrapping - again
And now, to support our bootstrapping process, we need
to add a Button to add a new blog Post object to the
selected category in the EcoSpace. Place a Button on
the web form, set its (ID) to NewPost and its Title to “New
Post”, and implement its Click event handler as
follows:
procedure TWebForm1.btnNewPost_Click(sender: System.Object; e: System.EventArgs);
var
NewPost: Post;
begin
NewPost := Post.Create(EcoSpace);
NewPost.Author := 'Bob Swart';
NewPost.Title := 'Title';
NewPost.Posted := DateTime.Now;
NewPost.Categories.Add(rhRoot.Element.AsObject as Category);
UpdateDatabase;
DataBind;
end;
This code will add a new Post to the EcoSpace, set its
Author, Title and Posted attributes, and add the current
selected Category to the list of Categories.
If we compile and run the application, the two DataGrids
are not displayed next to each other. In order to enforce
that, we have to add a HTML table with two horizontal
cells: one for the Categories DataGrid and New Category
button, and the other for the Posts DataGrid and the
New Post button.
With Table | Insert Table, we can insert a HTML table.
Set the Rows to 1, Columns to 2, and the Width to 100%
instead of 300 pixels. Finally, set the Border size to
0, so it won’t shown at run-time (but it will display
helpful border lines at design-time). Drag the Categories
DataGrid and New Categories Button in the left Table
Cell, and the Posts DataGrid and New Post Button into
the right Table Cell. Finally, set the valign property
of both Table Cells to top.
We can also add Edit, Update, Cancel and Delete Columns
to the second DataGrid, allowing us to modify the contents
of new posts. However, since this will edit everything
except for the actual Contents attribute, I would rather
add the edit capabilities to the new page: Blog.aspx,
which will display (and allow us to edit) just one weblog
Post object.
The ability to Delete Posts is welcome, so we can use
the Property Builder dialog to add a Delete Column. Don’t
forget to connect the DeleteCommand event of the second
DataGrid to the DataGrid_DeleteCommand predefined handler.
Note that both DataGrids now share the same event handler.
Save, compile and run the application, and test adding
new Posts to the selected category. You can also verify
that a click on another Category will refresh the list
of Posts, only showing those Posts that belong to that
particular Category.
Note that the Posts are sorted by date – the most
recent on top. The only thing that is not obvious from
the last screenshot is the current selected category.
This can be clarified by placing an ASP.NET Label control
just above the second DataGrid. Set its (ID) property
to lbCategory, and set the Font sub-properties to something
that is big and easy to read. Clear the text property,
as we’ll add a value in code.
Now, double-click on the Web Form itself, which will
bring us to the Page_Load event handler, where the code
is present to look for the RootId query field, pointing
the rhRoot ReferenceHandle to the selected Category object.
If the RootId field is found, then we should also assign
the Name of the currently selected Category to the lbCategory
Label, which takes just one additional line of code (marked
with )
as follows:
procedure TWebForm1.Page_Load(sender: System.Object; e: System.EventArgs);
var
Id: string;
begin
EcoSpace.Active := True;
Id := Request.Params['RootId'];
if Assigned(Id) and (Id <> '') then
begin
rhRoot.SetElement(ObjectForId(Id));
lbCategory.Text := (rhRoot.Element.AsObject as Category).Name;
end;
if not IsPostBack then
DataBind;
end;
With this last addition, we can view the Blogs page
in action as follows:
New ECO ASP.NET Blog Page
We now need to create the new ECO Web Form, called
Blog.aspx, and make sure it will show the current Post
object. Do File | New – Other, and go to the New
ASP.NET Files subcategory of the Delphi for .NET Projects
category, and double-click on the ECO ASP.NET Page icon.
This will produce a new ECO ASP.NET web form, that we
should save as Blog.aspx (the name we used in the Title
HyperLink Column).
This new ECO web form also has an rhRoot component
in the non-visual components area of the HTML Designer.
This time, we need to tell it that the EcoSpaceType property
value is WeblogEcoSpace.TWeblogEcoSpace (just open up
the drop-down combobox and select the only value), and
that the StaticValueTypeName is Post.
Before we can continue, we need to make sure that the
second WebForm can access the objects and types from
the CoreClassesUnit. Go to the top of the Blog.pas unit,
and add the unit CoreClassesUnit to the uses clause of
the interface section.
Showing Post Contents
We can now add controls to the Blog.aspx page
to display the attributes (Author, Title, Posted and
Contents) from the current selected Post. However, this
page can be seen in two different ways: either read-only,
by a weblog visitor, or in edit mode, by the actual author
of the weblog.
As I’ve already hinted earlier, next month
I’ll cover authentication and authorization, so
for now let’s focus on the more complex of the
two cases: the edit-mode, where the information should
be presented in a read/write format. Not everything,
by the way, since we can assume that the Author and Posted
attributes will not have to change, so only the Title
and actual Contents need to be edited.
In order to support a nice layout, let’s start
by inserting a HTML Table on the Blog web form, with
2 columns and 4 rows, a width of 100 percent, and border
size set to 0. Feel free to add a nice background colour
or image if you wish.
In the first column of the last row, I’ve placed
a HyperLink control from the Web Controls category, with
the Text property set to Back and the NavigateUrl property
set to Blogs.aspx, the page with the Categories and Posts
per Category.
In the second column of the first row of the HTML table,
I’ve placed a Label called lbPosted. In the second
column of the second row I’ve placed a TextBox
called tbTitle. In the third row I’ve placed a
TextBox called tbContents, this time with the TextMode
set to MultiLine, the Rows property set to 8 (for 8 Lines)
and the Width property set to 100%. Finally, in the last
row, I’ve placed another Label again, called lbAuthor.
I’ve cleared the Text property of the two Label
controls, since we can assign them in the Page_Load event
of the web form, as follows (again, the only two new
lines are the once with the comment, plus the begin-end
pair around them):
procedure TWebForm1.Page_Load(sender: System.Object; e: System.EventArgs);
var
Id: string;
begin
EcoSpace.Active := True;
Id := Request.Params['RootId'];
if Assigned(Id) and (Id <> '') then
begin
rhRoot.SetElement(ObjectForId(Id));
lbPosted.Text := (rhRoot.Element.AsObject as Post).Posted.ToString;
lbAuthor.Text := (rhRoot.Element.AsObject as Post).Author;
end;
if not IsPostBack then
DataBind;
end;
For the two TextBox controls, we can use a similar technique,
although we can also use the DataBinding functionality
(which means we don’t have to write any source
code).
To demonstrate the later, select the tbTitle TextBox,
and in the Object Inspector double-click on the (DataBindings)
property to start the tbTitle DataBindings wizard. We
have to bind the Text property of tbTitle to the Title
field of the rhRoot.[0] item.
In a similar way, we can bind the Text property of the
tbContents TextBox control to the ehPost[0].Contents
field.
Save Changes
All we need to add now, is a final Button to apply
the changes in the tbTitle and tbContents TextBox controls
back to the current Post object in the EcoSpace, and
update the database again.
For this, place a new Button on the web form, for example
in the last column of the last row, set the (ID) property
to btnSaveChanges, and the Title property to “Save
Changes”. The implementation of the Click event
should now be no problem: we have to cast the rhRoot
to the current Post again, set the Title and Content
properties, and call UpdateDatabase to ensure that the
database is updated with the changes. That’s all.
procedure TWebForm1.btnSaveChanges_Click(sender: System.Object;
e: System.EventArgs);
begin
with (rhRoot.Element.AsObject as Post) do
begin
Title := tbTitle.Text;
Contents := tbContents.Text;
end;
UpdateDatabase;
DataBind
end;
We can now save, compile and run the ECO ASP.NET weblog
application. Go to the Enterprise Core Objects category,
and select the Post of today, so I can edit the Title
and Contents.
Clicking on Save Changes will post the modifications
to the EcoSpace. And using the Back button we get back
to the main page where we can select the Categories and
individual Posts again.
Note that we still haven’t written more than
a few dozen lines of code! Still, full source code is
available for download from the Bitwise Magazine website
(delphiblog2.zip).
Next month, we’ll add some management capabilities
to the application, adding authentication and authorization,
making sure that the weblog author can add and edit Categories
and Posts, and delete Comments (if needed), while weblog
visitors can only read Categories, Posts and Comments
by others, or add their own Comments.
All this and more next time, so stay tuned...
Copyright © 2005
Bob Swart
|
Bob
Swart (aka Dr.Bob - www.drbob42.com)
is an author, trainer, developer, consultant and
webmaster for Bob Swart Training & Consultancy
(eBob42) in The Netherlands, who has spoken at
Delphi and Borland Developer Conferences since
1993. Bob has written chapters for seven books,
as well as the Borland Delphi 8 for .NET Essentials and Delphi
8. ASP.NET Essentials courseware manuals licensed
by Borland worldwide, and is selling his updated
Delphi 2005 courseware manuals online at http://www.drbob42.com/training.
Bob received the Spirit of Delphi award at BorCon
in 1999, together with Marco Cantù. |
June 2005
|