wenmo8 发布的文章

在我们一些Winform程序中,往往需要具有一些特殊的权限才能操作系统文件,我们可以设置运行程序具有管理员权限或者设置运行程序的目录具有写入的权限,如果是在操作系统里面,我们可以设置运行程序以管理员身份运行,或者设置Users用户组在运行目录中具有写入权限都可以解决问题,不过如果我们想通过C#代码进行自动的处理,那么应该如何实现呢?

1、系统设置管理员权限或者目录写入权限

如果我们需要让程序以管理员身份运行,那么可以通过设置快捷方式的属性或者应用程序的属性为【以管理员身份运行此程序】即可实现,如下所示。

如果我们需要一些写入文件的权限,如我们程序可能需要操作SQLite文件数据库,那么也可以通过设置Users用户组在运行目录中具有写入权限都可以解决问题,否则可能会出现【 attempt to write a readonly database】的错误。

我们设置步骤如下所示:找到SQLite数据库所在的文件夹,单击右键,属性->安全,为Users用户组添加写入权限。

2、使用C#代码实现

上面的步骤可以解决我们实际碰到的权限访问问题,那么我们如果使用C#代码,应该如何实现这些操作呢?

对于第一个以管理员身份运行程序的处理操作,我们是可以通过程序修改配置的方式实现,这样可以避免一些Winform程序运行时刻的权限问题:

1)在通过winform程序执行cmd命令时,某些情况下如果不是以管理员身份运行,则会提示命令无效。

2)或者通过winform程序执行Windows Service 服务时,也需要以管理员身份才能调用Service服务。

3)处理其他需要管理员身份的相关操作。

我们如果是编译Winform程序,只需要几步就可以在让程序在运行的时候获得管理员身份,如下所示在我们Winform的UI项目【属性】【安全性】里面,勾选ClickOne的设置。

然后我们就可以看到在UI项目【Properties】目录里面,生成了一个app.manifest文件。

这个app.manifest文件是自动生成的,我们修改其中的一项设置,然后取消上面勾选ClickOne的设置就可以了。

把其中app.manifest文件的内容:

<requestedExecutionLevel level="
asInvoker
" uiAccess="false" />

改为:

<requestedExecutionLevel level="
requireAdministrator
" uiAccess="false" />

即可,这样取消上面勾选ClickOne的设置,然后重新编译整个程序即可。

在程序运行的时候,会提示“用户账户控制”来获取管理员权限运行,点击“是”则获取了管理员权限。

对于需要为指定目录设置用户组权限,那么也是可以通过C#代码进行处理的。

一般情况下,我们可以在程序安装或者启动的时候,对目录进行用户组权限的处理,这样程序运行起来就自然具有对应目录的读写权限了。

如我们在程序启动的时候处理,那么我们可以在Main函数的里面进行设置。

        /// <summary>
        ///应用程序的主入口点。/// </summary>
[STAThread]private static voidMain()
{

}

为了方便处理,我们添加一个公共的函数,用来处理用户组的目录权限访问操作,C#代码如下所示。

        /// <summary>
        ///为指定用户组,授权目录指定完全访问权限/// </summary>
        /// <param name="user">用户组,如Users</param>
        /// <param name="folder">实际的目录</param>
        /// <returns></returns>
        private static bool SetAccess(string user, stringfolder)
{
//定义为完全控制的权限 const FileSystemRights Rights =FileSystemRights.FullControl;//添加访问规则到实际目录 var AccessRule = newFileSystemAccessRule(user, Rights,
InheritanceFlags.None,
PropagationFlags.NoPropagateInherit,
AccessControlType.Allow);
var Info = newDirectoryInfo(folder);var Security =Info.GetAccessControl(AccessControlSections.Access);boolResult;
Security.ModifyAccessRule(AccessControlModification.Set, AccessRule,
outResult);if (!Result) return false;//总是允许再目录上进行对象继承 const InheritanceFlags iFlags = InheritanceFlags.ContainerInherit |InheritanceFlags.ObjectInherit;//为继承关系添加访问规则 AccessRule = newFileSystemAccessRule(user, Rights,
iFlags,
PropagationFlags.InheritOnly,
AccessControlType.Allow);

Security.ModifyAccessRule(AccessControlModification.Add, AccessRule,
outResult);if (!Result) return false;

Info.SetAccessControl(Security);
return true;
}

然后我们在Main函数里面进行调用就可以了。

        /// <summary>
        ///应用程序的主入口点。/// </summary>
[STAThread]private static voidMain()
{
//为用户组指定对应目录的完全访问权限 SetAccess("Users", Application.StartupPath);//界面汉化 System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("zh-Hans");
DevExpress.UserSkins.BonusSkins.Register();
DevExpress.Skins.SkinManager.EnableFormSkins();

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(
false);
Application.ThreadException
+= newSystem.Threading.ThreadExceptionEventHandler(Application_ThreadException);//登录界面 Login dlg = newLogin();
dlg.StartPosition
=FormStartPosition.CenterScreen;if (DialogResult.OK ==dlg.ShowDialog())
{
if(dlg.bLogin)
{
SplashScreen.Splasher.Show(
typeof(SplashScreen.frmSplash));

gc.MainDialog
= newMainForm();
gc.MainDialog.StartPosition
=FormStartPosition.CenterScreen;

Application.Run(gc.MainDialog);
}

}
dlg.Dispose();
}

这样在程序运行后,我们就可以看到对应目录具有完全的读写操作权限了,这样对于一些如读写SQLite出错的问题,也就迎刃而解了。

