今天偶然发现 Vista 中自带一个截图工具,可以方便地截取任意格式、矩形、窗口或全屏幕,截取之后可以直接存储为 JPG、PNG等格式,而且能够进行一些批注,同时也能够直接发送电子邮件。不过遗憾的是,在直接存储成 JPG 时,截图工具和画笔一样,都会使图形失真,还是先保存成其他格式,然后再转换成 JPG 。
在 GridView 中增加记录的方法
在 ASP.NET 2.0 中,GridView 支持修改/删除记录,但却不支持新增记录的功能(个人感觉是 GridVew 的一大缺憾,估计在下一版本中会加入此功能),大多数人建议用 FormView 来完成增加记录的功能,但是 FormView 和 GridView 不是同一个表格,所以无法在同一个页面的同一个表格中显示。如果故意将 FormView 或自己的一堆于用新增功能的控件使用普通的表格组装起来,那么会碰到一个很麻烦的问题,即两个表格的列宽如何协调一致,大多数情况下,大家在做表格的时候,表格中各列的宽度都是自动调整的,所以强行指定宽度在很多情况下并不适用。
通过实践,想出了一种办法,主要步骤如下所示:
1) 在 GridView 的 EmptyDataView 中,放置一个普通的Html Table,以便在GridView绑定的数据源中无数据时依旧显示表头(如果数据源为返回的数据行数为0,GridView默认是不显示表头的),假设 ID 为 tbHeader,它的作用是下面用于新增功能的 tbForm 的各列控件提供说明(充当表头);
2)在 GridView 下面,放置一个普通的 HTML 表格,其列数和 GridView 中定义的列数保持一致,但行数只有一行,然后在此表格的各列中放入用于新增功能的各个控件(如 TextBox等),假设此表格的 ID 为 tbForm
3)在页面中加入一段客户端脚本,以便使页面展示到客户端时,利用 Javascript 将两个表格强行合并到一起,这样就可以将只有一行的 tbForm 合并到 GridView中,因此 GridView 的最下面多出一行,其中有 tbForm 表格中定义的输入控件和“添加”链接(按钮),主要代码(JavaScript)如下:
function MergeTable(source,dest)
{
var row;
var cell;
var sourceTb = document.all(source);
var destTb = document.all(dest);
for (var i=0; i<sourceTb.rows.length; i++)
{
row = document.createElement(“TR”);
for (var j=0; j<sourcetb.ROWS(I).CELLS.LENGTH; j++)
<sourceTb.rows(i).cells.length; j++)
{
cell = document.createElement(“TD”);
row.appendChild(cell);
//复制对象
for(k=0;k<sourceTb.rows(i).cells(j).all.length;k++)
cell.appendChild(sourceTb.rows(i).cells(j).all.item(k));
}
destTb.tBodies(0).appendChild(row);
}
for (var i=sourceTb.rows.length-1; i>=0; i–)
{
sourceTb.deleteRow(i)
}
}
function ChangeTableLayout()
{
if(document.all(‘tbHeader’) == null)
MergeTable(‘tbForm’,”);
else
MergeTable(‘tbForm’,’tbHeader’);
}ChangeTableLayout();
如果 GridView 绑定时没有数据,将不显示其中定义的各列,而只显示 EmptyDataView 中的 tbHeader,这时要合并 tbHeader 和 tbForm。如果 GridView 绑定时包含数据,则不会显示 EmptyDataView(当然也不会显示其中的 tbHeader),但这时会显示 GridView 中定义的各个列,因此只需将 GridView 本身和 tbForm 合并即可。 GridView 的客户端ID可以用GridView.ClientID来获取。
在服务器端很容易知道 GridView 绑定后是否包含数据,但对于客户端来说,不容易检查,一个简单的作法就是检查页面中有没有 tbHeader 对象(如果有,则说明表格没有数据,如果无此对象,表示 GridView 中包含数据… 好啰嗦)
4)如果是 AJAX 环境,上述脚本有可能不被执行,可以调用 Sys.Application.load.add ( JavaScriptFunction) 来强制执行脚本,来合并表格,主要代码如下(C#):
ScriptManager myScriptManager = ScriptManager.GetCurrent(Page);
if (myScriptManager.IsInPartialRenderingMode)
{
Page.ClientScript.RegisterStartupScript(this.GetType(), “ShowFullTable1”, “ChangeTableLayout();\n”, true);
}
else
{
Page.ClientScript.RegisterStartupScript(this.GetType(), “ShowFullTable2”, “Sys.Application.load.add (ChangeTableLayout);\n”, true);
}
注:上述代码中的 ChangeTableLayout 为客户端脚本函数的名称,其中调用第3步骤中的代码,上述代码在 Atlas 中通过,在 ASP.NET AJAX Beta 上尚末测试。
另:为了更能说明上文代码的效果,我抓了一个截图,图中包括表头在内的前三行就是 GridView,最下面一行其实来自于另一个表格,在客户端强制合并后,显示效果就是这样,看起来象是一个表格。
感谢大家的意见,我对原文中的表述不明确的地方进行了更改。
授权管理器(AzMan)中授权策略信息的存储方式的选择
授权管理器(AzMan)是 Windows Server (2000、2003、Longhorn Server…)中自带的、能够和AD紧密集成的、供应用程序使用的授权检查引擎,它实现了完整的基于角色的授权管理框架,利用它,可以在应用程序中大大简化与授权有关的代码编写与实现。
在目前版本中,授权策略信息有两种存储方式:
- XML
- 目录(如活动目录-AD或ADAM)
上述两类三种存储方式的适用场景分别如下:
- 简单的小型应用可以直接使用XML,在这种方式中,不用设置/扩展 AD/ADAM 的 Schema,非常方便
- 如果基础域环境不好(如应用程序不便访问DC、存在多个Domain)等情况时,可以使用 ADAM
- 如果基础域环境较好,且应用程序的身份完全与AD集成,最好使用AD
在 Vista 中,AzMan 又增加了一种存储方式:SQL Server,这样对于应用程序来说,又多了一种更实用的选择,而且即然能存在 SQL Server中,估计通过一些手段,这些信息也能存储到其他类型的数据库中,如Access、Oracle等,现在 ASP.NET 2.0中的一些 Provider 不也是公开源代码了,并且实现了其他数据库类型的 Provider 了嘛。
不过,AD/ADAM 具有复制功能,可以方便地实现多个实例之间的目录同步,而SQL Server 不具有此功能,另外 AD/ADAM 在查询方面具有性能优势(AzMan的绝大多数请求是查询),所以到底要使用哪一种存储方式,还是要综合评估。
关于 AzMan 的更多信息,可以参考 AzMan MSDN Blog。
利用 AOP 实现 .NET 上完整的基于角色的访问控制(RBAC)模型
近期帮别人在 ASP.NET 中实现简捷的安全/权限控制,于是进行了相关研究与内容搜集,并整理成代码,已在实际的项目中运用,效果突出,比 .NET 中的代码访问安全性(CAS)和 ASP.NET 中的相关安全控制机制更为灵活。
于是把其中实现的内容和主要代码共享出来,但内容较多,放在在随笔里会干扰大家查看首页的视线,所以归到文章里,感兴趣者可以在这里(http://blog.joycode.com/moslem/articles/85194.aspx)查看。
本文及其中的代码主要介绍了以下内容:
- 如何在 C# 中简捷/方便地进行 AOP 编程
- 如何利用 AOP 来透明地在业务逻辑对象中进行权限检查
- 如何实现完整的基于角色的访问控制模型
- 如何在UI上针对不同的用户权限对控件进行设置(如禁止或隐藏)
- 如何设置 Attribute,如何利用反射获取程序集中所有的、指定类型的 Attribute 定义
- 其他相关内容
一组 ASP.NET 2.0 主题文件下载
在 ASP.NET 2.0 中,大多数控件都可以直接“自动套用格式”,以便设置一些内置的配色方案,能够方便地设计多姿多彩、色彩丰富的页面,除些之外,ASP.NET 2.0 还提供了网站和页面的主题(Theme)和Skin的功能,可以在网页或网站层次设定各种控件的显示风格,以便统一站点的外观。
在实际开发中,实际上大家可能不会过多地采用内置的“自动套用格式”的功能,而是自己来设定控件的色彩、字体等,当然也有可能是通过 Theme/Skin 来进行的,但是,以我的实践来看,实现机制是很好,但结果往往另人不满意,主要原因是缺乏色彩方面的感觉,往往会设计出来色彩冲突、不协调,甚至在很多情况下相当不好看的界面 …
即然有这么好的实现机制,肯定会有人实现/共享很好的 Theme/Skin ,Google 了一下,找到一组 Theme,看起来还是相当不错的,可以直接应用的项目开发中,当然了,这组 Theme 也有一些小瑕疵,即某些对比色的过于接近而比较模糊。
Theme预览:http://www.dotnettreats.com/SampleThemes/Default.aspx
Theme下载:http://www.dotnettreats.com/tools/Default.aspx
在 ASP.NET 上实现锁定表头、支持滚动的表格的做法
首先看看 CodeProject 上的两个东西
1、The Freeze Pane DataGrid (http://www.codeproject.com/aspnet/FreezePaneDatagrid.asp)
利用文章中提到做法及代码,可以实现在 ASP.NET 1.1 上的、支持横向滚动与纵向滚动的表格,基本上是使用 CSS 实现的,比较简单。
在 ASP.NET 2.0 上,由于文档 HTML DOCKTYPE 发生了变化(HTML->XHTML),所以在使用原文中的横向滚动条会出现问题,但是使用纵向滚动条和锁定表头没有问题。
这种做法没有考虑页面 PostBack 时记录表格的滚动位置,使得用户不得不重新去寻找刚才选中/编辑的那条记录,这比较的不人性化。
2、ScrollingGrid: A cross-browser freeze-header two-way scrolling DataGrid(http://www.codeproject.com/aspnet/ScrollingGrid.asp)
此文章利用 Panel 控件和 DataGrid 控件实现了 ASP.NET 1.1 下的完整的、可实现双向滚动、表头锁定的表格,而且它实现了可以记录表格的滚动位置,页面 PostBack 后,表格仍能自动滚动到原有位置。这个控件的一个最大优点是能够适应多种浏览器,如 Internet Explorer 、FireFox 等。
在 ASP.NET 平台上,由于 DataGrid 控件已经升级为 GridView ,所以此控件已不能使用,按照文章下面的讨论,作者声称会尽快升级控件,但似乎在实现时碰到一些麻烦(如何确实表头各列的宽度),目前还没有结果。
目前我的做法:
- 参照文章1中提到的作法,利用 CSS 来实现锁定表头的功能
- 利用 Panel 控件,设置 ScrollBar 为 Vertical,再在其中放入 GridView 控件 ,可以实现竖向滚动条的功能
- 利用 Atlas ,将上述 Panel 再放入 UpdatePanel ,以透明实现保持滚动条位置的功能
示例代码:
<h1>滚动条表格演示h1>
<style type="text/css">......
th {...}{...}{...}{
border-right: 1px solid silver;
position:relative;
top: expression(this.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.scrollTop-2); /**//**//**//*IE5+ only*/
}
style>
![]()
<atlas:ScriptManager ID="ScriptManager1" runat="server" EnablePartialRendering="True">
atlas:ScriptManager>
<br />
<asp:Panel ID="GridPanel" runat="server" Height="250px" ScrollBars="Auto" Width="562px">
<atlas:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataSourceID="ObjectDataSource1" SkinID="GridView" Width="434px">
</asp:GridView>
</ContentTemplate>
</atlas:UpdatePanel>
</asp:Panel>
这样能基本上实现一个能够锁定表头、竖向滚动、能够在页面PostBack时保持滚动位置的表格,能够满足大部分应用需要。
几种常见的 ASP.NET 平台上表格控件的简单评价
由于这次试用它们的目的主要是寻找一种能够简单实现滚动条的表格控件,所以关注重点也在这个方面,其他方面没有过多涉及。
1. Developer Express ASPxGrid
1) 程序本身有 Bug ,有乱码现象
2) 代码量很大
3) 不能创建 DataSource ,但可以绑定已经建好的 DataSource
4) Scroll 动作需要 Postback 到服务器上,较慢,横向的无须 CallBack
5) 代码量较大,尤其是处理数据更新时(例子有 600 行代码)
6) 不支持自动格式,在 UI 定制方面工作量较大
7) 过于臃肿,更象是一个 WinForm 而非 WebForm 的控件
价格: 1 Developer License $265
网站: www.devexpress.com
2. ComponetOne WebGrid
1) 和 GridView 比较接近
2) 有专用界面来定义列
3) 可以横向或纵向滚动,但要求将 Grid 的大小(宽度和高度)设置为绝对值,不能设为百分比
4) 可以设置模板
5) 必须设置列宽,不便于自动调整
价格: $999.99 Componet Studio Enterprise
网站: www.componentone.com
3. ComponentArt Grid
1) 不带设计器,所有属性和事件都需要手动设置,无法在 GUI 下设计
2) 大多数效果都是以代码实现的
3) 启用滚动条的方式比较复杂,如需要设置 Scrollbar 的宽度,图像等
价格: 1 Developer License 799$ (共 13 个控件)
网站: www.componentart.com
4. Telerik Grid
1) 内置 AJAX Framework,有可能和 Atlas 冲突
2) 可以方便地设置 Scroll
3) 支持的绑定控件比较丰富,直接支持 普通/Button/Checkbox/DropDown/HyperLink 等
4) 支持 Theme
5) 与普通的 GridView 的对象模型不太一致,需要重新学习其使用方法
价格: 1 Developer License $399 (17个控件)
网站: http://www.telerik.com
———————————-
这次比较令人意外的是,ComponentArt 的 Grid 控件没有 DesignTime 的 UI 支持,远远不如 ASP.NET 的 GridView 使用起来方便。
IIS 7 in Vista RC1
在控制面板中启用 IIS 时,发现各项功能的控制粒度很细,打开首页,只显示这样一个图片,不过目前 IIS7 的管理工具似乎还没有包含在 Vista 中。
这几天领教了 Vista 中的 UAC 和 Internet Explorer Protocted Mode 的厉害,不过,我对一部分最终用户能不能很好地接受这个Fetature信心不足,尤其是开启/关闭 UAC 的时候,需要重启计算机。
在 Internet Explorer Protocted Mode 时使用极点五笔,一直报错,不让访问安装目录下的码表文件(注:只在 IE 中报错,在其他应用程序中则不受影响),不得已,只好设置 Protocted 为 Off,现在我还没有找到其他好的解决办法,在网上看到一些 Post ,好象其他的一些输入法也有类似问题。
ASP.NET 2.0 中 AuthorizationStoreRoleProvider 可用性不高
一、授权管理概述
一般在应用程序中,比较理想、完整的授权模式就是基于角色的授权(Role Based Access Control-RBAC),它的主要目标是实现了用户与安全操作的分离,而在中间加入了角色的隔离层,从而实现了灵活性和可扩展性,具体来说,主要是这种方式:
- 在编写程序的时候,定义出程序的功能(或称为安全操作,如 AddBook, ModifyBook …)
- 在应用程序部署的时候,按照业务安全策略的要求,定义角色(如 BookManager),并定义角色与功能的对应关系
- 在应用程序运行期,定义可以使用系统的用户(如张三、李四),然后定义用户与角色的关系
这样通过“三个实体,两个对应”,就能找到用户和功能的关系(即用户能否执行此功能)。
二、ASP.NET 2.0 中的授权管理功能
ASP.NET 2.0中,提供了三种授权管理的提供者,分别为:AspNetSqlRoleProvider、AuthorizationStoreRoleProvider、AspNetWindowsTokenRoleProvider。
默认 AspNetSqlRoleProvider 只能提供角色与用户的实体及其对应关系,缺乏功能层次的定义和关系,所以仅能适用于有限场合,如基于 URL 的授权,或者在程序中手动使用 Roles API(Roles.IsUserInRole)来判断当前用户是否具有授权,但是这种方式的前提是角色是 Hard Code(硬编码的),以后如果角色所允许的功能发生了变化,只能通过修改程序来实现。
再来看看 Windows 2003 中附带的授权管理器(Authorization Manager,以下简称 AzMan),不可否认,它的设计比较完整地体现了 RBAC的思想,即首先定义操作,然后把操作归类形成任务(大任务也可以包括子任务),可以定义角色,然后在角色中定义其可以执行的任务或操作(大角色也可以包含小角色),最后可以应用程序中分配用户、建立角色与用户的对应关系,同时,AzMan 还提供业务规则检查的功能,即可以在任务级别定义脚本,对是否授权进行进一步的细粒度的检查,表面上看,还是比较圆满的。
但是 AzMan 的一个重大限制就是只支持 Windows 用户,它要求用户的标识为 SID(如S-1-5-21-XXXX-XXX)的格式。虽然我们可以使用 AzMan 的 API ,如 InitializeClientContextFromStringSid 来构造一个虚拟的 SID (例如,在我们的应用程序中,我们用户的 ID 为一个自增量的整数 Y,那就可以虚拟成 S-1-5-21-Y的方式)这样我们就可以利用 AzMan API 来绕开只允许Windows 用户的限制,但这些工作全部要手工完成,纯体力活,得不偿失。
本来以为 ASP.NET 2.0 的AuthorizationStoreRoleProvider 做为默认的三种 RoleProvider 之一,能为我们提供这些方面的便利功能,但在
使用之后才发现,限制依然存在,实用性仍然不够:
- 在定义角色时,它会把角色定义在 AzMan的“角色分配”结点上,而不是“角色定义”结点上,有点奇怪
- 如果使用 AspNetSqlMembershipProvider ,那么在为用户分配角色时,就会引发“帐户名与安全标识间无任何映射完成”的错误,这可以看出来 AzMan 依然是使用 Windows 用户,这一个限制很是致命,也就是说
AspNetSqlMembershipProvider 和 AuthorizationStoreRoleProvider 是不能共用的。
如果能够把 AspNetSqlMembershipProvider 中用户的 MemberId (GUID类型,格式为“XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX”)转换成 SID (S-R-I-S-S)还有可能通过 AzMan API 来利用AzMan ,但这不是通过 AuthorizationStoreRoleProvider 来进行的,而且目前我也没有找到好的、能够双向转换的方法。
三、一种实用的授权管理机制设想
目前临时想到一种即能完整实现 RBAC,又能利用 RoleProvider 的解决办法是:
- 利用 AspNetSqlRoleProvider 来管理 Role、定义 Role 与AspNetSqlMembershipProvider 中用户之间的关系
- 新增一自定义XML文件,在其中定义任务/操作列表,在编写程序时,可以在类或方法的属性中指定操作名称
- 同时在上述XML文件中定义 Role 与操作的对应(包含)关系
- 在程序中利用公用程序来检查权限,这无需写死(Hard Code)任何东西,因为是直接从当前类/方法中取出操作名称,再直接获得当前用户名,再检查第 2、3步的XML 文件文件即可。
一些关于Visual Studio 2005 代码段(Code Snippet)的信息
非常喜欢Visaul Studio 2005 中的代码段,大大节省了一些无聊的、没有技术含量的代码编写工作,如实体类的属性设置等。
Visual Studio 2005 中的 VB.NET 的代码段比 Visual C# 的多出不少,是不是吸引开发人员?
我们可以修改系统中现有的 snippet 文件(C:\Program Files\Microsoft Visual Studio 8\VC#\Snippets\2052\Visual C#目录),来增加自己常用的 snippet。
网上(http://www.gotcodesnippets.net/)也有不少别人编写的 snippet ,可以按需取用。
要想共享或发布 snippet,最好做成Visual Studio 支持的格式 vsi ,其实是就是一个 更改了后缀名的 zip 文件,里面包含一个 XML 描述文件、一个或多个 snippet 文件(可以从上面网站中获得一个 vsi 文件来参考如何制作过程)。
在 Visual Studio 2005 中,插入代码段的快捷键是 Ctrl K + Ctrl X ,代码段管理器的快捷窗口是 Ctrl K + Ctrl B。
这里提供一个比较好用的 Public Property 的 Snippet ,是对 http://www.gotcodesnippets.net 上拿到 Public Property 稍加修改之后形成的东西,这个我最常用 🙂 。
<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Header>
<Title>Public Property</Title>
<Shortcut>pp</Shortcut>
<Description>公共属性</Description>
<Author>LiYanZhi ([email protected]_</Author>
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
</SnippetTypes>
</Header>
<Snippet>
<Declarations>
<Literal>
<ID>accessor</ID>
<ToolTip>The access modifier</ToolTip>
<Default>public</Default>
</Literal>
<Literal>
<ID>type</ID>
<ToolTip>属性类型</ToolTip>
<Default>string</Default>
</Literal>
<Literal>
<ID>name</ID>
<ToolTip>属性名称</ToolTip>
<Default>MyProperty</Default>
</Literal>
</Declarations>
<Code Language="csharp">
<![CDATA[private $type$ _$name$;
![]()
$accessor$ $type$ $name$
{
get { return _$name$; }
set { _$name$ = value; }
}$end$]]>
</Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>
在修改 Snippet 时,也发现其中的一些不足,比如表达式中函数支持太少,例如上例中,一般用 _name 表示内部字段,用 Name 表示属性,但缺乏第一个字母大小写转换的函数。