![]() Creation of a forum in ASP.NET Introduction Forums - they news - groups and echo conferences, - perhaps, one of the most attractive services which the web - resource can offer the visitors. Huge popularity of some sites runeta (I think, the reader and itself will guess about what speech) not in the last instance speaks that, that on these sites it is possible to find the interesting and professionally made forums with high attendance where can always help efficient advice{council}. Clearly, that popularity of this or that forum not in the last instance speaks quality of his realization - if the forum is made too primitively, it can frighten off visitors, but, on the other hand, if to go too far with various "navorotami" job of a forum can overload seriously the server, that too will not so well have an effect on attendance of your web - resource. There is a set of various variants of creation of a forum. Most simple of them can relieve entirely you of great volumes of coding - for example if you want to be content only with flat output mode of messages, without division into subjects. Certainly, the choice of this or that realization is a business of taste, but to devote whole clause{article} to creation of the flat list of messages obviously does not cost. Therefore here we shall consider more interesting variant. Present{True} clause{article} describes the basic stages of creation of a hierarchical forum with use ASP.NET. From messages the tree on which it is convenient to trace development of a subject by interlocutors will be built: Figure 1. An example of a forum The number of messages in each branch is theoretically indefinite. Messages of a forum are deduced page by page, and those on page can specify quantity{amount} manually - having chosen the necessary number from the combined list located in heading of a forum. For rendering such page enough one SQL-search about which we shall talk hardly later. The forum was tested and fine works in browsers MSIE of version 5 and is higher, Mozilla 1.4, Opera versions 7 and are higher. The forum has been written by a principle of " a separate code » - all server scripts are separated from a aspx-code. As SQL server it was used MS SQL Server. In present{true} clause{article} the basic stages of creation of server and client scripts of the given forum are described. In the application to clause{article} it is possible to find ready realization of a forum - the project for Visual Studio .NET 2003. Creation of patterns of a DHTML-code The general{common} principles For the beginning we shall say some words about the basic ideology of realization described in the present{true} clause{article}. Though we also shall consider{examine} here creation of the ASP.NET-project in language C *, our approach will be, we shall say so, is not absolutely typical for development of webs - resources with use ASP.NET. It is natural, when speech comes about advantages ASP.NET the first and, perhaps, the brightest advantage which is to be mentioned are convenient northern elements of the management, allowing to hide from the developer "complex " details DHTML. Due to such control-am as DataGrid, it is possible to reduce strongly time of development - to reduce the decision of some problems{tasks} to « job by a mousy » in the designer of webs - forms, and any knowledge DHTML it is not required. In this sense server elements of management represent ready decisions for most often used functionality but even if in ASP.NET you something is required the greater it is necessary to forget about convenience of the visual designer. The same is fair and for desktop applications - first of all for what already are under construction on a ready skeleton (framework), inkapsulirujuhhem system calls for simplification of job on creation of the user interface. So that to develop the interface with base functionality, it is enough to lead{carry out} some minutes in the designer of forms or even simply to use the corresponding master. But if something is required the greater, than standard elements of management Windows without great volumes of manual coding to not do without. Even in FCL for the decision of similar problems{tasks}, as a rule, it is necessary to work directly with system functions though, indoubtedly, one of the basic advantages of the given library of classes consists in pleased strong covering Windows API and an opportunity to create the user interfaces, "abstracting" from API-functions. Concerning webs - applications some kind of "analogue" Windows API the DHTML-code - i.e. it can act, we shall say so, that « a low level » on which it is possible to create the web - application. Realization of a forum described here just also is such "nizkourovnevoj" realization. Whether it means, what we will not use any advantages ASP.NET? Answering this question, it is not necessary to forget that, besides convenient server control-ov, among advantages ASP.NET is as well an opportunity to write a server part in such modern object-oriented languages as C *, and to use rich opportunities of library of classes FCL. Also it is necessary to say, that without this advantage creation of "heart" of our project - class ForumGenerator - began was impossible. The principle of job of the generator of a forum is simple enough. All job on the server will be limited only actually to creation of a DHTML-code of a forum - though it is the basic and most complex stage in realization, - then "management" will pass to client scripts, and all loading will be removed{will be taken off} from the server. The resulting page of a forum develops of the ready patterns of a HTML-code stored{kept} in resources of assembly. Such approach is used in realization of a forum applied to clause{article} and at him , certainly, there is a number{line} of essential lacks. For example, it is necessary to make recompilation of the project for change of patterns DHTML. If in the developed project it is necessary to change patterns HTML enough often, and necessity of recompilation for fulfilment of each change will seriously complicate support of a web - resource it is better to transfer patterns HTML from resources of assembly to an external XML-file - the general{common} principles of realization of a forum from it will not change at all. As practically all code of page will be dynamically formed, the basic starting page of the demonstration project - default.aspx - will contain only empty container (DIV), processable on the party of the server, and some the latent fields (INPUT type = "hidden"), intended for data exchange between client and server scripts. THE NOTE If you want, that on edges{territories} of page the border, that was not deduced{removed}, defining{determining} for page style, do not forget, that for removal{distance} of this border in MSIE it is necessary to specify margin:0px 0px 0px 0px, and in browsers Mozilla and Opera - padding:0px 0px 0px 0px. Therefore for correct display of page in all browsers considered{examined} here it is necessary to use these both instructions{indications}. For convenience all client scripts which are used in realization described here, are placed in a separate file forum.js. Therefore on page links to this file and to a file with the table of styles which will define{determine} appearance of our forum - forum.css also should contain. THE NOTE That the cursor of the mouse at prompting on element HTML became a hand, it is necessary to specify in style not cursor:hand as this directive does not correspond{meet} to the standard and her are able to interpret only Opera and MSIE, and cursor:pointer - and then you will achieve desirable effect in all browsers. Do not pay attention that the editor of tables of styles VS .NET marks cursor:pointer directive as erroneous is problem VS .NET, instead of yours. The given instruction{indication} will be quite correct to interpret browser MSIE. Do not forget also correctly to specify the codepage - windows-1251 - both in a body of the aspx-page, and in a configuration file of the web - application (web.config). For example: <globalization requestEncoding = "windows-1251" responseEncoding = "windows-1251"/> Besides the basic file default.aspx, in the application files post.aspx, a containing code for sending the message in a forum, and postbody.aspx in which the text of the message chosen the user will be deduced{removed} will be used. Basic patterns HTML Let's start with a spelling of a DHTML-code which will be used at rendering a forum. Two basic elements of a forum is a message without answers and the message with answers (having clicked on which icon, it it is possible to open all tree of affiliated messages). It will be most convenient to make the given elements as tables. As all "events" of a forum (as, for example, disclosing of a branch of messages, creation of the answer to the message and so forth) after his rendering will be processed only on the client it is necessary to add the special latent fields in which it will be stored{kept} not displayed on page to a body of each element, but necessary for creation of new messages the information - the unique identifier of messages, the identifier of a subject and so forth. So, the code of the message without answers will look so: <TABLE style = " margin-top:0px; margin-bottom:0px; display:block; width:100 % "> <TR style = " width:100 % "> <TD style = " width:70 %; padding-left:Changepx; "> <IMG src = "clipitem.gif" align=absmiddle onmouseover = " this.style.cursor ='pointer ' "> <SPAN onmouseover = " overPost (this); " class = "TreeRoot" onmouseout = " outPost (this); " onclick = " showPost (' Post | ',' Auth | ',' Date | ','ID | ', this, ' TYPE | ',' TID | ',' Level | '); " style = " left:0; "> Text </SPAN> </TD> <TD id ='Auth | ' style = " width:30 %; text-align:left; " class = "TreeRoot"> Author </TD> <TD id ='Date | ' style = " width:120px; text-align:left; " class = "TreeRoot" nowrap> Date </TD> </TR> </TABLE> The symbol "|" will be replaced during generation with indexes. The method showPost will deduce{remove} the text of the message in staff IFrame - his realization will be considered below. Methods overPost and outPost dynamically change a class of style when above the message passes the cursor of a mousy. For creation of spaces between messages of a different level the element of style padding is used. Word Change specified in the given element, will be dynamically replaced with the necessary space during generation of page. It would be possible to set, certainly, to elements relative positioning (position:relative) and to establish fields in some constant - for example, margin-left:10px. However such way in our case is unacceptable, as it is necessary, that contents of a field with a subject of the message were shifted only, and the others remained motionless. The HTML-code of the message which contains answers, differs only two moments. First of all, in teg IMG it is added obrabotchik events onclick where the index on function TreeExpand revealing the list of affiliated messages is placed. Besides each pattern of the message containing answers, contains open teg DIV (this teg is closed programmno), acting as the container for affiliated messages: <DIV id = " Child | " style = " width:100 %; display:none; “> Accordingly, at generation of a tree of a forum it will be already necessary to close this teg in a code of the generator for each hierarchical level. Besides it as it was already spoken, at otrisovke a forum to each message the latent fields for storage of the additional data connected to this message will be added. Also on page of a forum the heading of messages and a page header of the forum will be placed - their HTML-code does not represent any interest. Navigation on a forum (transition between pages) will be carried out with the help special panel`ki for which rendering we shall use also element HTML table: <TABLE class = "PagesBody" style = " font-size:10pt; font-weight:bold; position:relative; width:100 % "> <TR> <TD style = " font-weight:normal; font-size:8pt; "> Page <strong> pagenumber </strong> from pagecount </TD> <TD style = "padding-left:0px" align = "right" nowrap> <INPUT style = "disptype" id = "Back" class = "ButtonItem" type = "submit" value = " and lt; *lt; Previous " onclick = " goPrevious (); "> <INPUT style = "displaytype" slass = "ButtonItem" type = "submit" value = " Sleduhhaja *gt; *gt; " onclick = " goNext (); "> </TD> </TR> </TABLE> Words pagenumber and pagecount will be dynamically replaced with number{room} of the current page and total of pages, accordingly, at a stage of rendering of page. Also you have for certain noticed, that words are inserted into attribute style cells of the table disptype and displaytype. They will be replaced with instructions{indications} display:none or display:inline, depending on lines of the reasons. For example, if the forum will consist only of one page, buttons will not be deduced{removed} at all, etc. Buttons with which help, actually, navigation on a forum also is carried out, have type submit. After their pressing the data will be automatically sent on the server. It is possible to use also the javascript-instruction document. [formName] .submit () where formName is a name of your form (do not overlook to specify always object document - otherwise this code will be correctly interpreted not with all browsers). Code symbols <i> will be displayed on page HTML as triangular brackets. With the help of functions goPrevious and goNext, actually, navigation between pages also will be carried out. Besides the panel for navigation on page it will be placed also and panel`ka with the buttons, allowing to send the new message in a forum. <TABLE class = "RateBody" style = " font-size:10pt; font-weight:bold; MARGIN-RIGHT: 0px; padding:5 043; POSITION: relative; width:100 % "> <TR> <TD style = “ width:30 % “> <INPUT type = "button" id = "Answer" value = "To answer" class = "ButtonItem" onclick = " reply (); " onmouseover = " this.className ='ButtonItemHover ' " onmouseout = " this.className ='ButtonItem ' " style = "display:none"> </TD> <TD style = " width:70 % "> <INPUT type = "button" value = " the New branch " onclick = " newPost (); " class = "ButtonItem" onmouseover = " this.className ='ButtonItemHover ' " onmouseout = " this.className ='ButtonItem ' "> </TD> </TR> </TABLE> These buttons not so necessarily should have type submit so by their pressing the data on the server will not be sent, but the page for creation of the new message will simply open. The code for opening the given pages will be placed in javascript-functions newPost and reply, accordingly. Use IFrame The text of the message will be deduced{removed} in window IFrame placed between the panel for navigation on a forum and the panel for creation of the new message. The given approach is convenient that, that he allows to not place the text of messages deduced{removed} on it{her} in resulting HTML-page - as it is done{made}, for example, in forums codeproject.com where during rendering a tree of messages contents of the last also are deduced in the special latent fields which are automatically opened when the user clicks on a subject of the message. Such technique simplifies the general{common} structure of elements of management HTML which are used for performance of a forum a little. For example, disappears necessity for a special window in which the text of messages will be deduced{removed}. However the size of resulting HTML-pages considerably increases, that can have a negative effect on productivity of a forum. For this reason in the present{true} realization we also shall use window IFrame. Actually in IFrame the special ASPX-page will be loaded. As parameter the unique identifier of the message, which contents we will be passed this page want to see{overlook}, and in the server code placed on the given page, sample of a body of the message of a database will be carried out. We shall consider this code hardly later when we will address to creation of server scripts. Spelling of client scripts At passage of a mousy on heading of the message, the heading will be allocated with the help of a special framework zasvetki. At click under the given message it will be marked as chosen, that will allow the user to define{determine} easily what from messages he at the moment looks through. For realization of these effects we use such Javascript-functions: function overPost (item) { if (item.className! = "SelectedPost") item.className = "TreeHover"; } function outPost (item) { if (item.className! = "SelectedPost") item.className = "TreeRoot"; } As we see, the class of style will vary only in the event that he is a class of the message chosen the user - in that case appearance of heading of the message will not react in any way to passage above it of the cursor of a mousy. To mark the message as chosen, such function will be used: var selPost; function showPost (topic) { if (selPost! = null) selPost.className = "TreeRoot"; topic.className = "SelectedPost"; selPost = topic; } The variable selPost will store{keep} the link to last message chosen the user. Topic is a link to the message on which the user has clicked a mousy. Due to use of a variable selPost it is possible to guarantee, that during each moment of time only one message will be marked as chosen THE NOTE The code of the given function has been a little bit reduced - she also contains obvious logic for display of the data of the message in window IFrame; the full code of this function can be found in a file forum.js. Besides it, at click on an icon of the message (becoming "plus" or "minus" depending on a situation) the branch of the given subject will be opened. For this purpose such client function will be used: function TreeExpand (item, img) { if (item.style.display == "block") { item.style.visibility = "hidden"; item.style.display = "none"; img.src = "clip\tplus.gif"; } else { item.style.display = "block"; item.style.visibility = "visible"; img.src = "clip\tminus.gif"; } } With the help of the given function subjects of messages will not only be opened, but also to be made dynamic replacement of the icons designating, the given subject is opened or curtailed{turned}. For navigation between pages functions goNext and goPrevious are used. In these functions it is made inkrement numbers{rooms} of the current page. The received value enters the name in latent field CountBox. Functions reply and newPost contain a code forming all messages necessary for creation parameters which will be passed in function performPost. These parameters will consist from identification given the message chosen the user. In a result on their basis the search for opening page post.aspx in which as parameters will be passed will be generated: ? Unique ID the chosen message, ? ID a root subject of the message, ? A level of an enclosure of the chosen message, ? The identifier of a subject which part is the message, ? The text of a subject of the message, the answer on which is the message. (If the message opens a new subject instead of some from these parameters value "-1" will be passed). Function performPost will open in a separate window page for creation of the message (with the help of javascript-function window.open), passing in it{her} these parameters as search as a part of an address bar. Hardly any additional explanatories here are required. For dynamic change of quantity{amount} by that on page function refresh which call occurs by pressing the button such as submit therefore the data are sent on the server will be used. A code of this function: function refresh () { document.getElementById ("PerPageBox") .value = document.getElementById ("PerPage") .value; } Element PerPageBox is the latent field processable on the server. The quantity{amount} is stored{kept} in this field by that, displayed on page. PerPage is element HTML such as Select, allowing the user to choose quantity{amount} that on page. The data transmitted to latent field PerPageBox, will be interpreted by already server script. Designing of a database For the beginning it is necessary to say, that the decision offered{suggested} here allows to place some forums on a Web-resource. Thus for addition of a new forum it is required to bring only in corresponding recording to one of tables of a DB and to place the link to this forum in the main menu of a site. Certainly, similar functionality imposes the print on structure of a DB and though in the demonstration project the example only one forum is resulted, realization is constructed so, that at desire it is possible to add some new forums. Nevertheless, in this case designing of base - is far from being the most complex stage in development. A database - we shall name her forumdb - will consist all of two tables connected on a key field forum_id. So they should look: Figure 2. The circuit of a database. The list of all forums will be stored{kept} in the table forum. Forums are identified on a key field forum_id which also represents itself as foreign key in the basic table of a DB forum_post. With the help of the given field sample of messages of a concrete forum is made. The name of a forum will be stored{kept} in a field name. Where the greater interest is represented with the table forum_post. The structure of the given table is specially optimized under a data storage with hierarchical structure what messages of our forum are. Below I shall list all fields of the given table with brief explanatories: ? forum_post_id - avtoinkremental`noe a field - the identifier of recording. ? author - a name of the author of the message. ? topic - a subject of the message. ? body - the text of the message. ? date - date of creation of the message, such as DateTime. ? answer - in the given field the link to the unique identifier (id) messages will be stored{kept}, the answer on which is the given message. If the message opens a new subject in this field value-1 will be by default written down. ? topic_id - is used to mark all messages belonging to one subject. ? level - marks a level of the message in hierarchy of other messages. For example, the root message of a subject will have a level 1, the answer to him - a level 2, and so on. ? answers - a field such as Bit (analogue Boolean in MS SQL Server), shows, whether there are answers to the message. It will be used for simplification of algorithm of construction of a tree of messages so, messages on which there are no answers, and messages which already someone has had time to answer, will be renderit`sja in a various HTML-code. ? forum_id - as we already spoke, identifies the message with that or other forum. The applicability of these fields becomes more understandable when we will address directly to consideration of algorithm of generation of a HTML-code of a forum. As you have already noticed, in realization considered{examined} here as the SQL-server was used MS SQL Server, however in structure of a database and a code of SQL-searches there is nothing such, that it is impossible to translate with a minimum of complexities on SyBase or Oracle. Creation of SQL-searches First of all it is necessary to be defined{determined} with problems{tasks}. So, messages of a forum will be deduced{removed} page by page, and on each page will be placed no more than ten subjects. As the text of messages will not be included in a resulting code of page, that, accordingly, he should not be included and in sample of the data of the table forum_posts. Besides, as we were already defined{determined}, the given table can contain messages for any quantity{amount} of forums, accordingly, sample of messages will be made on a field forum_id. At last, on the basis of the data received as a result of search, it will be filled dataset, and the basis cycle of generation of a forum will occur at the disconnected connection to a database. Therefore the basic search should make sample at once all messages for the current page - that is, for example, ten root messages with the first level of an enclosure and all messages which are answers to them. So, for these purposes we can use such search. SELECT id, author, topic, date, answer, topic_id, level, answers FROM forum WHERE topic_id IN (SELECT TOP X topic_id FROM forum WHERE forum_id = @forum AND level = 1 AND topic_id NOT IN (SELECT TOP Y topic_id FROM forum WHERE forum_id = @forum AND level = 1 ORDER BY id DESC) ORDER BY id DESC) ORDER BY level ASC, id DESC The variable @forum will define{determine}, from what forum sample of messages is made at the moment. X it will be dynamically replaced with quantity{amount} with that on page (which calculation on the party of the client has been shown above), Y - on the number calculated under the formula [number{room} of the current page] * [quantity{amount} by that on page]. Thus, if the first page (i.e. actually number{room} of page is equal to zero) instead of X it will be substituted 0 - and so on is current. Sorting upside-down on a key field is necessary that messages were built in that order in which they have been written down in the table of a forum. Sorting upside-down on a field level is necessary for job of the generator of a forum which will be considered below. He does not use T-SQL, does not block the table at the moment of sample of the data, does not create time tables. Necessity of two enclosed SELECT'ov speaks in this case that, that is necessary to return some set quantity{amount} to those with any quantity{amount} of messages - i.e. the given search can return any quantity{amount} of lines of the table basically. His radical difference also consists in it from classical realization of paginal sample without use specific T-SQL. At it two searches are two important lacks. The first lack consists that last enclosed SELECT with transition to each new page will make sample of the increasing number of messages - so, at show of the first page the quantity{amount} of messages will be equal to zero, at show of the second page - already to ten, at show of the third - to thirty and so forth. Basically, in this case loss of productivity will be not so critical, because above mentioned SELECT makes sample only one field and only root messages of one forum. The second lack is an absence of caching of plans of search at use of sample with the help of keyword TOP. The matter is that though in vysheoznachennom an example that on page we have designated number{room} of the current page and quantity{amount} as X and Y, actually these sizes it is impossible parametrizirovat` (as it is done{made}, for example, for the identifier of a forum), and the search should be generated dynamically in a code. Advantage of this search consists that he will work on the majority of SQL-servers. At desire it is possible to optimize, certainly, the given search, however a subject of a paginal conclusion of the data from the table - and it is especial with reference to various SQL-servers - deserves separate big clause{article}, therefore here we shall not concern her in detail. We shall result only an example of how it is possible to copy this search, using specific T-SQL and the time table: DECLARE @id int, @min_id int, @start_record int SELECT @start_record = (@page - 1) * @page_size + 1 SET ROWCOUNT @start_record SELECT @id = topic_id FROM forum_post ORDER BY topic_id DESC SET ROWCOUNT @page_size SELECT DISTINCT topic_id INTO temp_table FROM forum_post WHERE topic_id <= @id ORDER BY topic_id DESC SELECT @min_id = MIN (topic_id) FROM temp_table DROP TABLE temp_table SET ROWCOUNT 0 SELECT * FROM forum_post WHERE topic_id> = @min_id AND topic_id <= @id ORDER BY level ASC, forum_post_id DESC Besides it, in the basic class of the generator of a forum it is used two more simple searches. One of them will return total to those in the specified forum. Another will make sample of the table forum and to return the name of the current forum on his identifier. These last two searches will be carried out in one package with the first, but only once, at the first opening a forum. Subsequently the data received with the help of these searches, will be written down in the latent fields on page, and when the user will start to travel on pages of our forum, these searches repeatedly to be executed any more will not be. In other classes of our project we shall use some more searches. However they more than are simple, therefore do not demand detailed consideration. For simplicity and presentation we will register all searches directly in a code though such approach hardly should be taken on arms. In the real project the most preferable way is use of stored{kept} procedures. Class ForumGenerator The general{common} data on a class Realization of class ForumGenerator - the basic development cycle of a forum. All parameters necessary for job of this class, will be passed in his designer at creation of a copy. The given class will contain only one open method - string Generate () - not accepting any parameters and a returning full DHTML-code of a forum as a line. The following fields appear in a body of a class: string slider, sliderDiv, current, forumName; int forum, pageSize; ResourceManager res; The blocks of a DHTML-code received from resources of assembly through the manager of resources (res) will be stored{kept} in fields slider and sliderDiv. The applicability of other fields becomes clear from the description of the designer of a class which looks so: public ForumGenerator (string forumName, string current, int forum, int pageSize) { res = new ResourceManager (" RSDNMag. Forum. Resources. Forum ", this. GetType () .Assembly); this.pageSize = pageSize; this.forum = forum; this.forumName = forumName; if (current! = String. Empty) this.current = current; else this.current = "0"; } As parameter forumName contents of latent field StateBox which at the first creation of a copy of a class will be empty, and accordingly will be passed, as the given parameter the empty line (String. Empty) will be passed. Similarly and with parameters pageSize and current - they represent contents of fields PerPageBox and CountBox which will be filled through a client script, that was already considered{examined} by us earlier. As parameter forum identification number{room} of a forum under the table forum will be passed. Besides public method Generate the class also will contain some the closed auxiliary methods and two classes which will be necessary at construction of a tree of messages of a forum. Objective model of messages Necessity of construction of a tree of messages in this case can seem not so obvious, however actually she can strongly help us during generation of a forum. All the matter is that on the one hand, we should form already a DHTML-code of all messages that generation was not broken into two stages - creation of a tree of messages and actually generation DHTML on the basis of this tree. But on the other hand, we still should have opportunity of access to some data associated with each message and as after final formation of a DHTML-code for the message this last will represent a usual line such performance of a problem can become a little bit inconvenient. Therefore to simplify to themselves a life, we shall create the special class representing the message on which there are no answers. The given class will be switched on in a body of basic class ForumGenerator as it will not be used anywhere outside the generator of a forum: class Post { string source; public Post (string source, int id) { this.source = source; _iD = id; } private int _iD; public int ID { get {return _iD;} } public override string ToString () { return source; } } As you see, in a class the method string ToString () is overloaded - so that with his help it was possible to receive DHTML-contents of the given message. Convenience of such approach becomes clearer further when we shall consider realization of the second class used at construction of a tree of messages. The above-stated realization of class Post as it is simple to guess, can contain only given for the single message on which there are no answers. If the message is the beginning of the whole subject or separate discussion inside a subject it should contain as well links to affiliated messages. For such messages we should create a separate class - successor Post: class TopicPost: Post { public TopicPost (string source, int id): base (source, id) { _childNodes = new ArrayList (); } private ArrayList _childNodes; public ArrayList ChildNodes { get {return _childNodes;} } public override string ToString () { StringBuilder result = new StringBuilder ();
foreach (object o in _childNodes) result. Append (((Post) o) .ToString ());
// Opening teg DIV is in a pattern return base. ToString () + result + "</DIV>"; } } The main innovation in class TopicPost in comparison with a parental class is property ChildNodes, inkapsulirujuhhee a field in which links to all affiliated messages will be stored{kept}. Affiliated messages can be both single, and containing answers and consequently, will be submitted in our tree by various objects. Use in this case a typified collection is inexpedient, therefore the given property has been declared as ArrayList. One more important innovation is more complex in comparison with the previous class realization of method ToString. The matter is that if the similar method of a class of the single message should return a DHTML-code only one message in this case we need to receive a DHTML-code of all affiliated messages. Besides, as it was already spoken at the description of the pattern used at rendering of the message with answers, all affiliated messages should be framed tegom DIV. It is the easiest to make it, using model offered{suggested} here. Now if you, for example, will call method ToString the root message of the long and ramified subject receive correctly grouped DHTML-code of all messages of the given subject (I think, hardly who will have doubts, it is what is it valid so). We shall make a call of method ToString only once, at the latest stage of generation of a forum. However it yet all. The current model allows to create hierarchical a tree most, apparently, convenient for our purposes, however possessing one serious lack - to touch all elements of a tree, it is necessary to make perebor elements of all collections in repeatedly enclosed cycles, that not only will lower productivity of the generator, but also will make length of a branch limited. However taking into account, that in this case we shall not use the certain initially set hierarchical structure, but independently to form own, this problem is quite decided{solved}. Everything, that it is necessary to make, is to write the method representing all enclosed classes of a collection as the linear list which elements can be touched in one cycle. Realization of the given method is switched on in class TopicPost and looks so: public ArrayList GetEnumeration () { ArrayList enumeration = new ArrayList (); enumeration. AddRange (_childNodes); foreach (object o in _childNodes) { if (o. GetType () == typeof (TopicPost)) enumeration. AddRange (((TopicPost) o) .GetEnumeration ()); } return enumeration; } GetEnumeration is the recursive function returning the list of all affiliated branches. Thus, we receive the simple mechanism for perebora all members of a collection. The basic algorithm of generation of a forum Now we can already proceed{pass} to a spelling of the main method of class ForumGenerator - method Generate. In him auxiliary methods will be used some: ? A method initialize, making sample of patterns DHTML of a file of resources of assembly in fields slider and sliderDiv; ? A method buildQuery, generating SQL-searches, which examples were resulted earlier; ? Methods rootBuild and nodeBuild which form a DHTML-code of root and single messages on the basis of patterns which contain in fields slider and sliderDiv; ? And, at last, a method buildResult, a forming full code of a forum - including headings, elements of management, window IFrame and so forth. Realization of these methods is quite obvious, and its{her} many aspects was already mentioned above. Actually they contain auxiliary logic of the generator of the forum, not representing special interest. Here we shall consider the main algorithm of the generator. Below I shall result all code of method Generate - how he is realized in the demonstration project applied to clause{article}: public string Generate () { initialize (); SqlConnection connection = new SqlConnection (ConfigurationSettings. AppSettings ["ConnectionString"]); string query = buildQuery (); string source = String. Empty; ArrayList topics = new ArrayList (10); Hashtable topicNumbers = new Hashtable (10); SqlDataAdapter adapter = new SqlDataAdapter (query, connection); adapter. SelectCommand. Parameters. Add ("@forum", forum); DataSet dataSet = new DataSet ("sender"); adapter. Fill (dataSet); DataTable table = dataSet. Tables [0]; int i = 0; foreach (DataRow r in table. Rows) { if ((Int32) r ["level"] == 1) { if ((Boolean) r ["answers"]) source = rootBuild (i, r); else source = nodeBuild (i, r); Post rootData; if ((Boolean) r ["answers"]) rootData = new TopicPost (source, (Int32) r ["forum_post_id"]); else rootData = new Post (source, (Int32) r ["forum_post_id"]); topics. Add (rootData); topicNumbers. Add (r ["topic_id"], i); } else { Post msgData;
if (! (Boolean) r ["answers"]) msgData = new Post (nodeBuild (i, r), (Int32) r ["forum_post_id"]); else msgData = new TopicPost (rootBuild (i, r), (Int32) r ["forum_post_id"]); int value = (Int32) topicNumbers [r ["topic_id"]];
TopicPost topicPost = (TopicPost) topics [value];
if ((Int32) r ["level"] == 2) topicPost. ChildNodes. Add (msgData); ArrayList enumeration = topicPost. GetEnumeration (); foreach (object o in enumeration) { if ((Int32) r ["answer"] == ((Post) o) .ID) { ((TopicPost) o) .ChildNodes. Add (msgData); break; } } } i ++; } return buildResult (topics, dataSet); } Here to not do without several comments. On the basis of resulted before searches it is filled dataset. If the forum opens for the first time dataset will consist of three tables: the first is, actually, messages of a forum, and the second and the third contain total of root messages of a forum and the name of a forum taken from the table forum. If the forum is simply glanced over by the user dataset will consist all of one table with messages. The basic part of a code of a method is engaged in construction of a tree of messages on the basis of the data which contain in the first table dataseta. At construction of a tree the main part is played with two fields - topics such as ArrayList and topicNumbers such as Hashtable. The field topics is a collection with the length equal to quantity{amount} by that, displayed on page simultaneously. Objects of types Post will be stored{kept} in the given collection and TopicPost. The sense of a collection topics consists that, due to her, the order of following of messages is saved. As (and about it it was already spoken) the list of messages in the table dataseta is sorted on increase of a field level among the very first messages there are root messages, i.e. those, answers on which are all other messages of the given page of a forum, and all others will be located in decreasing order their index, than we and shall use at generation. Therefore at an initial stage of construction of a tree all collection topics will be entirely (or nearly so entirely if, for example, in all a forum less than ten subjects) are filled with root messages that. If on the root message there are no answers, and for his performance in a collection the copy such as Post all his rendering will be completed at this first stage was used and, it is possible to say, that it will not participate in the further generation. If on the message there are answers (that is defined{determined} by contents of a field answers such as Bit) for his performance the copy such as TopicPost which as we remember, in itself can contain a collection of affiliated elements will be used. And in the given collection messages of the second and deeper levels of an enclosure will be added. Here, certainly, not all so is simple. When the second stage of generation (his code contains in the block else) first of all it is necessary to define{determine}, a part what of subjects message considered{examined} at the moment (or begins is, speaking in other words, what index of the given subject in a collection topics). To use for these purposes a field answer, containing the link on forum_post_id that message, the answer on which is considered{examined}, it is impossible, as if the first stage of generation is made only for messages of the first level the second stage - for messages of all other levels, and to us should to use repeatedly enclosed cycles for this purpose. Here to us there comes to the aid beforehand certain field topic_id which is at each message and which allows associirovat` it with this or that subject. To optimize search of the necessary subject in a collection topics, the khehsh-table topicNumbers is used. At the first stage of generation in this khehsh-table indexes of all subjects added in a collection topics will be worn out, and as a key acts, certainly, topic_id this subject. Therefore everything, that is necessary for us at the second stage is simply to read the necessary value from the khehsh-table. It is done{made} with the help of a line of a code: int value = (Int32) topicNumbers [r ["topic_id"]]. In result in a field value there will be an index of a subject in a collection topics - i.e. that we and needed to receive. But, besides, difficulties on it do not come to an end. Now we precisely know, to what from subjects the considered{examined} message belongs and what to search for it it is necessary, say, in that collection, the link on which contains, for example, in topics [0]. The area of search, certainly, is narrowed, however all the same remains not such narrow as it would be desirable. Unfortunately, the knowledge of a level of an enclosure of the considered{examined} message already in any way will not help us. In fact at the same level there can be a set of different messages, each of which can begin separate podtemu. But here method GetEnumeration which realization was considered{examined} above to the aid will come. Due to him it is possible to present all hierarchical collections of messages of the given subject as one flat collection, iteration on which to make already absolutely simply. So last stage of generation of a tree of messages comes to an end. Now it is necessary to call only method ToString all root thematic messages - and we shall receive completely generated DHTML-code of page of a forum. Viewing and sending of the message Page for viewing messages Basically, all code of this page is extremely simple. By its{her} development the visual editor of Web-forms at all is not required. It is necessary to create only the container (DIV), processable on the server, or any other element which you will consider the most convenient, and to write a method which will be caused at loading page: private void Page_Load (object sender, System. EventArgs e) { if (Request. QueryString ["id"]! = null) { string query = " SELECT body FROM forum_post WHERE forum_post_id = @id ";
SqlConnection connection = new SqlConnection (ConfigurationSettings. AppSettings ["ConnectionString"]); using (SqlCommand command = new SqlCommand (query, connection)) { command. Parameters. Add ("@id", Int32. Parse (Request. QueryString ["id"])); connection. Open ();
SqlDataReader reader = command. ExecuteReader (); reader. Read (); ForumText. InnerHtml = reader. GetString (0); reader. Close (); } connection. Close (); } } ForumText is element HTML in which the text of the message will be deduced{removed}. All code contained in the given method, will be carried out only when the parameter will be transferred{handed} to page in a line of search forum_id. Otherwise the page will remain empty (as it occurs when the tree of messages of a forum, for example, is simply loaded). The everything else, basically, and so obviously - with the help of the elementary search contents of a field body from the table forum_post - from that line which corresponds{meets} set forum_id will be taken. Page for sending messages This page will consist of several elements of management which are the most convenient for creating in the visual designer. Appearance of these elements of management can be defined{determined} directly in the editor of properties, but it is better to create corresponding CSS-classes all the same. THE NOTE M.MakDonald in the fundamental work « ASP.NET: complete reference » asserted{approved}, that the opportunity to work with server elements of management as with usual control-ami, say, in Windows Forms is serious break in Web-development even in comparison with CSS. For example, now it is possible to specify the sizes of any element of management in the editor of properties that will allow to save time which leaves on creation of the table of styles or inlajnovoe the instruction{indication} of style elements. To be convinced, as far as serious break is this advantage ASP.NET, advise to define{determine} the sizes of any element of management thus and to see the turned out page through browsers Mozilla, Opera or Netscape. For certain strange reasons all data on the sizes elements of management will not be included at all in a resulting DHTML-code. So, on the form three text fields - for input of a name, a subject of the message and the text of the message should be placed. Besides it is necessary to add buttons OK and Cancel. check of contents of all three text fields located on the form, with the help of components such as RequiredFieldValidator will be made also. For a conclusion of total messages validatorov component ValidationSummary will be used. In a result the form will look approximately so: At loading the form method Page_OnLoad will be caused. The code of the given method will read out searches from an address bar with which help the given form has been called, and also to calculate the general{common} quantity{amount} that in the specified forum (in the event that the created message is not the answer to any other message and, accordingly, opens a new subject). Also in that case when the message is the answer, his heading will be dynamically formed on the basis of heading of the message on which the answer is made, with the prefix added to it « Re: ». So the code of the given method will look: private void Page_Load (object sender, System. EventArgs e) { if (Request. QueryString ["topic"]! = String. Empty **! this. IsPostBack) Topic. Text = " Re: " + Request. QueryString ["topic"];
level = Int32. Parse (Request. QueryString ["level"]);
if (Request. QueryString ["topicID"] == "-1") { SqlConnection connection = new SqlConnection (ConfigurationSettings. AppSettings ["ConnectionString"]); string query = " SELECT count (*) FROM forum_post WHERE answer =-1 ";
using (SqlCommand command = new SqlCommand (query, connection)) { command. Connection. Open (); topicID = (int) command. ExecuteScalar (); } connection. Close (); } else topicID = Int32. Parse (Request. QueryString ["topicID"]); } By pressing button OK method OK_Click and, accordingly, the given button server element of management will be caused should represent. By pressing the button the Cancellation the window will be simple to be closed, i.e. to be carried out a javascript-code window.close () and, accordingly, the given button should represent usual element HTML and be processed only on the client. A code which will be executed by pressing button OK: private void OK_Click (object sender, System. EventArgs e) { string query = " UPDATE forum_post SET answers = 1 WHERE forum_post_id = @answer; INSERT INTO forum_post (author, topic, body, date, answer, forum_id, topic_id, level) VALUES (@author, @topic, @body, @date, @answer, @forum, @topicID, @level) "; SqlConnection connection = new SqlConnection (ConfigurationSettings. AppSettings ["ConnectionString"]); using (SqlCommand command = new SqlCommand (query, connection)) { command. Connection. Open (); command. Parameters. Add ("@author", parse (AuthorName. Text)); command. Parameters. Add ("@topic", parse (Topic. Text)); command. Parameters. Add ("@body", parse (Msg. Text)); command. Parameters. Add ("@date", DateTime. Now); command. Parameters. Add ("@level", level + 1); command. Parameters. Add ("@answer", Request. QueryString ["answer"]); command. Parameters. Add ("@forum", Request. QueryString ["ID"]); command. Parameters. Add ("@topicID", topicID); command. ExecuteNonQuery (); } connection. Close (); Response. Redirect ("include\send.htm"); } Apparently, in the event that the created message is the answer, the code will make changes to that message on which the user that at the subsequent reading the given message from dataseta it was possible to define{determine} at once responds, that on him there are answers. In a method parse, returning value such as string, there is a check of the text of the message. In particular, symbols "<" i ">" are replaced with codes of these symbols | |