以上就是我对于两种不同权限访问的处理经验总结,希望给在Winform开发中的同行参考,感谢耐心的阅读和支持。

在最新的MVC4+EasyUI的Web开发框架里面,我整合了关于网购运单处理的一个模块,其中整合了客户导单、运单合并、到货扫描、扣仓、出仓、查询等各个模块的操作,里面涉及到一些运单套打的操作,不过由于之前介绍LODOP不兼容Chrome等浏览器,因此曾经想放弃这个控件的打印处理,不过他们及时推出了“云打印控件C-Lodop“,而且对之前的接口几乎完全兼容,因此在框架里也继续沿用了这个控件来进行相关的打印处理,包括常规的打印和运单信息套打等处理。

1、控件的安装

这个云控件C-Lodop(
http://www.lodop.net/
)其实是在本地安装后,驻留服务提供JS的服务的,安装后启动程序后界面如下所示。

这种通过服务器提供JS服务的方式,比插件方式好很多,测试过Chrome浏览器也可以顺利打印了,原来的LODOP插件方式的打印是不兼容的。

通过它们本身自带的JS初始化代码,可以了解到该控件目前采用非插件方式进行处理打印操作的了。

//让其它电脑的浏览器通过本机打印(适用例子):
var oscript = document.createElement("script");
oscript.src
= "/CLodopfuncs.js";var head = document.head || document.getElementsByTagName("head")[0] ||document.documentElement;
head.insertBefore(oscript, head.firstChild);
//让本机浏览器打印(更优先): oscript = document.createElement("script");
oscript.src
= "http://localhost:8000/CLodopfuncs.js?priority=1";var head = document.head || document.getElementsByTagName("head")[0] ||document.documentElement;
head.insertBefore(oscript, head.firstChild);
//本机浏览器的后补端口8001(这种兼顾做法可能报错不用理它): oscript = document.createElement("script");
oscript.src
= "http://localhost:8001/CLodopfuncs.js?priority=2";var head = document.head || document.getElementsByTagName("head")[0] ||document.documentElement;
head.insertBefore(oscript, head.firstChild);

官方例子提供了LodopFuncs.js文件用来构建打印控件的,其中在LodopFuncs.js文件里面定义了getLodop函数,用来获得打印控件对象的。

检查是否安装了云打印控件的JS代码如下所示。

<script language="javascript" type="text/javascript">    
    functionCheckIsInstall() {try{var LODOP=getLodop();if(LODOP.VERSION) {if(LODOP.CVERSION)
alert(
"当前有C-Lodop云打印可用!\n C-Lodop版本:"+LODOP.CVERSION+"(内含Lodop"+LODOP.VERSION+")");elsealert("本机已成功安装了Lodop控件!\n 版本号:"+LODOP.VERSION);

};
}
catch(err){
}
};
</script>

2、云打印控件C-Lodop的使用

这个控件和原来的LODOP的使用保持一致性,不用修改原来的代码就可以直接使用最新的打印方式,非常赞,关于这个控件的使用,我在前面介绍了很多相关的使用过程。

如在Winform里面利用网页套打证件的案例《
使用NVelocity生成内容的几种方式
》。

以及在Web页面的套打处理《
Web打印的解决方案之证件套打
》,以及《
Web打印的解决方案之普通报表打印
》,里面对控件的使用操作做了很详细的介绍。

该控件提供了很详细的各种案例(
http://www.lodop.net/demo.html
),可以参考学习使用。

在前面介绍了,我在框架里面的网购运单处理里面,继续使用了这个控件进行套打的处理,例如我们需要套打类似这样的界面内容。

我们设计好套打页面内容,如下所示。

