<%@ Page Language="C#" MasterPageFile="~/MasterPage.master" AutoEventWireup="true" CodeFile="ProjectEdit.aspx.cs" Inherits="PTWeb.ProjectEdit" Title="项目信息" %> <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server"> <div> <asp:MultiView ID="MultiView1" runat="server" ActiveViewIndex="0"> <!-- 视图1 --> <asp:View ID="MainView" runat="server"> <!-- 提供项目编辑显示 AutoGenerateRows="False" 的好处在于可以优化访问,代码可以控制她的刷新,这样只在有更新的时候调用DataBind()方法刷新 --> <asp:DetailsView ID="DetailsView1" runat="server" Height="50px" Width="536px" AutoGenerateRows="False" DataSourceID="ProjectDataSource" DataKeyNames="Id" OnItemDeleted="DetailsView1_ItemDeleted" OnItemInserted="DetailsView1_ItemInserted" OnItemUpdated="DetailsView1_ItemUpdated" OnItemCreated="DetailsView1_ItemCreated"> <Fields> <asp:BoundField DataField="Id" HeaderText="编号" ReadOnly="True" SortExpression="Id" InsertVisible="False"> <ItemStyle Width="90%" /> </asp:BoundField> <asp:BoundField DataField="Name" HeaderText="名字" SortExpression="Name"> <ControlStyle Width="90%" /> </asp:BoundField> <asp:BoundField DataField="Started" HeaderText="开始时间" SortExpression="Started"> <ControlStyle Width="90%" /> </asp:BoundField> <asp:BoundField DataField="Ended" HeaderText="结束时间" SortExpression="Ended"> <ControlStyle Width="90%" /> </asp:BoundField> <%-- 描述是一个模板列,她提供了 编辑,添加,显示三种状态的显示模板 --%> <asp:TemplateField HeaderText="描述" SortExpression="Description"> <EditItemTemplate> <asp:TextBox ID="TextBox1" TextMode="MultiLine" Width="90%" runat="server" Text='<%# Bind("Description") %>'></asp:TextBox> </EditItemTemplate> <InsertItemTemplate> <asp:TextBox ID="TextBox1" TextMode="MultiLine" Width="90%" runat="server" Text='<%# Bind("Description") %>'></asp:TextBox> </InsertItemTemplate> <ItemTemplate> <asp:TextBox ID="TextBox1" TextMode="MultiLine" ReadOnly="true" Width="90%" runat="server" Text='<%# Bind("Description") %>'></asp:TextBox> </ItemTemplate> </asp:TemplateField> <asp:CommandField ShowDeleteButton="True" ShowEditButton="True" ShowInsertButton="True" /> </Fields> </asp:DetailsView> <br /> <!-- 提供相关项目的资源分派列表,也就是子表的显示 --> <asp:GridView ID="GridView1" runat="server" AllowPaging="True" AutoGenerateColumns="False" DataSourceID="ResourcesDataSource" Width="432px" DataKeyNames="ResourceId"> <Columns> <asp:BoundField DataField="ResourceId" HeaderText="ResourceId" ReadOnly="True" SortExpression="ResourceId" Visible="False" /> <%-- 显示的文本是 DataTextField="FullName" 全名 --%> <asp:HyperLinkField DataNavigateUrlFields="ResourceId" DataNavigateUrlFormatString="ResourceEdit.aspx?id={0}" DataTextField="FullName" HeaderText="名字" /> <asp:BoundField DataField="Assigned" HeaderText="分配" ReadOnly="True" SortExpression="Assigned" /> <%-- 提供了职务的模板,她是一个列表, 在编辑模式下 将其激活 显示模式下 将Enabled="False" 设置为假 DataTextField="Value" DataValueField="Key" 分别代表对象的数据源的属性 由于数据源是RoleList(名值对列表) SelectedValue='<%# Bind("Role") %>'> 很关键,首先你要知道<%# Bind("Role") %>是什么,这个是GridView1的资源 这样这个属性是用于设置DDL(DropDownList)当前显示的项目是谁 --%> <asp:TemplateField HeaderText="职务" SortExpression="Role"> <EditItemTemplate> <asp:DropDownList ID="DropDownList1" runat="server" DataSourceID="RoleListDataSource" DataTextField="Value" DataValueField="Key" SelectedValue='<%# Bind("Role") %>'> </asp:DropDownList> </EditItemTemplate> <ItemTemplate> <asp:DropDownList ID="DropDownList2" runat="server" DataSourceID="RoleListDataSource" DataTextField="Value" DataValueField="Key" Enabled="False" SelectedValue='<%# Bind("Role") %>'> </asp:DropDownList> </ItemTemplate> </asp:TemplateField> <asp:CommandField ShowDeleteButton="True" ShowEditButton="True" /> </Columns> </asp:GridView> <asp:LinkButton ID="AddResourceButton" runat="server" OnClick="AddResourceButton_Click">添加资源</asp:LinkButton><br /> <br /> </asp:View> <!-- 视图2 --> <asp:View ID="AssignView" runat="server"> <strong>选资源:</strong><br /> <!-- OnSelectedIndexChanged在点选一个项目之后被激发 --> <asp:GridView ID="GridView2" runat="server" AllowPaging="True" AutoGenerateColumns="False" DataKeyNames="Id" DataSourceID="ResourceListDataSource" OnSelectedIndexChanged="GridView2_SelectedIndexChanged"> <Columns> <asp:BoundField DataField="Name" HeaderText="资源" ReadOnly="True" SortExpression="Name" /> <asp:CommandField ShowSelectButton="True" /> </Columns> </asp:GridView> <asp:LinkButton ID="CancelAssignButton" runat="server" OnClick="CancelAssignButton_Click">取消</asp:LinkButton><br /> <%--只读资源列表做数据源--%> <csla:CslaDataSource ID="ResourceListDataSource" runat="server" TypeName="ProjectTracker.Library.ResourceList, ProjectTracker.Library" OnSelectObject="ResourceListDataSource_SelectObject" TypeSupportsPaging="False" TypeSupportsSorting="False"> </csla:CslaDataSource> </asp:View> </asp:MultiView> <asp:Label ID="ErrorLabel" runat="server" ForeColor="Red"></asp:Label><br /> <%-- 数据源集中区 项目基类资源--%> <csla:CslaDataSource ID="ProjectDataSource" runat="server" TypeName="ProjectTracker.Library.Project, ProjectTracker.Library" OnDeleteObject="ProjectDataSource_DeleteObject" OnInsertObject="ProjectDataSource_InsertObject" OnSelectObject="ProjectDataSource_SelectObject" OnUpdateObject="ProjectDataSource_UpdateObject" TypeSupportsPaging="False" TypeSupportsSorting="False"> </csla:CslaDataSource> <%-- 用的项目分配类,维护分配关系 --%> <csla:CslaDataSource ID="ResourcesDataSource" runat="server" TypeName="ProjectTracker.Library.ProjectResources, ProjectTracker.Library" OnDeleteObject="ResourcesDataSource_DeleteObject" OnSelectObject="ResourcesDataSource_SelectObject" OnUpdateObject="ResourcesDataSource_UpdateObject" TypeSupportsPaging="False" TypeSupportsSorting="False"> </csla:CslaDataSource> <%-- 名值对类表 --%> <csla:CslaDataSource ID="RoleListDataSource" runat="server" TypeName="ProjectTracker.Library.RoleList, ProjectTracker.Library" OnSelectObject="RoleListDataSource_SelectObject" TypeSupportsPaging="False" TypeSupportsSorting="False"> </csla:CslaDataSource> <br /> </div> <br /> </asp:Content>
//====================================================================== // // Guid1: 91a22c9c-c56a-4986-8ea5-d3db474ed789 // CLR版本: 2.0.50727.3053 // 新建项输入的名称: ProjectEdit // 机器名称: SOFTWARE-FRT-08 // 命名空间名称: PTWeb // 文件名: ProjectEdit // 当前系统时间: 2008-10-2 19:07:41 // 用户所在的域: TECHSTAR // 当前登录用户名: frt // 创建年份: 2008 // // Created by Terry.Feng.C (冯瑞涛) at 2008-10-2 19:07:41 // // //====================================================================== using System; using System.Data; using System.Configuration; using System.Collections; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; using ProjectTracker.Library; namespace PTWeb { public partial class ProjectEdit : System.Web.UI.Page { #region 私有成员 private enum Views { MainView = 0, AssignView = 1 } private void ApplyAuthorizationRules() { Project obj = GetProject(); // 项目 if (Project.CanEditObject()) { if (obj.IsNew) this.DetailsView1.DefaultMode = DetailsViewMode.Insert; else this.DetailsView1.DefaultMode = DetailsViewMode.Edit; //新的是不显示的,因为此时这个项目还没有被实际存在(数据库中没有) //***其实在这里我有一些自己的看法 //如果我们能够不在保存父对象的情况下,可以为其添加和建立子对象,将结果缓存起来,最后我们在父对象上点击保存,结果是把所有的父子,全都保存 //CSLA的演示程序,在这里她并不是这样的,但是她是支持的,如果你没有建立一个父对象她是不会让你为其添加资源的,她没有这么实现, //你可以看一下GridView2_SelectedIndexChanged这个事件,他在这里进行了保存SaveProject,如果我们不调用它,是可以的,成功了! // // 注意! 理解Session和Obj是出于同一个引用是这次的关键!在98行 // //在这里我分析了她当前能否实现这个需求, //1。他们的父子关系是多对多的关系,所以他们之间的分配是建立在一个中间表中的,这样中间表是需要父子两个表的主键为主键的 //this.AddResourceButton.Visible = !obj.IsNew; } else { this.DetailsView1.DefaultMode = DetailsViewMode.ReadOnly; this.AddResourceButton.Visible = false; } //这两个控件都是视图1中的 //由于Detailview是横向的,所以她是Rows.Count-1是最下面的控制行 this.DetailsView1.Rows[this.DetailsView1.Rows.Count - 1].Visible = Project.CanEditObject(); // 资源 // 最后一列根据权限同步显示 this.GridView1.Columns[this.GridView1.Columns.Count - 1].Visible = Project.CanEditObject(); } /// <summary> /// 获得,并缓存一个项目对象 /// </summary> /// <returns></returns> private Project GetProject() { //从缓存中取出项目对象 //这里Session,将内容付给Object,实际上是将一个引用传递,那么这代表这,如果Object这个对象,也就是GetProject返回的这个对象改变,同时也会影响 //Session["currentObject"] 的值,这是一个突破的理解,你在导出Session的活动,实际上就是在用另一种形式在使用Session的内容 object businessObject = Session["currentObject"]; //Session //如果不存在,进入 if (businessObject == null || !(businessObject is Project)) { try { string idString = Request.QueryString["id"]; //不是空的,代表是编辑或者是显示方法进来的 if (!string.IsNullOrEmpty(idString)) { //根据参数建立一个Guid Guid id = new Guid(idString); //获得这个项目 businessObject = Project.GetProject(id); } else //是新建进来的 businessObject = Project.NewProject(); //最后都要把这个对象付给缓存Session Session["currentObject"] = businessObject; //====Session } catch (System.Security.SecurityException) { Response.Redirect("ProjectList.aspx"); } } //将当前对象返回 return (Project)businessObject; //在此加断点,用监视器监控Session["currentObject"]的变化 } /// <summary> /// 这个方法用于保存一个工程,并将结果缓存,实际上,这个工程只是扩展了异常捕获的范围,(556) /// </summary> /// <param name="project"></param> /// <returns></returns> private int SaveProject(Project project) { //要返回的行影响标记 int rowsAffected; //返回 try { //保存,并缓存 Session["currentObject"] = project.Save();//====Session //标记设置成功,就是说数据有改变 rowsAffected = 1; } //权限异常(559) catch (Csla.Validation.ValidationException ex) { //字符串制造对象 System.Text.StringBuilder message = new System.Text.StringBuilder(); //先将获取的消息,添加进来 message.AppendFormat("{0}<br/>", ex.Message); //有问题规则列表如果有一个,就直接显示 if (project.BrokenRulesCollection.Count == 1) message.AppendFormat("* {0}: {1}", project.BrokenRulesCollection[0].Property, project.BrokenRulesCollection[0].Description); else //否则是多个,就用循环取出问题规则,主要是这个字符串加了<br/>,上面的无需加换行,否则是无需判断的 foreach (Csla.Validation.BrokenRule rule in project.BrokenRulesCollection) message.AppendFormat("* {0}: {1}<br/>", rule.Property, rule.Description); //将问题显示 this.ErrorLabel.Text = message.ToString(); rowsAffected = 0; } //数据门户异常 catch (Csla.DataPortalException ex) { this.ErrorLabel.Text = ex.BusinessException.Message; rowsAffected = 0; } //最后的异常 catch (Exception ex) { this.ErrorLabel.Text = ex.Message; rowsAffected = 0; } return rowsAffected; } #endregion #region 事件方法 protected void Page_Load(object sender, EventArgs e) { if (!Page.IsPostBack) { //设置授权 ApplyAuthorizationRules(); } else this.ErrorLabel.Text = string.Empty; } #endregion #region 数据控制 #region ProjectDataSource /// <summary> /// 项目Detailview在删除的时候激发这个事件,这个事件在这里好像很难用上 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void ProjectDataSource_DeleteObject( object sender, Csla.Web.DeleteObjectArgs e) { try { Project.DeleteProject(new Guid(e.Keys["Id"].ToString())); //清空是为了重新加载 Session["currentObject"] = null; ////====Session e.RowsAffected = 1; } catch (Csla.DataPortalException ex) { this.ErrorLabel.Text = ex.BusinessException.Message; e.RowsAffected = 0; } catch (Exception ex) { this.ErrorLabel.Text = ex.Message; e.RowsAffected = 0; } } /// <summary> /// Detailview在插入的时候引发 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void ProjectDataSource_InsertObject( object sender, Csla.Web.InsertObjectArgs e) { Project obj = GetProject(); Csla.Data.DataMapper.Map(e.Values, obj, "Id"); e.RowsAffected = SaveProject(obj); } /// <summary> /// 对象的一次数据绑定,会引发这个事件 /// 要注意的一点是,如果你是点击新建进入这个页面的,在页面加载的时候也是会调用这个事件,为Detailview建立一个新的项目对象的 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void ProjectDataSource_SelectObject(object sender, Csla.Web.SelectObjectArgs e) { e.BusinessObject = GetProject(); } /// <summary> /// 当Detailview更新的时候激发 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void ProjectDataSource_UpdateObject(object sender, Csla.Web.UpdateObjectArgs e) { Project obj = GetProject(); Csla.Data.DataMapper.Map(e.Values, obj); //调用保存方法,她扩展了异常的捕获 e.RowsAffected = SaveProject(obj); } #endregion #region ResourcesDataSource /// <summary> /// ResourcesDataSource是 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void ResourcesDataSource_DeleteObject( object sender, Csla.Web.DeleteObjectArgs e) { Project obj = GetProject(); int rid = int.Parse(e.Keys["ResourceId"].ToString()); obj.Resources.Remove(rid); e.RowsAffected = SaveProject(obj); } /// <summary> /// 当可编辑资源列表加载的时候引发 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void ResourcesDataSource_SelectObject( object sender, Csla.Web.SelectObjectArgs e) { Project obj = GetProject(); //获得当前项目的所有资源 e.BusinessObject = obj.Resources; } /// <summary> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void ResourcesDataSource_UpdateObject( object sender, Csla.Web.UpdateObjectArgs e) { //获得当前项目 Project obj = GetProject(); //获得更新的资源id int rid = int.Parse(e.Keys["ResourceId"].ToString()); //通过父对象获得她的资源引用 ProjectResource res = obj.Resources.GetItem(rid); //拷贝数据 Csla.Data.DataMapper.Map(e.Values, res); //立即保存, e.RowsAffected = SaveProject(obj); } #endregion #region ResourceListDataSource /// <summary> /// ResourceListDataSource 是选择列表的资源,当载入一个对象的时候激发 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void ResourceListDataSource_SelectObject( object sender, Csla.Web.SelectObjectArgs e) { //只读列表 e.BusinessObject = ProjectTracker.Library.ResourceList.GetResourceList(); } #endregion #region RoleListDataSource /// <summary> /// RoleListDataSource是在给一个项目现在资源后,配置这个资源的职务的时候的那个下拉列表框,同样在加载的时候激发 /// 这个事件估计用的会很多,因为每一个被分配的资源记录都要计划一次她,来初始化对象的职务列表 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void RoleListDataSource_SelectObject( object sender, Csla.Web.SelectObjectArgs e) { //调用名只对列表 e.BusinessObject = RoleList.GetList(); } #endregion #endregion #region 界面控制 #region Project DetailsView /// <summary> /// 在DetailsView控件初始化后,激发,用于填充数据 /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void DetailsView1_ItemCreated(object sender, EventArgs e) { //校验模式,如果是录入模式这个录入才能成功,才是有意义的 if (DetailsView1.DefaultMode == DetailsViewMode.Insert) { //调用私有方法,获取这个对象,这个也许是空值的,也许也是编辑的 Project obj = GetProject(); //将第二行的第二列的第一个控件的Text属性设置值,要知道是从0开始数的,而这里也没有什么控件,这里是Details的格子控件,没有名字用位置描述 ((TextBox)DetailsView1.Rows[1].Cells[1].Controls[0]).Text = obj.Name; //将第三行的第二列的第一个控件的Text属性设置值 ((TextBox)DetailsView1.Rows[2].Cells[1].Controls[0]).Text = obj.Started; //将第四行的第二列的第一个控件的Text属性设置值 ((TextBox)DetailsView1.Rows[3].Cells[1].Controls[0]).Text = obj.Ended; //发现DetailsView1.中的名字为TextBox控件的设置Text属性 ((TextBox)DetailsView1.FindControl("TextBox1")).Text = obj.Description; } } /// <summary> /// 在Details点击插入的之后,MSDN:在单击 DetailsView 控件中的“插入”按钮时,但在插入操作之后发生。 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void DetailsView1_ItemInserted(object sender, DetailsViewInsertedEventArgs e) { Project project = GetProject(); //如果不是新的则跳转 if (!project.IsNew) Response.Redirect("ProjectEdit.aspx?id=" + project.Id.ToString()); } /// <summary> /// 在单击 DetailsView 控件中的“更新”按钮时,但在更新操作之后发生。 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void DetailsView1_ItemUpdated( object sender, DetailsViewUpdatedEventArgs e) { ApplyAuthorizationRules(); } /// <summary> /// 在单击 DetailsView 控件中的“删除”按钮时,但在删除操作之后发生。实际上这个事件是不会发生的,因为没有删除按钮 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void DetailsView1_ItemDeleted( object sender, DetailsViewDeletedEventArgs e) { Response.Redirect("ProjectList.aspx"); } #endregion #region Resource Grid /// <summary> /// 点击为项目添加一个资源按钮,切换视图 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void AddResourceButton_Click( object sender, EventArgs e) { this.MultiView1.ActiveViewIndex =(int)Views.AssignView; } /// <summary> /// GridView2是资源选择类表控件,当选择一个选项后激发此事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void GridView2_SelectedIndexChanged( object sender, EventArgs e) { //获取当前的项目对象 Project obj = GetProject(); try { //将选中的主键转换成整型,分配给自己 //GridView2.SelectedDataKey.Value 代表,被选中项目的主键的值(559) obj.Resources.Assign(int.Parse( this.GridView2.SelectedDataKey.Value.ToString())); //调用私有保存方法,如果返回不是0,成功,则刷新视图1的资源列表,并切换回主页面 //注销下面的if可以支持延迟保存 //if (SaveProject(obj) > 0) //{ this.GridView1.DataBind(); this.MultiView1.ActiveViewIndex = (int)Views.MainView; //} } //如果你选中了已经存在的子对象(重复分配),Assign会发出一个异常,这个异常会在这里被捕获,处理的方法是将这个异常付给ErrorLabel显示给用户 catch (InvalidOperationException ex) { ErrorLabel.Text = ex.Message; } } /// <summary> /// 在分配选择资源的时候点击取消,切回视图 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void CancelAssignButton_Click(object sender, EventArgs e) { this.MultiView1.ActiveViewIndex = (int)Views.MainView; } #endregion #endregion } }