[译]特警-一个简单的基于Web的异常跟踪器-第8部分
By robot-v1.0
本文链接 https://www.kyfws.com/applications/swat-a-simple-web-based-anomalies-tracker-part-8-zh/
版权声明 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
- 12 分钟阅读 - 5744 个词 阅读量 0[译]特警-一个简单的基于Web的异常跟踪器-第8部分
原文地址:https://www.codeproject.com/Articles/5046/SWAT-A-simple-Web-based-Anomalies-Tracker-Part-8
原文作者:Al Alberto
译文由本站 robot-v1.0 翻译
前言
An account of my experience in learning to develop in the .NET environment 介绍了我在.NET环境中学习开发的经验
- 下载SWAT源文件-68.2 Kb(Download SWAT source files - 68.2 Kb)
- 下载SQL脚本文件-3.76 Kb(Download SQL script files - 3.76 Kb)
- 下载SwatChartLib源文件-9.13 Kb(Download SwatChartLib source files - 9.13 Kb)
图1特警的分析页面(Fig.1 Swat’s Analysis Page)
特警第8部分(Swat Part 8)
这是倒数第二篇特警文章.仅完成一个第1部分中描述的目标而已.我将与您分享一个秘密,写文章比编写原始应用程序要困难得多.但是我想那也是一种学习经历.这很有趣,我希望它能为读者带来一些好处.也许应用程序本身已经找到了一些可以运行的服务器.如果有人使用它并进行了任何增强,请告诉我,以便每个人都可以受益或更好地撰写文章!(This is the penultimate SWAT article. Only one more to complete the goals described in part 1. I’ll share a secret with you, writing the articles was way harder than coding the original application. But I suppose that’s also a learning experience. It’s been fun and I hope that it’s provided some benefit to the readers. Perhaps the application itself has found a few servers to run on. If anyone is using it and has made any enhancements please let me know so everyone can benefit or better yet write an article!) 关于安装最新版本的快速说明.如果您一直在使用该应用程序并且数据库中有数据,请确保在安装新版本和运行脚本之前导出数据.只是为了安全起见.(A quick note on installing the latest version. If you have been using the application and have data in the database make sure you export the data before installing the new version and running the scripts. Just to be on the safe side.) 对于那些正在阅读第一篇特警文章的人来说,这是一句话.本系列文章描述了我作为学习项目设计的应用程序的开发.该项目的目的是获得在.NET环境中进行开发的经验.我给自己的目标是定义一个基于Web的应用程序,然后使用ASP.NET开发该应用程序.文章介绍了我针对该应用程序的实现解决方案.正在开发的应用程序是功能齐全的错误跟踪应用程序.(Here’s the verbage for those who are reading this as the first SWAT article. This series of articles describe the development of an application I devised as a learning project. The purpose of the project was to gain experience developing in the .NET environment. The goal I had given myself was to define a web-based application and then develop the application using ASP.NET. The articles describe my implementation solution for the application. The application being developed is a full-featured bug tracking application.)
- 特警第1部分(Swat Part 1)
- 特警第二部分(Swat Part 2)
- 特警第3部分(Swat Part 3)
- 特警第4部分(Swat Part 4)
- 特警第5部分(Swat Part 5)
- 特警第6部分(Swat Part 6)
- 特警第7部分(Swat Part 7)
特警的分析功能(SWAT’s Analysis Feature)
上次我们几乎击败了分析功能的原因.因此,让我们开始做正事.该页面上的编码没有太多内容,因为大部分工作都移交给了存储过程和制图控件.我要指出的唯一"整洁"功能是,映像是动态创建的,它们不是存储在服务器上的静态映像.(Last time we pretty much beat to death the reasons for the analysis feature. So let’s get right down to business. There’s not much to the coding on this page since most of the work is relegated to the stored procedures and the charting control. About the only ‘neat’ feature that I can point to is the fact that the images are being created dynamically, they are not static images that are stored on the server.) 将一个新的WebPage添加到项目,并将其命名为SwatAnalyse.参考图1并添加控件并设置它们的属性,如下所示.此页面将"驱动"渲染.我们将在下面添加另一个页面,该页面将生成图像(在制图控件的帮助下).(Add a new WebPage to the project and name it SwatAnalyse. Refer to Fig.1 and add the controls and set their properties as shown below. This page will ‘drive’ the rendering. We’ll add another page below that will generate the image (with help from the charting control).) |控制(Control) |ID(ID) |文本(Text)
Label |
||
lblSelChart |
||
选择图表(Select Chart) | ||
Label |
||
lblSelProject |
||
选择项目(Select Project) | ||
Label |
||
lblSelModule |
||
选择模块(Select Module) | ||
Label |
||
lblSelSeverity |
||
选择严重性(Select Severity) | ||
Label |
||
lblMessage |
||
Button |
||
btnGetChart |
||
获取图表(Get Chart) | ||
|控制(Control) |ID(ID) |宽度(Width) |高度(Height) |图(ImgUrl)
Image |
||||
imgSwatChart |
||||
488 | ||||
328 | ||||
SwatChart.aspx | ||||
|控制(Control) |ID(ID)
DropDownList |
|
ddlChart |
|
MasterDetail.MasterDDL |
|
ddlProjects |
|
MasterDetail.DetailDDL |
|
ddlModules |
|
DropDownList |
|
ddlSeverity |
|
您会注意到我们已经利用了(You’ll notice that we’ve made use of the) MasterDetailDDL
我在上一篇文章中提到的.确保已添加对该控件的引用,并将其添加到"工具箱"中.该控件将允许用户对图表进行所有选择,而不必前往服务器.放置"(that I mentioned in the last article. Make sure sure that you have added a reference to the control and that you added it to your Toolbox. This control will allow the user to make all selections for the chart without having to make a trip to the server. Place the ‘) lblMessage
控件位于图像控件上方,并将其大小设置为较宽.从图1的图像中您无法分辨出标签的空间.但这是因为我必须对其进行一些编辑以适应CP要求.您还需要将"可见"属性设置为"假".我们将使用(’ control above the image control and size it as wide. You can’t tell from the image in Fig.1 that there’s room there for a label. But that’s ‘cause I had to edit it a little to fit the CP requirements. You will also need to set the ‘Visible’ property to ‘false’. We’ll use the) lblMessage
控件在需要时向用户显示消息.让我们开始,添加以下内容(control to present a message to the user when needed. So let’s begin, add the following) enum
到(to the) SwatAnalyse
类定义.这定义了我们将要实现的四种基本图形类型.(class definition. This defines the four basic graph types that we’ll implement.)
public enum GraphMode
{
GraphModuleStatus = 1,
GraphProgress,
GraphActivity,
GraphProjStatus
}
与其他所有页面一样,我们需要检查(As with all the other pages, we need to check during) Page_Load()
如果用户实际上具有访问此页面的特权.然后,我们要做页面需要做的所有内务处理.这些评论表明发生了什么.(if the user actually has privileges to access this page. Then we do whatever housekeeping needs to be done for the page. The comments indicate what’s happening.)
private void Page_Load(object sender, System.EventArgs e)
{
int nRole = (int)AccessPrivilege.Manager;
if (Request.Cookies["Roles"] != null)
{
nRole = System.Convert.ToInt16(Request.Cookies["Roles"].Value);
if ((int)AccessPrivilege.Manager !=
(nRole & (int)AccessPrivilege.Manager))
Response.Redirect("SwatBugs.aspx",true);
}
else
Response.Redirect("SwatLogon.aspx",true);
//We know we have this because the user is logged in
if (Request.Cookies["UserID"] != null)
{
Response.Cookies.Add(Request.Cookies["UserID"]);
Response.Cookies["UserID"].Expires = DateTime.MaxValue;
}
if (!Page.IsPostBack)
{
//Dynamically adding an item to a DropDownList...
//Add an "All" selection to the severity combo
ListItem ls = new ListItem("All","0");
ddlSeverity.Items.Add(ls);
ddlSeverity.SelectedIndex = System.Convert.ToInt32(
ddlSeverity.Items.IndexOf(ddlSeverity.Items.FindByText("All")));
//Populate the projects combo
BindProjectCB();
//Set a default chart
ViewState["mode"] = (int)GraphMode.GraphModuleStatus;
imgSwatChart.Visible = false;
//First time, tell user to select something
lblMessage.Text = "Select options to plot.";
lblMessage.Visible = true;
}
else
{
imgSwatChart.Visible = true;
//Populate the projects combo
BindProjectCB();
lblMessage.Visible = false;
}
}
的(The) MasterDetailDDL
控件要求我们创建一个要显示的主/从信息表.在我们的例子中,数据库表是以控件期望它们的方式配置的,因此我们不需要执行其他任何操作.如果没有,我们将需要进行表联接以获取所需的配置. MasterDetailDDL文章对此进行了讨论并提供了示例.(control requires that we create a table of the master/slave information to be displayed. In our case the database tables are configured the way the control expects them so we don’t need to do anything else. If they weren’t, we would need to do a table join in order to get the required configuration. The MasterDetailDDL article discusses this as well as providing an example.)
protected void BindProjectCB()
{
SqlConnection cnn;
SqlCommand cmd;
SqlDataReader dr;
string ConnectionString = ConfigurationSettings.AppSettings["dbconn"];
cnn = new SqlConnection(ConnectionString);
cmd = cnn.CreateCommand();
cnn.Open();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "SWATGetAllProjects";
dr = cmd.ExecuteReader();
ddlProjects.DataSource = dr;
ddlProjects.DataTextField = "itemname";
ddlProjects.DataValueField = "id";
ddlProjects.DataBind();
dr.Close();
if (Response.Cookies["Project"].Value != null)
ddlProjects.SelectedIndex = ddlProjects.Items.IndexOf(
ddlProjects.Items.FindByValue(Response.Cookies["Project"].Value));
else
ddlProjects.SelectedIndex = 0;
cmd.CommandText = "SWATGetAllModules";
SqlDataAdapter da = new SqlDataAdapter(cmd);
//Setup the MasterDetail DropDownList control
DataSet ds = new DataSet("ProjMod");
da.Fill(ds,"MODULES");
Array Data;
Data = Array.CreateInstance(typeof(object),1,3);
Data.SetValue("ddlModules",0,0);
Data.SetValue(ds,0,1);
Data.SetValue("MODULES",0,2);
ddlModules.TableName = "MODULES";
ddlProjects.SlaveData = Data;
cnn.Close();
}
嵌入式页面(Embedded Pages)
因此,当浏览器加载(So when the browser loads the) SwatAnalyse
页面上,它看到控件之一是需要加载的图像.它读取图像的URL,然后继续从指定位置加载项目.通常情况下,图像会存在一条路径.在我们的例子中,我们实际上是在引用一个ASP页面.当请求该页面时,该页面上的代码将动态生成图像并将其返回到浏览器.浏览器不知道或不在乎它不是它所指向的图像URL.我想它所关心的是,无论返回的结果如何,最好采用它会处理的格式.(page it sees that one of the controls is an image which needs to loaded. It reads the URL of the image and proceeds to load the item from the specified location. Normally there would be a path to where the image is located. In our case we’re actually referencing an ASP page. When that page gets requested the code on that page will dynamically generate an image and return it to the browser. The browser doesn’t know or care that it’s not an image URL that it’s pointing to. I guess all it cares is that whatever is returned better be in a format it knows how to handle.)
到目前为止,这一切都非常简单,但是要实现我们想要的目标,还需要一些技巧.为了使引用的页面生成图像,我们需要它需要知道(That’s all pretty straight forward so far, but there’s a little bit of trickery required in order to achieve what we want. In order for the referenced page to generate the image we want it needs to know)什么(what)这就是我们想要的.这意味着我们必须将一些参数传递给页面以告诉我们要生成什么.这就是我们现在需要做的.为"添加事件处理程序(it is that we want. That means that we have to pass some parameters to the page to tell it what we want generated. And that’s what we need to do now. Add an event handler for the ‘) GetChart
按钮并进行如下修改.(’ button and revise as follows.)
private void btnGetChart_Click(object sender, System.EventArgs e)
{
StringBuilder strParameters = new StringBuilder("SwatChart.aspx?mode=");
strParameters.Append(ddlChartType.SelectedItem.Value);
strParameters.Append("&proj=");
strParameters.Append(ddlProjects.SelectedItem.Value);
strParameters.Append("&mod=");
strParameters.Append(ddlModules.SelectedItem.Value);
strParameters.Append("&sev=");
strParameters.Append(ddlSeverity.SelectedItem.Value);
imgSwatChart.ImageUrl = strParameters.ToString();
}
基本上,我们正在做的是打包用户选择,以将其命名为"参数"传递给图像URL.(Basically what we are doing is packaging up the users selection to pass as named ‘arguments’ to the image URL.)
图像就是一切(Image is everything)
将另一个网页添加到项目并命名(Add another WebPage to the project and name it) SwatChart
.该页面将被调用以进行图表的实际呈现(在制图控件的帮助下).换句话说,它将返回一个图像文件.(. This is the page that will be called to do the actual rendering of the chart (with help from the charting control). In other words it will return an image file.)
因此,让我们从(So let’s start with the) Page_Load()
.由于此页面需要一些参数,因此我们要做的第一件事是检查" mode"参数以查看要生成的图表.如果没有得到该命名的参数,那么我们将仅返回一条消息,指出这一点.否则,我们仅调用适当的方法来生成用户选择的图表.(. Since this page is expecting some arguments, the first thing we do is check for the ‘mode’ argument to see which chart we are to generate. If we didn’t get that named argument then we just return a message indicating so. Otherwise we just call the appropriate method to generate the chart the user selected.)
private void Page_Load(object sender, System.EventArgs e)
{
Response.Clear();
//What's the request
if (Request.QueryString["mode"] != null &&
Request.QueryString["mode"].Length != 0)
{
string strMessage="";
int nMode = Convert.ToInt16(Request.QueryString["mode"]);
switch(nMode)
{
case (int)GraphMode.GraphModuleStatus:
if (DoGraphModuleStatus(ref strMessage) == false)
DisplayMessage(strMessage);
break;
case (int)GraphMode.GraphProgress:
if (DoGraphProgress(ref strMessage) == false)
DisplayMessage(strMessage);
break;
case (int)GraphMode.GraphActivity:
if (DoGraphActivity(ref strMessage) == false)
DisplayMessage(strMessage);
break;
case (int)GraphMode.GraphProjStatus:
if (DoGraphProjectStatus(ref strMessage) == false)
DisplayMessage(strMessage);
break;
}
}
else
DisplayMessage("Sumpin' went wrong!");
}
四种制图方法都以一种稍微不同的方式进行相同的操作.每个实例都随需要从数据库加载的数据以及我们要实例化的图表对象类型而有所不同.因此,我仅描述其中一个,您可以从下载中查看其他内容,以了解任何特定的兴趣.(All four charting methods sorta kinda do the same thing in just a slightly different way. Each just varies by the data that needs to be loaded from the database and which chart object type we’re going to instantiate. So I’m just going to describe one of them and you can look at the others from the download for any specific interest.) 进度图希望绘制数据库中每天可用的每种状态下存在的错误数.也就是说,从输入错误的第一天(对于指定的项目)到数据库中存在的最后错误更改状态(对于指定的项目).我们基本上想绘制一段时间内打开/修复/关闭的错误总数.因此,我们需要做的第一件事就是确定我们要处理的时间跨度.开始日期很容易,因为我们知道必须先输入错误才能修复或关闭它.结束日期更难获得,因为它可能是三个州中任何一个州的错误.例如,如果今天是最后日期,则可能是今天打开了一个错误,或者可能已经修复或关闭了一个现有的错误.因此,我们必须检查所有三个状态以获取最后的"活动"日期.(The progress graph wants to plot the number of bugs that exist in each state for each day available in the database. That is, from the first day that a bug was entered (for the specified project) until the last bug change state that exists in the database (for the specified project). We basically want to plot the total number of bugs open/fixed/closed over a period of time. So the first thing we need to do is determine the time span we’re dealing with. The start date is easy since we know that a bug has to be entered before it can be fixed or closed. The end date is a little harder to get since it can be a bug in any of the three states. For example if today was the last date, a bug could have been opened today, or an existing bug could have been fixed or closed. So we have to check all three states to get the last ‘activity’ date.) 制图控件要求我们传入定义要绘制的序列的信息.这就是我们下一步要做的.这将告诉控件要绘制多少个系列,以及每个系列要使用的颜色和标签.(The charting control requires that we pass in information defining the series to be plotted. So that’s what we do next. This will tell the control how many series are to be plotted, as well as the color and label to use for each series.) 然后,我们创建一个数组来保存要绘制的数据,实例化适当的图表对象,并告诉它绘制图表.就是这样.仅出于可读性考虑,我们将实际加载数据委托给辅助函数.(Then we create an array to hold the data to be plotted, instantiate the appropriate charting object and tell it to draw the chart. And that’s it. Just for readability, we relegate the actual loading of the data to a helper function.)
private bool DoGraphProgress(ref string strMessage)
{
int nProjID;
if (Request.QueryString["proj"] != null &&
Request.QueryString["proj"].Length != 0)
nProjID = Convert.ToInt32(Request.QueryString["proj"]);
else
{
strMessage = "Select option to plot.";
return false;
}
try
{
SqlDataReader dr;
DateTime dt1 = DateTime.Now;
DateTime dt2 = DateTime.Now;
//Get the database data
//First need to find the date range for the bugs
SqlConnection cnn;
string ConnectionString = ConfigurationSettings.AppSettings["dbconn"];
cnn = new SqlConnection(ConnectionString);
cnn.Open();
SqlCommand cmd = cnn.CreateCommand();
cmd.CommandType = CommandType.StoredProcedure;
//entereddate will give us the start date
cmd.CommandText = "SWATGetFirstEnteredDate";
// Fill our parameters
cmd.Parameters.Add("@projid", SqlDbType.Int).Value = nProjID;
dr = cmd.ExecuteReader();
if (dr.Read())
dt1 = (DateTime)dr[0];
else
{
strMessage = "Nothing in the DB.";
return false; //we can't do anything...
}
//but the end date could be an enteredate, fixeddate, or closeddate
dr.Close();
cmd.CommandText = "SWATGetLastDate";
dr = cmd.ExecuteReader();
if (dr.Read())
{
if (dr[0] != null && !dr[0].Equals(System.DBNull.Value))
dt2 = (DateTime)dr[0];
if (dr[1] != null && !dr[1].Equals(System.DBNull.Value))
{
if (dt2 < (DateTime)dr[1])
dt2 = (DateTime)dr[1];
}
if (dr[2] != null && !dr[2].Equals(System.DBNull.Value))
{
if (dt2 < (DateTime)dr[2])
dt2 = (DateTime)dr[2];
}
//Set the series name and color
Array SeriesInfo;
SeriesInfo = Array.CreateInstance(typeof(object),3,2);
SeriesInfo.SetValue("Open",0,0);
SeriesInfo.SetValue(Color.Red,0,1);
SeriesInfo.SetValue("Fixed",1,0);
SeriesInfo.SetValue(Color.Blue,1,1);
SeriesInfo.SetValue("Closed",2,0);
SeriesInfo.SetValue(Color.Green,2,1);
TimeSpan numDays = dt2 - dt1;
float fMax = 0F;
Array Data;
//We'll store the data as a 2-dim array, the first element of
//each row will be a label, if any. Followed by each series data
Data = Array.CreateInstance(typeof(object),numDays.Days+1,4);
//Get the data from the database and store it in the array
GetProgressData(dt1,dt2,ref Data,ref fMax);
//Create the line chart object
SwatChartLib.SwatLineChart ch = new SwatChartLib.SwatLineChart(
this,400F,300F);
//Set the XAxis label
StringBuilder strDateRange = new StringBuilder();
strDateRange.Append(dt1.ToShortDateString());
strDateRange.Append("-");
strDateRange.Append(dt2.ToShortDateString());
ch.strXaxisLabel = strDateRange.ToString();
//Set the YAxis label
ch.strYaxisLabel = "# of Bugs";
//Draw the graph
ch.DrawChart(SeriesInfo,Data,fMax,1F,1F);
}
cnn.Close();
}
catch(Exception e)
{
strMessage = e.ToString();
return false;
}
return true;
}
这是实际上从数据库加载数据的帮助程序功能.您可以按照注释进行操作,但是基本上我们要做的就是遍历日期范围并获取每种错误状态的错误计数.(And here’s the helper function that actually loads the data from the database. You can follow the comments but basically all we’re doing is iterating through the date span and getting the bug count for each bug state.)
private void GetProgressData(DateTime Start, DateTime End,ref Array Data,
ref float fMax)
{
SqlConnection cnn;
string ConnectionString = ConfigurationSettings.AppSettings["dbconn"];
cnn = new SqlConnection(ConnectionString);
cnn.Open();
SqlCommand cmd = cnn.CreateCommand();
cmd.CommandType = CommandType.StoredProcedure;
DateTime DateCounter = Start;
float fOpen = 0F;
float fFixed = 0F;
float fClosed = 0F;
int nCount = 0;
cmd.Parameters.Add("@currdate", SqlDbType.DateTime);
fMax = 1F;
//Get the number of bugs for each day in the range. We want the
//total for each state.
while(DateCounter <= End)
{
cmd.Parameters["@currdate"].Value = DateCounter;
cmd.CommandText = "SWATGetOpenBugCount";
//No label
Data.SetValue("",nCount,0);
fOpen = System.Convert.ToSingle(cmd.ExecuteScalar());
Data.SetValue(fOpen,nCount,1);
cmd.CommandText = "SWATGetFixedBugCount";
fFixed = System.Convert.ToSingle(cmd.ExecuteScalar());
Data.SetValue(fFixed,nCount,2);
cmd.CommandText = "SWATGetClosedBugCount";
fClosed = System.Convert.ToSingle(cmd.ExecuteScalar());
Data.SetValue(fClosed,nCount,3);
DateCounter = DateCounter.AddDays(1);
nCount++;
//We know the open bugs has to be the largest one
if (fOpen > fMax)
fMax = fOpen;
}
}
当你只有锤子(When all you have is a hammer)
由于我们创建的只是图像,因此我们没有任何设施可以将文本信息返回给用户.例如,在发生错误的情况下.以下方法将消息创建为图像,以便我们可以将消息返回给用户.(Since all we are creating is an image, we don’t have any facilities to return textual information to the user. For example, in the case of an error. The following method creates a message as an image so that we can return a message to the user.)
protected void DisplayMessage(string sMess)
{
Bitmap bmp = new Bitmap(m_nImageWidth, m_nImageHeight,
PixelFormat.Format24bppRgb);
Graphics g = Graphics.FromImage(bmp);
g.SmoothingMode = SmoothingMode.None;
g.TextRenderingHint = TextRenderingHint.SystemDefault;
g.Clear(Color.White);
System.Drawing.RectangleF rcMess = new System.Drawing.RectangleF();
Font fontMess = new Font("Arial", 16);
SizeF szMess = g.MeasureString(sMess,fontMess);
rcMess.Size = szMess;
rcMess.X = (m_nImageWidth - szMess.Width)/2;
rcMess.Y = (m_nImageHeight - szMess.Height)/2;
g.DrawString(sMess
,fontMess
,new System.Drawing.SolidBrush(Color.Black)
,rcMess);
MemoryStream stream = new MemoryStream();
bmp.Save(stream, ImageFormat.Png);
this.Response.Clear();
this.Response.ContentType = "image/png";
this.Response.BinaryWrite(stream.ToArray());
g.Dispose();
this.Response.End();
}
登出功能(Logout Feature)
特警工具栏上有一些尚未实现的按钮.其中之一是"注销"按钮.让我们快速实现这一点以结束本文.添加一个新网页并命名(There are a few buttons on the SWAT toolbar that have not been implemented. One of them is the ‘Logout’ button. Let’s quickly implement that to end this article. Add a new Web page and name it) SwatLogoff
.该页面不需要做很多事情,只需从客户端计算机上删除我们的身份验证cookie.我还添加了一个脚本,该脚本将提示用户并关闭"应用程序"窗口.这是代码(. This page doesn’t need to do much, only to remove our authentication cookie from the client’s machine. I also added a script that will prompt the user and close the ‘application’ window. Here’s the code for the) Page_Load()
事件.(event.)
private void Page_Load(object sender, System.EventArgs e)
{
Response.Cookies.Clear();
FormsAuthentication.SignOut();
StringBuilder strScript = new StringBuilder();
strScript.Append(
"<SCRIPT FOR=window EVENT=onload LANGUAGE="\""JScript\">\n");
strScript.Append("top.close();\n");
strScript.Append("</SCRIPT>");
Page.RegisterClientScriptBlock("ClientScript",strScript.ToString());
}
然后,您需要对工具栏页面的HTML代码进行以下更改,以钩住按钮.(And you’ll need to make the following change to the Toolbar page HTML code to hook in the button.)
...
<td><a onmouseover =
"ChangeImages('TbLogoutImage', 'Toolbar_Logout_hover.gif');
return true;"
href="SwatLogoff.aspx" target="_top"...
...
胖女人正在热身.(The fat lady is warming up.)
就是这样.分析功能已完成,您将能够生成所有这些"漂亮"的图(用于回归分析的方式).当然,您需要生成一些错误才能使它们有趣.但是我认为我们不会很快耗尽代码中的错误.下次,我们将添加报告功能,然后完成.至少达到我在第一篇文章中给自己的目标.(Well that’s it. The analysis feature is completed and you will be able to generate all those ‘nifty’ (how’s that for a throwback) graphs. Of course you’ll need to generate some bugs in order to make them interesting. But I think we won’t run out of bugs in our code any time soon. Next time we’ll add the reporting feature and I’ll be done. At least with the goals I had given myself in the first article.)
许可
本文以及所有相关的源代码和文件均已获得The Code Project Open License (CPOL)的许可。
C# .NET1.1 Windows .NET .NET1.0 Visual-Studio ASP.NET Dev 新闻 翻译