设计好的内容,我们可以把它们转换为页面里面的JS代码如下所示。

    <scriptsrc="/Content/JQueryTools/LODOP/CheckActivX.js"></script>
    <scripttype="text/javascript">
        varLODOP;//声明为全局变量

        functionPreview() {//打印预览
LODOP=getLodop();
LODOP.PRINT_INITA(
-1,-1,824,1129,"运单套打");

CreateLicenseData();
LODOP.SET_PREVIEW_WINDOW(
2,0,0,800,600,"");
LODOP.SET_PRINT_PAGESIZE(
1,0,0,"A4");
LODOP.PREVIEW();
};
functionSetup() {//打印维护 LODOP=getLodop();
LODOP.PRINT_INITA(
-1,-1,824,1129,"运单套打");

CreateLicenseData();
LODOP.PRINT_SETUP();
};
functionDesign() {//打印设计 LODOP=getLodop();
LODOP.PRINT_INITA(
-1,-1,824,1129,"运单套打");

CreateLicenseData();
LODOP.PRINT_DESIGN();
};
functionCreateLicenseData() {if(printID!=undefined&&printID!= '') {//使用同步方式,使得联动的控件正常显示 $.ajaxSettings.async= false;//首先用户发送一个异步请求去后台实现方法 $.getJSON("/BillDetail/FindByID?id=" +printID,function(info) {

LODOP.ADD_PRINT_SETUP_BKIMG(
"<img src='/Content/Template/空白套打模板.png'/>");
LODOP.SET_SHOW_MODE(
"BKIMG_IN_PREVIEW",true);//预览包含背景 LODOP.SET_SHOW_MODE("BKIMG_PRINT",true);//打印内容包含背景 LODOP.ADD_PRINT_BARCODE(78,441,262,56,"128C", info.FenyunDanhao);
LODOP.ADD_PRINT_BARCODE(
684,441,262,56,"128C", info.FenyunDanhao);

LODOP.ADD_PRINT_TEXT(
186,287,277,39, info.Shou_Com);
LODOP.SET_PRINT_STYLEA(
0,"FontSize",12);
LODOP.ADD_PRINT_TEXT(
236,378,186,28, info.Shou_Name);
LODOP.SET_PRINT_STYLEA(
0,"FontSize",12);
LODOP.ADD_PRINT_TEXT(
276,283,284,67, info.Shou_Dizhi);
LODOP.SET_PRINT_STYLEA(
0,"FontSize",12);
LODOP.ADD_PRINT_TEXT(
362,306,254,30, info.Shou_Phone);
LODOP.SET_PRINT_STYLEA(
0,"FontSize",12);
LODOP.ADD_PRINT_TEXT(
404,319,51,30, info.Jianshu);
LODOP.SET_PRINT_STYLEA(
0,"FontSize",12);
LODOP.ADD_PRINT_TEXT(
400,476,95,31, info.Zhongliang);
LODOP.SET_PRINT_STYLEA(
0,"FontSize",12);
LODOP.ADD_PRINT_TEXT(
187,33,227,39, info.Fa_Gongsi);
LODOP.SET_PRINT_STYLEA(
0,"FontSize",12);
LODOP.ADD_PRINT_TEXT(
277,33,230,65, info.Fa_Dizhi);
LODOP.SET_PRINT_STYLEA(
0,"FontSize",12);
LODOP.ADD_PRINT_TEXT(
364,37,227,25, info.Fa_Phone);
LODOP.SET_PRINT_STYLEA(
0,"FontSize",12);
LODOP.ADD_PRINT_TEXT(
410,37,226,26, info.Pinming);
LODOP.SET_PRINT_STYLEA(
0,"FontSize",12);
LODOP.ADD_PRINT_TEXT(
803,34,234,39, info.Fa_Gongsi);
LODOP.SET_PRINT_STYLEA(
0,"FontSize",12);
LODOP.ADD_PRINT_TEXT(
895,32,238,66, info.Fa_Dizhi);
LODOP.SET_PRINT_STYLEA(
0,"FontSize",12);
LODOP.ADD_PRINT_TEXT(
979,33,234,25, info.Fa_Phone);
LODOP.SET_PRINT_STYLEA(
0,"FontSize",12);
LODOP.ADD_PRINT_TEXT(
1024,35,228,25, info.Pinming);
LODOP.SET_PRINT_STYLEA(
0,"FontSize",12);
LODOP.ADD_PRINT_TEXT(
802,285,287,39, info.Shou_Com);
LODOP.SET_PRINT_STYLEA(
0,"FontSize",12);
LODOP.ADD_PRINT_TEXT(
849,373,198,28, info.Shou_Name);
LODOP.SET_PRINT_STYLEA(
0,"FontSize",12);
LODOP.ADD_PRINT_TEXT(
894,285,287,67, info.Shou_Dizhi);
LODOP.SET_PRINT_STYLEA(
0,"FontSize",12);
LODOP.ADD_PRINT_TEXT(
980,287,281,26, info.Shou_Phone);
LODOP.SET_PRINT_STYLEA(
0,"FontSize",12);
LODOP.ADD_PRINT_TEXT(
1021,317,59,29, info.Jianshu);
LODOP.SET_PRINT_STYLEA(
0,"FontSize",12);
LODOP.ADD_PRINT_TEXT(
1017,477,95,30, info.Zhongliang);
LODOP.SET_PRINT_STYLEA(
0,"FontSize",12);
});
}
}
//打印预览 varprintID;functionPrintDetail() {varrow=$("#gridDetail").datagrid("getSelected");if(row) {varindex=$('#gridDetail').datagrid('getRowIndex', row);
printID
=row.ID;
Preview();
}
else{
$.messager.alert(
"提示","请选择一个记录");
}
}
//批量打印 functionBatchPrint() {//得到用户选择的数据的ID varrows=$("#gridDetail").datagrid("getSelections");if(rows.length>= 1) {//然后确认发送异步请求的信息到后台删除数据 $.messager.confirm("批量打印确认","您确认批量打印选定的记录吗?",function(action) {if(action) {for(vari= 0; i<rows.length; i++) {
LODOP
=getLodop();
LODOP.PRINT_INIT(
"");
LODOP.SET_PRINT_PAGESIZE(
1,0,0,"A4");

printID
=rows[i].ID;
CreateLicenseData();

LODOP.PRINT();
}
}
});
}
else{
$.messager.alert(
"提示","请选择你要批量打印的数据");
}
}
//打印维护 functionPrintSetup() {varrow=$("#gridDetail").datagrid("getSelected");if(row) {varindex=$('#gridDetail').datagrid('getRowIndex', row);
printID
=row.ID;
Setup();
}
}
</script>

其中这个打印界面中,还用到了二维码的打印处理操作,这样很方便直接使用条码枪直接读取,如下代码:

LODOP.ADD_PRINT_BARCODE(78, 441, 262, 56, "128C", info.FenyunDanhao);
LODOP.ADD_PRINT_BARCODE(684, 441, 262, 56, "128C", info.FenyunDanhao);

然后在主页面里面设定打印的操作功能按钮进行处理,下面是我们基于EasyUI的Web框架界面效果。

打印预览界面如下所示,实际打印的时候,我们可以设定不打印背景图片。

如果是常规打印,那么他们的界面和我们看到的页面内容非常接近,如下所示。

参考代码如下所示。

@*添加对LODOP控件的支持*@<scripttype="text/javascript">
        varLODOP;//声明为全局变量

        functionPreview() {//正本打印
CreateLicenseData();
LODOP.SET_SHOW_MODE(
"PREVIEW_IN_BROWSE",1);
LODOP.PREVIEW();
};
functionPrintA() {
CreateLicenseData();
LODOP.PRINTA();
};
functionSetup() {//正本打印维护 CreateLicenseData();
LODOP.PRINT_SETUP();
};
functionDesign() {//正本打印设计 CreateLicenseData();
LODOP.PRINT_DESIGN();
};
functionCreateLicenseData() {
LODOP
=getLodop();

LODOP.PRINT_INIT(
"政策法规");varstrBodyStyle= "<link type='text/css' rel='stylesheet' href='/Content/Themes/Default/style.css' /><style><!--table { border:1;background-color: #CBCBCC } td {background-color:#FFFFFE;border: 1; } th { background-color:#F1F1F3;padding-left:5px;border:1}--></style>";varstrFormHtml=strBodyStyle+ "<body>" +document.getElementById("printContent").innerHTML+ "</body>";
LODOP.ADD_PRINT_HTM(
20,40,610,900, strFormHtml);
LODOP.PREVIEW();
}
functionSaveAs() {varid=$('#ID2').val();
window.open(
'/Information/ExportWordById?id=' +id );
}
</script>

以上就是基于新版云打印控件C-Lodop的使用效果和代码,希望对大家有所帮助。

在常规的后台管理系统或者前端界面中,一般都有一个导航菜单提供给用户,方便选择所需的内容。基于Metronic的Bootstrap开发框架,是整合了Metroinc样式,以及Boostrap组件模块的内容,因此菜单的效果自然也是和Bootstrap一脉相承的。基于经常使用的几种菜单样式,本文进行了相关的介绍和展示。

1、菜单展示的三种方式

菜单的展示可以做成各种各样的效果,本文只是针对性的介绍其中的三种,两种左侧竖放的菜单,一种是水平横放的菜单。

样式1:

菜单收缩后界面如下所示。

样式2:

样式3:

水平样式的菜单如下所示:

2、几种布局的组织方式

由于这几种布局方式,它们的框架布局页面(也就是Layout页面)内容有所不同,一般情况下我们可以把它分别定义为一种Layout页面,这样在实际使用的时候包含它即可。

我根据上面几种样式,分别定义了几个不同的Layout页面,如下所示

一般情况下,我们生成一个视图页面,会提示我们选择不同的视图布局页面的,确认之后会在页面顶部应用对应的Layout页面。

如果我们需要动态指定页面的布局内容,可以在上面使用一个变量来处理,如下代码所示。

@{
Layout
= ConfigData.ViewLayoutFile;//"~/Views/Shared/_Layout.cshtml"; ViewBag.Title = "角色信息";
}

实际上,我们一般的页面布局确定好后,就相对比较少变更的,所以为了不打破这个生成页面的布局内容,我们可以把前面处理好的Layout1/2/3等这些复制到Layout页面里面进行处理即可。

不过页面里面还是需要根据不同布局页面,指定不同的资源的。

我们添加样式的资源如下所示。

            //开始全局必需样式引用
            css_metronic.Include("~/Content/metronic/assets/global/plugins/font-awesome/css/font-awesome.min.css","~/Content/metronic/assets/global/plugins/simple-line-icons/simple-line-icons.min.css","~/Content/metronic/assets/global/plugins/bootstrap/css/bootstrap.min.css","~/Content/metronic/assets/global/plugins/bootstrap-switch/css/bootstrap-switch.min.css",

..........................
//主题全局样式 "~/Content/metronic/assets/global/css/components-rounded.css","~/Content/metronic/assets/global/css/plugins.min.css",//主题布局样式 "~/Content/metronic/assets/layouts/" + Layout + "/css/layout.css","~/Content/metronic/assets/layouts/" + Layout + "/css/themes/default.min.css","~/Content/metronic/assets/layouts/" + Layout + "/css/custom.min.css");

上面红色的Layout是我们根据不同布局页面获得的一个变量,从而能够使得不同布局页面的样式和脚本顺利引入的。

    /// <summary>
    ///系统定义的一些常用变量/// </summary>
    public classConfigData
{
/// <summary> ///Web开发框架所属的系统类型定义/// </summary> public const string SystemType = "WareMis";/// <summary> ///配置视图的样式布局/// </summary> public static LayoutType Layout =LayoutType.Layout2;
}
/// <summary> ///布局样式枚举/// </summary> public enumLayoutType
{
Layout,
Layout2,
Layout3
}

3、菜单的动态生成

为了实现菜单动态的生成,我们需要了解这几种方式的菜单结构,然后我们根据它们的特点进行数据结构的生成即可。

第一第二种布局方式的菜单结构数据是一样的,它们的内容如下所示。

                <ulclass="page-sidebar-menu page-header-fixed "data-keep-expanded="false"data-auto-scroll="true"data-slide-speed="200">
                    <liclass="nav-item start"id="1">
                        <ahref="/Home/index?tid=1"class="nav-link nav-toggle">
                            <iclass="icon-home"></i>
                            <spanclass="title">首页</span>
                            <spanclass="selected"></span>
                            <spanclass="arrow open"></span>
                        </a>
                    </li>@Html.Raw(@ViewBag.MenuString)</ul>

我们可以通过后台生成:
@Html.Raw(@ViewBag.MenuString)

然后输出的界面里即可。

我们来看看前两种菜单的结构,简单的结构如下所示

                    <liclass="nav-item start"id="1">
                        <ahref="/Home/index?tid=1"class="nav-link nav-toggle">
                            <iclass="icon-home"></i>
                            <spanclass="title">首页</span>
                            <spanclass="selected"></span>
                            <spanclass="arrow open"></span>
                        </a>
                    </li>

如果有子菜单的,那么两级菜单的结构如下所示。

            <liclass="nav-item "id="3">
                <ahref="javascript:;"class="nav-link nav-toggle">
                    <iclass="icon-rocket"></i>
                    <spanclass="title">客户管理</span>
                    <spanclass="arrow"></span>
                </a>
                <ulclass="sub-menu">
                    <liclass="nav-item "id="31">
                        <ahref="second?tid=3&sid=31">
                            <iclass="glyphicon glyphicon-th-list"></i>
                            <spanclass="title">客户管理</span>
                        </a>
                    </li>
                    <liclass="nav-item  "id="32">
                        <ahref="second?tid=3&sid=32">
                            <iclass="icon-basket"></i>
                            <spanclass="badge badge-roundless badge-danger">new</span>
                            <spanclass="title">客户联系人</span>
                        </a>
                    </li>
                </ul>
            </li>

如果是三级菜单的结构,如下所示:

          <liclass="nav-item active open"id="2">
                <ahref="javascript:;"class="nav-link nav-toggle">
                    <iclass="icon-basket"></i>
                    <spanclass="title">行业动态</span>
                    <spanclass="selected"></span>
                    <spanclass="arrow open"></span>
                </a>
                <ulclass="sub-menu">
                    <liclass="nav-item  active open"id="21">
                        <ahref="javascript:;"class="nav-link nav-toggle">
                            <iclass="icon-home"></i>
                            <spanclass="title">行业动态</span>
                            <spanclass="arrow"></span>
                            <spanclass="selected"></span>
                        </a>
                        <ulclass="sub-menu">
                            <liclass="nav-item  active open"id="211">
                                <ahref="second?tid=2&sid=21&ssid=211">
                                    <iclass="icon-home"></i>
                                    <spanclass="badge badge-danger">4</span>
                                    <spanclass="title">政策法规</span>
                                </a>
                            </li>
                            <liclass="nav-item "id="212">
                                <ahref="second?tid=2&sid=21&ssid=212">
                                    <iclass="icon-basket"></i>
                                    <spanclass="badge badge-warning">4</span>
                                    <spanclass="title">通知公告</span>
                                </a>
                            </li>
                        </ul>
                    </li>
                </ul>
            </li>

根据这些内容,我们在后台动态输出菜单结构即可。

例如我们在基类控制器里面,通过判断视图的处理,实现菜单内容的输出。

        /// <summary>
        ///重载视图展示界面,方便放置一些常规的ViewBag变量。///如果放在OnActionExecuting,则会导致任何请求都会执行一次,从而导致多次执行,降低响应效率/// </summary>
        protected override ViewResult View(string viewName, string masterName, objectmodel)
{
//登录信息统一设置 if (CurrentUser != null)
{
ViewBag.FullName
=CurrentUser.FullName;
ViewBag.Name
=CurrentUser.Name;//ViewBag.MenuString = GetMenuString(); ViewBag.MenuString = GetMenuStringCache(); //使用缓存,隔一段时间更新 }return base.View(viewName, masterName, model);
}

为了提高菜单的响应处理,避免多次递归耗时,我们可以把生成好的菜单内容,放到缓存里面,通过键值实现处理。

        /// <summary>
        ///使用分布式缓存实现菜单数据的缓存处理/// </summary>
        /// <returns></returns>
        public stringGetMenuStringCache()
{
string itemValue = MemoryCacheHelper.GetCacheItem<string>("GetMenuStringCache_" + CurrentUser.ID , delegate()
{
returnGetMenuString();
},
null, DateTime.Now.AddMinutes(5) //5分钟以后过期,重新获取 );returnitemValue;
}

而对于水平方向的菜单处理,和前面两者有所不同。

如二级菜单数据展示如下所示。

                        <liclass="menu-dropdown classic-menu-dropdown ">
                            <ahref="index.html"class="nav-link  ">
                                <iclass="icon-bar-chart"></i>Default<spanclass="arrow"></span>
                            </a>
                        </li>
                        <liclass="menu-dropdown classic-menu-dropdown active">
                            <ahref="javascript:;"class="nav-link nav-toggle ">
                                <iclass="icon-basket"></i>Dashboard<spanclass="arrow"></span>
                            </a>
                            <ulclass="dropdown-menu pull-left">
                                <liclass=" active">
                                    <ahref="index.html"class="nav-link  active">
                                        <iclass="icon-bar-chart"></i>Dashboard<spanclass="arrow"></span>
                                    </a>
                                </li>
                            </ul>
                        </li>

三级菜单展示的数据如下所示。

                        <liclass="menu-dropdown classic-menu-dropdown ">
                            <ahref="javascript:;"class="nav-link nav-toggle ">
                                <iclass="icon-briefcase"></i>Pages<spanclass="arrow"></span>
                            </a>
                            <ulclass="dropdown-menu pull-left">
                                <liclass="dropdown-submenu ">
                                    <ahref="javascript:;"class="nav-link nav-toggle ">
                                        <iclass="icon-basket"></i>eCommerce<spanclass="arrow"></span>
                                    </a>
                                    <ulclass="dropdown-menu">
                                        <liclass=" ">
                                            <ahref="ecommerce_index.html"class="nav-link ">
                                                <iclass="icon-home"></i>Dashboard</a>
                                        </li>
                                        <liclass=" ">
                                            <ahref="ecommerce_orders.html"class="nav-link ">
                                                <iclass="icon-basket"></i>Orders</a>
                                        </li>
                                    </ul>
                                </li>

                                <liclass="dropdown-submenu">
                                    <ahref="javascript:;"class="nav-link nav-toggle">
                                        <iclass="icon-notebook"></i>Login<spanclass="arrow"></span>
                                    </a>
                                    <ulclass="dropdown-menu pull-left">
                                        <liclass="">
                                            <ahref="page_user_login_1.html"class="nav-link "target="_blank"> Login Page 1 </a>
                                        </li>
                                        <liclass="">
                                            <ahref="page_user_login_2.html"class="nav-link "target="_blank"> Login Page 2 </a>
                                        </li>
                                    </ul>
                                </li>
                            </ul>
                        </li>

我们所需要做的就是把我们数据库里面的菜单数据,动态构建这种递进式的数据结构即可。

如果感兴趣Bootstrap开发框架系列,可以参考学习下面的文章,感谢您的阅读。

基于Metronic的Bootstrap开发框架经验总结(10)--优化Bootstrap图标管理

在MVC控制器里面使用dynamic和ExpandoObject,实现数据转义的输出

在我自己的《Web开发框架》中,用了很多年的EasyUI,最新版本EasyUI为1.4.5,随着版本的更新,其很多功能得到了很大的完善和提高,同时也扩展了一些新的功能,以前在布局和对话框弹出层的自动适应大小的问题,也在最近的一些版本得到了解决,本文在迁移到最新EasyUI版本的时候,总结了一些经验,希望对大家使用这个强大的Web界面组件有所帮助。

1、Web主界面的布局调整

上面的布局是顶部内容+一级菜单、左边菜单,右边主内容为页面内容,页面内容是变化的内容,其他部分为不变的,这样的布局代码如下所示。

<!DOCTYPE html>
<html>
<bodystyle="overflow-y:hidden;"scroll="no">
    <divclass="easyui-layout"data-options="fit:true,border:false">
            <!--顶部Banner-->
            <divregion="north"id="header">
            
            </div>

            <!--左侧导航菜单-->
            <divregion="west"split="true"title="导航菜单"style="width:200px;padding:1px;overflow:hidden;">
            
            </div>

            <!--主工作区-->
            <divid="mainPanle"region="center"title=""style="overflow:hidden;">
            
            </div>

            <!--底部版权标识-->
            <divdata-options="region:'south',split:true"style="height: 40px;background: #D2E0F2;">
            
            </div>
            
    </div>
</body>

</html>

在上面的主页面布局代码里面,<div class="easyui-layout" 外面注意不要包含有Form的标志,否则会出现一些莫名其妙的错误。我们通过
data-options="fit:true,border:false"
来设定布局的自动适应,这样在放大缩小页面的时候,布局总是能够自动适应页面的变化的。

2、DataGrid表格的自动调整处理

在之前的一些版本里面,DataGrid总是没有能够自动实现宽度的自动调整,为了实现这种效果,还需要添加一些JS代码进行处理,这种方式在现在DataGrid支持宽度百分比的属性后,变得简单容易了。

表格的HTML代码如下所示。

            <!-------------------------------详细信息展示表格----------------------------------->
            <tableid="grid"title="用户操作"data-options="iconCls:'icon-view'"></table>

而其中我们自动通过JQuery赋值的JS代码如下所示。

        //实现对DataGird控件的绑定操作
        functionInitGrid(queryData) {
$(
'#grid').datagrid({ //定位到Table标签,Table标签的ID是grid url: '/User/FindWithPager', //指向后台的Action来获取当前用户的信息的Json格式的数据 title: '系统用户信息',
iconCls:
'icon-view',
height:
650,
width:
'100%',
nowrap:
true,
autoRowHeight:
true,
striped:
true,
collapsible:
true,
pagination:
true,
pageSize:
50,
pageList: [
50, 100, 200],
rownumbers:
true,//sortName: 'ID', //根据某个字段给easyUI排序 sortOrder: 'asc',
remoteSort:
false,//idField: 'ID', //不设置idField,翻页不会记录选择 queryParams: queryData, //异步查询的参数 columns: [[
{ field:
'ck', checkbox: true }, //选择 { title: 'ID', field: 'ID', width: 80, sortable: true},
{ title:
'用户编码', field: 'HandNo', width: 80, sortable: true},
{ title:
'用户名/登录名', field: 'Name', width: 120, sortable: true},
{ title:
'真实姓名', field: 'FullName', width: 80, sortable: true},
{
title:
'是否过期', field: 'IsExpire', width: 80, formatter: function(val, rowdata, index) {if(val) {return '<a class="grid_unvisible" href="javascript:void(0)" >' + val + '</a>';
}
else{return '<a class="grid_visible" href="javascript:void(0)" >' + val + '</a>';
}
}
},
....................................
]],
onLoadSuccess:
function() {
$(
".grid_visible").linkbutton({ text: '可用', plain: true, iconCls: 'icon-ok'});
$(
".grid_unvisible").linkbutton({ text: '过期', plain: true, iconCls: 'icon-stop'});

$(
".grid_normal").linkbutton({ text: '正常', plain: true, iconCls: 'icon-ok'});
$(
".grid_deleted").linkbutton({ text: '已删除', plain: true, iconCls: 'icon-stop'});
},

toolbar: [{
id:
'btnAdd',
text:
'添加',
iconCls:
'icon-add',
handler:
function() {
ShowAddDialog();
//实现添加记录的页面 }
},
'-', {
id:
'btnEdit',
text:
'修改',
iconCls:
'icon-edit',
handler:
function() {
ShowEditOrViewDialog();
//实现修改记录的方法 }
},
'-', {
id:
'btnDelete',
text:
'删除',
iconCls:
'icon-remove',
handler:
function() {
Delete();
//实现直接删除数据的方法 }
},
'-', {
id:
'btnView',
text:
'查看',
iconCls:
'icon-table',
handler:
function() {
ShowEditOrViewDialog(
"view");//实现查看记录详细信息的方法 }
},
'-', {
id:
'btnReload',
text:
'刷新',
iconCls:
'icon-reload',
handler:
function() {//实现刷新栏目中的数据 $("#grid").datagrid("reload");
}
}],
onDblClickRow:
function(rowIndex, rowData) {
$(
'#grid').datagrid('uncheckAll');
$(
'#grid').datagrid('checkRow', rowIndex);
ShowEditOrViewDialog();
}
});
};

为了实现其自动伸缩,我们只需要设置
width:
'100%'
就可以了,不需要像以前那样需要自动宽度。

3、对话框的自动调整处理

在我们《Web开发框架》里面,利用DIV层作为界面的子窗口是一种很常见的处理方式,因此弹出的对话框层需要设定好其对应的自动缩放效果。

特别是由于不同的电脑设备上,笔记本和台式电脑的高度不同,还有分辨率不同,因此它们的宽度高度需要非常灵活的自动调整处理,实现我们界面的一致性。

以上界面我们通过样式的调整就可以实现对话框大小的变化,以及跟随整体布局的调整,总体居中的效果了。

    <!--------------------------添加信息的弹出层---------------------------->
    <divid="DivAdd"class="easyui-dialog"style="width:98%;max-width:824px;height:98%;max-height:300px;"closed="true"resizable="true"modal="true"data-options="iconCls: 'icon-add',buttons: '#dlg-buttons', onResize:function(){$(this).dialog('center');}">

   </div>

其中
style="width:98%;max-width:824px;height:98%;max-height:300px;"
确保了对话框不会太大变得难看,我们让它保持一个较好的大小。

其中
onResize:function(){$(this).dialog('center');}
让它在布局变化的时候自动把对话框居中,这样效果更加好看。

以上就是我们通常在实现布局和对话框自动适应大小所做的一些处理,能够很好的适应我们不同的设备分辨率。

使用博客园写博客也有10年有余了,对博客园是有一种莫名的亲切感和深刻的感情的,现在则是从博客园迁移到简书平台。这10多年来一直坚持写着博客,也是对自己的一个很好的技术历程总结。这十多年来,每次学习了一些新的技术,或者有一些感兴趣的方向,都会通过随笔进行记录,有时候也会总结很多自己的开发成果,随着技术路线的成熟,基本上是分享我的Winform界面技术、模块设计思路、代码生成工具、分页控件、公用类库、Visio的C#二次开发、Winfrom/Web/微信方面框架开发,以及各种各样的技术总结。

1、博客迁移到简书的背景历程

以前博客园还在发展期,技术话题可以无所不谈,没有任何避讳,可以介绍下自己的研究所得,也可以介绍自己的技术产品;不过这几年博客园不喜欢任何掺杂产品宣传、广告的文章,即使是比较有技术分享的文章也是禁止一些宣传的,因此写博客很长一段时间,总是小心翼翼把自己软件界面的公司名称、主界面涂改再放上去,搞得很是麻烦。偶尔触犯就被撤下主页,曾经有一次被取消首页发布几个月,这次又来,不让继续发布,因此不太喜欢这种粗暴的方式。

不过回头想想,不让发布首页也无妨,自己可以没有任何内容负担,可以按自己喜欢的方式总结技术内容,即使带上自己的一些产品信息也无妨。同时,还是决定找一个相对比较宽松的博客环境,看过简书、51CTO等一些博客平台,简书给我的印象非常好,从手机阅读书写,以及内容的管理方面,电脑端的处理方式也是非常不错的体验,特别是一些分享也可以做到实时处理,可以在手机自己找到自己的文章发给我自己的客户或者朋友,非常方便。

博客园相对来说,是比较简陋而且移动支持不够的一个博客社区,不过好在主要专注的方向还是.NET,简书从写文章以及管理的角度来感受,是一种完全不同的感觉,你会被感觉作者受到非常特别的优待,而且使用起来也是非常方便,特别是使用Markdow的书写方式,逐渐成为我博客的书写习惯了。

这次决定从博客园迁移到简书来说,也是一个比较艰难的决定,一方面在博客园写了十余年的博客,随笔文章有400多篇,同时也积攒了很多热心的粉丝和朋友,迁移意味着以后的重点在简书里面,同时也希望把随笔文章迁移到简书上,毕竟自己这么多年随笔,也是一笔非常可观的财富和人生经历,每篇随笔少则一两个小时,多则是一两周的研究成果总结而出的随笔。随笔记录着我技术的闪光点,也记录着在研究过程的心路历程,每每看着自己几年前的随笔,心情澎湃,有一种故地重游的熟悉感。因此,自己写博客,也尽可能把一些重要的、特别的、闪光着思考思想的内容写出来,寄希望读者和我共鸣,也希望自己每每返回看自己的随笔,有一种幡然醒悟的感觉。

2、简书及其使用总结

简书使用中文拼音作为其域名(
http://www.jianshu.com/
),简书非常方便的特点是可以随时随地用手机维护自己的博客或者查看其它博客文章,如我的简书博客地址如下(
http://www.jianshu.com/users/5c9663046ca3
)。

移动端的页面效果如下所示:

而其电脑页面效果如下所示。

无论在电脑端,还是移动端,简书的整体界面使用起来是非常舒服的,这也是我最终选择简书作为我博客迁移的归宿的重要原因。

从博客园到简书的迁移过程中,花了一周多的时间完成了100多篇随笔的迁移工作,后面我会逐渐把我自己写的大量博客迁移过来,并在今后把简书作为优先采用的博客平台。

我使用Chrome浏览器作为处理的浏览器,因此基本上把内容复制过去,图片也会自动上传的,不过如果是代码,则需要使用Markdown格式进行处理一下,其他标题也是如此,下面我会介绍一下,常见使用的Markdown语法即可。

3、常用的Markdown语法介绍

如果是用惯了富文本的模式,特别是搞开发的,我们需要粘贴代码,可能有点不太习惯在简书里面编辑,不过我们可以采用简书里面的Markdown编辑器进行编辑,这种是非常方便、强大的格式编辑语法,在简书里面我推荐使用。

一般来说,如果我们进行博客迁移,图片会重新上传到简书的空间(这点很好,防止网站盗链保护不显示),连接会自动处理,如果是本地要上传图片,可以打开图片复制后在浏览器粘贴,图片会自动上传并添加连接的,不过如果是表格这需要另外处理(比较讨厌没有自动处理,我一般为了方便直接截图上传)。

Markdown 是一种用来写作的轻量级
「标记语言」
,它用简洁的语法代替排版,而不像一般我们用的字处理软件
Word

Pages
有大量的排版、字体设置。它使我们专心于码字,用「标记」语法,

下面几种是我们编辑随笔的时候,经常用到的Markdown语法。

标题

这是最为常用的格式,在平时常用的的文本编辑器中大多是这样实现的:输入文本、选中文本、设置标题格式。

而在 Markdown 中,你只需要在文本前面加上
#
即可,同理、你还可以增加二级标题、三级标题、四级标题、五级标题和六级标题,总共六级,只需要增加
#
即可,标题字号相应降低。例如:

# 一级标题
## 二级标题
### 三级标题
#### 四级标题
##### 五级标题
###### 六级标题

注: # 和「一级标题」之间建议保留一个字符的空格,这是最标准的 Markdown 写法。

列表

列表格式也很常用,在 Markdown 中,你只需要在文字前面加上
-
就可以了,例如:

- 文本1
- 文本2
- 文本3

如果你希望有序列表,也可以在文字前面加上
1.
2.
3.
就可以了,例如:

1. 文本1
2. 文本2
3. 文本3

注: - 1. 和文本之间要保留一个字符的空格。

链接和图片

在 Markdown 中,插入链接不需要其他按钮,你只需要使用
[显示文本](链接地址)
这样的语法即可,例如:

[简书](http://jianshu.io)

在 Markdown 中,插入图片不需要其他按钮,你只需要使用
![](图片链接地址)
这样的语法即可,例如:

![](http://ww4.sinaimg.cn/bmiddle/aa397b7fjw1dzplsgpdw5j.jpg)

注:插入图片的语法和链接的语法很像,只是前面多了一个

引用

在我们写作的时候经常需要引用他人的文字,这个时候引用这个格式就很有必要了,在 Markdown 中,你只需要在你希望引用的文字前面加上
>
就好了,例如:

> 一盏灯, 一片昏黄; 一简书, 一杯淡茶。 守着那一份淡定, 品读属于自己的寂寞。 保持淡定, 才能欣赏到最美丽的风景! 保持淡定, 人生从此不再寂寞。

注: > 和文本之间要保留一个字符的空格。

最终显示的就是:

粗体和斜体

Markdown 的粗体和斜体也非常简单,用两个
*
包含一段文本就是粗体的语法,用一个
*
包含一段文本就是斜体的语法。例如:

 *一盏灯*, 一片昏黄;**一简书**, 一杯淡茶。 守着那一份淡定, 品读属于自己的寂寞。 保持淡定, 才能欣赏到最美丽的风景! 保持淡定, 人生从此不再寂寞。

最终显示的就是下文,其中「一盏灯」是斜体,「一简书」是粗体:

一盏灯
, 一片昏黄;
一简书
, 一杯淡茶。 守着那一份淡定, 品读属于自己的寂寞。 保持淡定, 才能欣赏到最美丽的风景! 保持淡定, 人生从此不再寂寞。

最终显示的就是:

表格

表格是我觉得 Markdown 比较累人的地方,例子如下:

| Tables        | Are           | Cool  |
| ------------- |:-------------:| -----:|
| col 3 is | right-aligned | $1600 |
| col 2 is | centered | $12 |
| zebra stripes | are neat | $1 |

这种语法生成的表格如下:

Tables Are Cool
col 3 is right-aligned $1600
col 2 is centered $12
zebra stripes are neat $1

代码框

如果你是个程序猿,需要在文章里优雅的引用代码框,在 Markdown下实现也非常简单,只需要用三个 ` 把中间的代码包裹起来。图例:

得到的效果如下所示