麻雀虽小五脏俱全

mikel阅读(1270)

很多时候,程序员面临的需求就是这么个问题,客户觉得简单的不行的玩意儿,到你那怎么就那么多问题,可现实中的问题不是什么都可以用程序能够办到的,因为整个流程当中变数最大的因素就是人为因素,你想不到的一个很小的问题就会让整个程序陷入混乱之中,所以说很多人都说程序员想得太多了,想得太复杂了。

变化是不可能消除的,但是可以避免影响最大化,健全的程序应该是将变化造成的混乱的机会降到最低,很多时候不得不让人工参与进来,程序本来就是一个矛盾的综合体,想要解放人工又不得不依赖人工处理。

现在互联网变得普及了,人们就认为网络平民化软件也更加智能了,的确这是个发展趋势,人工智能迟早会走进千家万户,机器人也无可避免的会出现来解放人工的重复劳动,但是绝对不会取代人,因为很多时候人是这个变化世界的起因,形形色色的人改变了这个世界也让这个世界充满了变化因素,如果哪天人的行为和思想不变了,那这个世界就是个机器了。

没有不变的需求,所以会有人提出敏捷开发、迭代、重构,这些都是程序员用于适应变化想出的办法,当客户提出一个需求来的时候也就是变化的开始,从每个需求点都可以深挖出更多的需求,当你被缠住的时候你就输了,所以要跳出来看看是不是真的需求,或者需求背后客户想要什么?这样可能会好些,不要拘泥于形式,把握住根本就行了。

[转载]C#读写TxT文件 - 碧风Befeng - 博客园

mikel阅读(1361)

[转载]C#读写TxT文件 – 碧风Befeng – 博客园.

文/嶽永鹏

WPF 中读取和写入TxT 是经常性的操作,本篇将从详细演示WPF如何读取和写入TxT文件。

首先,TxT文件希望逐行读取,并将每行读取到的数据作为一个数组的一个元素,因此需要引入List<string> 数据类型。且看代码:

复制代码
 public List<string> OpenTxt(TextBox tbx)
        {
            List<string> txt = new List<string>();
            OpenFileDialog openFile = new OpenFileDialog();
            openFile.Filter = "文本文件(*.txt)|*.txt|(*.rtf)|*.rtf";
            if (openFile.ShowDialog() == true)
            {
                tbx.Text = "";
                using (StreamReader sr = new StreamReader(openFile.FileName, Encoding.Default))
                {
                    int lineCount = 0;
                    while (sr.Peek() > 0)
                    {
                        lineCount++;
                        string temp = sr.ReadLine();
                        txt.Add(temp);
                    }
                }

            }
            return txt;
        }
复制代码

其中

复制代码
 1  using (StreamReader sr = new StreamReader(openFile.FileName, Encoding.Default))
 2 {
 3         int lineCount = 0;
 4         while (sr.Peek() > 0)
 5               {
 6                     lineCount++;
 7                     string temp = sr.ReadLine();
 8                     txt.Add(temp);
 9                }
10 }
复制代码

StreamReader 是以流的方式逐行将TxT内容保存到List<string> txt中。

其次,对TxT文件的写入操作,也是将数组List<string> 中的每个元素逐行写入到TxT中,并保存为.txt文件。且看代码:

复制代码
 1 SaveFileDialog sf = new SaveFileDialog();
 2                 sf.Title = "Save text Files";
 3                 sf.DefaultExt = "txt";
 4                 sf.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*";
 5                 sf.FilterIndex = 1;
 6                 sf.RestoreDirectory = true;
 7                 if ((bool)sf.ShowDialog())
 8                 {
 9                     using (FileStream fs = new FileStream(sf.FileName, FileMode.Create))
10                     {
11                         using (StreamWriter sw = new StreamWriter(fs, Encoding.Default))
12                         {
13                             for (int i = 0; i < txt.Count; i++)
14                             {
15                                 sw.WriteLine(txt[i]);
16                             }
17                         }
18                     }
19 
20                 }
复制代码

而在这之中,相对于读入TxT文件相比,在写的时候,多用到了 FileStream类。

[转载]实现动态的XML文件读写操作(依然带干货) - 悲夢 - 博客园

mikel阅读(1129)

[转载]实现动态的XML文件读写操作(依然带干货) – 悲夢 – 博客园.

前言

最近由于项目需求,需要读写操作XML文件,并且存储的XML文件格式会随着导入的数据不同而随时改变(当然导入的数据还是有一定约束的),这样我们要 预先定义好XML文件的格式就不太现实了,如何实现不管导入的数据如何变化,我都能正确的把数据解析出来,这就是要实现的动态的XML文件读写操作!如果 大家有更好的方式欢迎交流!

具体实现

本文所实现的读写XML文件是使用序列话的方式,具体博文请参考:http://www.cnblogs.com/fish-li/archive/2013/05/05/3061816.html,当然如果只是序列化操作XML文件的话,只需要看这篇博文,也就不需要这篇文章了!

了解了如何序列化操作XML文件了话,你就会知道,要想写入和读取XML文件,我们就需要定义好一个数据类型(这一点很重要),但是问题就出现了,如果我们在编程时就定义好一个数据类型了,当导入的数据改变了,这个数据类型就不适合了,接下来就来解决这个问题

实现一:根据需求创建动态数据类型

废话先不多说,先上代码,代码说话,往往比语言来的直接也更容易懂

复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;
using System.Diagnostics;

namespace XMLDemo
{
    public class ReflectClass
    {
        //保存动态生成并编译的类的type对象   
        Type theType = null;
        //保存动态生成类的实例   
        object theClass = null;
        public Assembly DoOperation()
        {
            //未初始化   
            if (theType == null)
            {
                //初始化   
                return GenerateCode();
            }
            return null;
        }
        private Assembly GenerateCode()
        {
            //文件名   
            string fileName = "XmlClass";
            ////从编译好的dll文件load一个Assembly   
            //Assembly a = Assembly.LoadFrom(fileName + ".dll");

            //从编译好的dll文件load一个Assembly
            byte[] filedata = File.ReadAllBytes(fileName + ".dll");
            Assembly a = Assembly.Load(filedata);                      //此方法在使用完dll文件后会自动释放资源
            return a;
        }
        public Assembly DoOperation(List<ExcelData> list)
        {
            //未初始化   
            if (theType == null)
            {
                //初始化   
                return GenerateCode(list);
            }
            return null;
        }
        private Assembly GenerateCode(List<ExcelData> list)
        {
            //文件名   
            string fileName = "XmlClass";
            //打开文件,如果不存在,则创建   
            Stream s = File.Open(fileName + ".cs", FileMode.Create);
            //创建一个StreamWriter来写入数据   
            StreamWriter wrtr = new StreamWriter(s);

            #region 写入动态创建类的源代码 
            wrtr.WriteLine("// 动态创建的XmlClass类文件");

            //类名,此类是为序列化读取XML文件创建的类文件   
            string className = "XmlClass";
            wrtr.WriteLine("using System;");
            wrtr.WriteLine("using System.Xml.Serialization;");
            wrtr.WriteLine("public class {0}", className);
            wrtr.WriteLine("{");
            var ch = (from num in list select num.Mark).Distinct().ToList();
            foreach (var n in ch)
            {
                wrtr.WriteLine("\tpublic " + n + " " + n);
                wrtr.WriteLine("\t{");
                wrtr.WriteLine("\t\tget;set;");
                wrtr.WriteLine("\t}");
            }
            wrtr.WriteLine("}");
            foreach (var n in ch)
            {
                wrtr.WriteLine("public class {0}", n);
                wrtr.WriteLine("{");
                var nlist = from num in list where (num.Mark == n) select num;
                foreach (var m in nlist)
                {
                    wrtr.WriteLine("\tpublic string " + m.Name);
                    wrtr.WriteLine("\t{");
                    wrtr.WriteLine("\t\tget;set;");
                    wrtr.WriteLine("\t}");
                }
                wrtr.WriteLine("}");
            }
            //关闭StreamWriter和文件   
            wrtr.Close();
            s.Close(); 
            #endregion

            //启动进程编译源文件   
            //指定参数   
            ProcessStartInfo psi = new ProcessStartInfo();
            File.Delete(fileName + ".dll");                   //重新导入数据时删除旧的DLL文件
            //启动cmd.exe   
            psi.FileName = "cmd.exe";
            //cmd.exe的参数,/c-close,完成后关闭;后为参数,指定cmd.exe使用csc来编译刚才生成的源文件   
            string compileString = "/c C:\\WINDOWS\\Microsoft.NET\\Framework\\v4.0.30319\\csc.exe /optimize+ /target:library {0}.cs";
            psi.Arguments = String.Format(compileString, fileName);
            //运行时的风格-最小化   
            psi.WindowStyle = ProcessWindowStyle.Minimized;
            //启动进程   
            Process proc = Process.Start(psi);
            //指定当前在此进程退出前等待   
            proc.WaitForExit();
            //从编译好的dll文件load一个Assembly
            byte[] filedata = File.ReadAllBytes(fileName + ".dll");
            Assembly a = Assembly.Load(filedata);                      //此方法在使用完dll文件后会自动释放资源
            //Assembly a = Assembly.LoadFrom(fileName + ".dll");       //这样使用dll文件会一直被占用,不会释放,造成重新生成前无法自动删除
            //删除源文件   
            //File.Delete(fileName + ".cs");
            return a;
        }
    }
}
复制代码

以上 ReflectClass类帮助我们实现了通过导入的数据来创建一个供我们使用的编译好的类文件,下面主要讲如果通过这个使用这个编译好的dll文件来写入我们的XML文件

写入动态创建类的源代码那一块真是费了我不少脑细胞,感觉跟写模板类似的!(大家如果根据自己需求改写的话,应该就会体会到)

实现二:通过反射得到动态创建的数据类型并序列化写入XML文件

还是先上代码,XMLHelper帮助类可以去上面的连接去下载,也可以在我提供的Demo里下载,我只根据我的需要添加了一个方法,下面我的一个普通帮助类CommonHelper

复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace XMLDemo
{
    public class CommonHelper
    {
        /// <summary>
        /// 写文件
        /// </summary>
        /// <param name="filePath">文件名</param>
        /// <param name="Context">写的内容</param>
        /// <returns>是否成功</returns>
        public static bool FileWriter(string filePath, string Context)
        {
            try
            {
                //打开文件,如果不存在,则创建   
                Stream s = File.Open(filePath, FileMode.Create);
                //创建一个StreamWriter来写入数据   
                StreamWriter wrtr = new StreamWriter(s);
                //写入动态创建类的源代码   
                wrtr.WriteLine(Context);
                wrtr.Close();
                s.Close();
                return true;
            }
            catch (Exception)
            {

                return false;
            }
        }
        /// <summary>
        /// 读文件
        /// </summary>
        /// <param name="filePath">文件名</param>
        /// <returns>返回读的内容</returns>
        public static string FileRead(string filePath)
        {
            try
            {
                //打开文件,如果不存在,则创建   
                Stream s1 = File.Open(filePath, FileMode.Open);
                //创建一个StreamWriter来写入数据   
                StreamReader reader = new StreamReader(s1);
                //写入动态创建类的源代码   
                string xml = reader.ReadToEnd();
                reader.Close();
                s1.Close();
                return xml;
            }
            catch (Exception)
            {
                return null;
            }
        }
        /// <summary>
        /// 根据数据创建类,并得到动态创建类的集合
        /// </summary>
        /// <param name="list">导入的数据集合</param>
        /// <returns>返回类的集合</returns>
        public static List<object> GetXmlClassInstances(List<ExcelData> list)
        {
            List<object> o = new List<object>();
            ReflectClass t = new ReflectClass();
            var assemblys = t.DoOperation(list);
            Type[] types = assemblys.GetExportedTypes();
            foreach (Type type in types)
            {
                o.Add(assemblys.CreateInstance(type.Name));
            }
            return o;
        }
        /// <summary>
        /// 得到动态创建类的集合
        /// </summary>
        /// <returns></returns>
        public static List<object> GetXmlClassInstances()
        {
            List<object> o = new List<object>();
            ReflectClass t = new ReflectClass();
            var assemblys = t.DoOperation();
            Type[] types = assemblys.GetExportedTypes();
            foreach (Type type in types)
            {
                o.Add(assemblys.CreateInstance(type.Name));
            }
            return o;
        }
    }
}
复制代码

主要是一个GetXmlClassInstances(List<ExcelData> list):根据数据创建类,并得到动态创建类的集合

一个是GetXmlClassInstances():得到已经创建好的动态类的集合

下面是一个数据实体类,也就是我们所规范的数据格式

复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace XMLDemo
{
    /// <summary>
    /// 数据实体类
    /// </summary>
    public class ExcelData
    {
        /// <summary>
        /// 根节点
        /// </summary>
        public string Mark { get; set; }
        /// <summary>
        /// 子节点
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 子节点对应值
        /// </summary>
        public string Value { get; set; }
    }
}
复制代码

准备工作就绪后就可以进行XML文件的写入了,依然是废话不多说,上代码:

复制代码
       private void btnWrite_Click(object sender, EventArgs e)
        {
            List<object> XmlClassInstances = CommonHelper.GetXmlClassInstances(listData);
            if (InsertXmlClass(XmlClassInstances, listData) == false)
            {
                MessageBox.Show("数据导入失败");
                return;
            }
        }
        private bool InsertXmlClass(List<object> o, List<ExcelData> list)
        {
            
            for (int i = 1; i < o.Count; i++)
            {
                //给类的属性赋值
                var nlist = from num in list where (num.Mark == o[i].GetType().Name) select num;
                foreach (var model in nlist)
                {
                    PropertyInfo propinfo = o[i].GetType().GetProperty(model.Name);
                    propinfo.SetValue(o[i], model.Value, null);
                }
            }
            var XmlClass = o[0];               //默认第一个类为主体类,主体类的属性为其他类
            var tt = XmlClass.GetType();
            for (int i = 1; i < o.Count; i++)
            {
                //给主体类的属性赋值
                PropertyInfo propinfo = tt.GetProperty(o[i].GetType().Name);
                propinfo.SetValue(XmlClass, o[i], null);
            }
            //序列化主体类,得到序列化字符串
            string xml = XmlHelper.XmlSerialize(XmlClass, Encoding.UTF8);
            string FilePath = "QMSPlan.XML";
            if (CommonHelper.FileWriter(FilePath, xml) == false)
            {
                return false;
            }
            return true;
        }
复制代码

上面的代码可能有点绕,大家可以单步调试去理解,当时写的时候崩溃了几次,最后才调试好,不知道有什么更好的写法!

实现三:序列化读出XML文件

感觉用语言都表达不出来,还是直接上代码吧

复制代码
       private void btnRead_Click(object sender, EventArgs e)
        {
            List<ExcelData> listRead = new List<ExcelData>();
            var XmlClass = GetXmlClass();
            if (XmlClass == null)
            {
                MessageBox.Show("数据读取失败");
                return;
            }
            //获取所有主体类的属性(即所有的子类集合)
            PropertyInfo[] ps = XmlClass.GetType().GetProperties();
            foreach (var n in ps)
            {             
                //得到子类
                var m = n.GetValue(XmlClass, null);
                //获取子类的所有属性                
                PropertyInfo[] x = m.GetType().GetProperties();
                foreach (var y in x)
                {
                    ExcelData model = new ExcelData();
                    model.Mark = m.GetType().Name;
                    model.Name = y.Name;
                    model.Value = y.GetValue(m, null).ToString();
                    listRead.Add(model);
                }                
            }
        }
        private object GetXmlClass()
        {
            List<object> o = CommonHelper.GetXmlClassInstances();
            var XmlClass = o[0];               //默认第一个类为主体类

            string FilePath = "QMSPlan.XML";
            string xml = CommonHelper.FileRead(FilePath);
            if (xml == null)
            {
                return null;
            }
            XmlClass = XmlHelper.XmlDeserialize(XmlClass.GetType(), xml, Encoding.UTF8);
            return XmlClass;
        }
复制代码

上面大量运用了反射的特性,就是因为实现动态读取数据类型而造成的!

结束语

可能大家看完觉得完全没必要这样做,并且大量这样的操作会影响性能,不过这样做实实在在解决了我项目中的难点,实现了根据导入数据的不同,可以实现正确的XML的读写操作,

总结一下:以上总共有三个知识点的应用

1.动态创建数据类型并在程序中使用

2.序列话读写XML文件

3.利用反射特性实现

最后附上demo下载地址:http://files.cnblogs.com/beimeng/XML.demo.rar

[转载]TreeView的节点遍历 - 部落酋长 - 博客园

mikel阅读(1255)

[转载]TreeView的节点遍历 – 部落酋长 – 博客园.

 

foreach (TreeNode td in treeView1.Nodes)
                {
                    TreeNode targetNode = SearchNode(td, selectParentNum);
                    if (targetNode != null)
                    {
                        treeView1_AfterSelect(treeView1, new TreeViewEventArgs(targetNode));
                        break;
                    }
                }

/// <summary>
/// 遍历TreeView节点
/// </summary>
/// <param name="td">ParentNode</param>
/// <param name="selectParentNum">条件</param>
/// <returns></returns>

private TreeNode SearchNode(TreeNode td, string selectParentNum)
        {
            if (td.ToolTipText == selectParentNum)
            {
                return td;
            }
            TreeNode targetNode = null;
            foreach (TreeNode childNodes in td.Nodes)
            {
                targetNode = SearchNode(childNodes, selectParentNum);
                if (targetNode != null)
                    break;
            }
            return targetNode;
        }

[转载]ASP.NET MVC 系列:Forms身份验证和基于Role的权限验证 - >>王刚 - 博客园

mikel阅读(1008)

[转载]ASP.NET MVC 系列:Forms身份验证和基于Role的权限验证 – >>王刚 – 博客园.

从Membership到SimpleMembership再到ASP.NET Identity,ASP.NET每一次更换身份验证的组件,都让我更失望。Membership的唯一作用就是你可以参考它的实现,它的数据库创建和扩展方面就真的让人实在无法使用了。

当大家欢呼着让ASP.NET开发走上ASP MVC的正确道路时,身份验证组件却走的更远了:SimpleMembership除了第三方验证的参考价值,它的主键和对领域模型的入侵让它成了摆设, 而ASP.NET Identity除了把第三方验证搞的更复杂化外依然没有改进。想起SimpleMembership在code first方面的支持上给我带来的各种麻烦让我现在依然碎碎念。

ASP.NET Identity在Code First这方面虽有改进,但在依赖方面不只是个IdentityUser的继承,竟然还用IdentityDbContext试图搞我的DbContext,你知道我有没有DbContext啊?

幸亏核心都还是基于Cookie的,Forms身份验证也一直可以使用,所以毫不犹豫拿出Forms+FormsAuthentication砍死 了SimpleMembership和ASP.NET Identity。这俩货让我这两年赌了无数次,冒着被无数小白和伪实战专家真小白喷的危险我也必须抛弃。还要庆幸权限验证的核心页依然是 IPrincipal,使用自定义的RoleProvider可以将权限验证从这俩鸡肋里拯救出来。

1.配置好Web.config

2.清爽的User和Role

3.只需要将2个方法的RoleProvider

4.IAccountService和IUserRepo只用于演示,依赖注入页简单使用Factory的静态方法。

为了便于演示,使用admin登录的,我们返回admin权限。

5.功能演示:

一想到我没有任何依赖任意测试的Domain Model,只对外接口依赖的Controller,心里真舒坦。登录、退出、admin权限控制,太晚了,不截图了自己跑Demo吧。

Demo:点击下载

 

如果这篇博客内容对您稍有帮助或略有借鉴,请您推荐它帮助更多的人。

不是一个圈的猪 就得想法儿才能往一个圈跳

mikel阅读(988)

没有万灵的灵药,同样也没有适用任何产品的推广方法,别以为推广网站能行,你推广移动APP就能搞定,不是一个圈的猪 就得想法儿才能往一个圈跳!互联网和移动互联网就是两个世界,人们的使用习惯和对待推广的态度截然不同,今天就被弄郁闷了!

信誓旦旦的想推广个APP安装量,觉得自己有那么多会员推推就能上量了,可真不明白咋就不上量呢?!难道真的是都闲麻烦,扫个码都懒得扫?!嗨,真的搞不懂了!

看来想要移动互联网上推广APP还真的不是一时半会儿能整明白的,看来还是自己不行,得学啊!

[转载]ASP.NET MVC使用Bootstrap系统(2)——使用Bootstrap CSS和HTML元素 - 木宛城主 - 博客园

mikel阅读(1180)

[转载]ASP.NET MVC使用Bootstrap系统(2)——使用Bootstrap CSS和HTML元素 – 木宛城主 – 博客园.

Bootstrap提供了一套丰富CSS设置、HTML元素以及高级的栅格系统来帮助开发人员快速布局网页。所有的CSS样式和HTML元素与移动设备优先的流式栅格系统结合,能让开发人员快速轻松的构建直观的界面并且不用担心在较小的设备上响应的具体细节。

Bootstrap 栅格(Grid)系统

在移动互联网的今天,越来越多的网站被手机设备访问,移动流量在近几年猛增。Bootstrap 提供了一套响应式、移动设备优先的流式栅格系统,随着屏幕或视口(viewport)尺寸的增加,系统会自动分为最多12列。

栅格参数

Bootstrap 3提供了一系列的预定义class来指定列的尺寸,如下所示:

Bootstrap 栅格系统被分割为12列,当布局你的网页时,记住所有列的总和应该是12。为了图示,请看如下HTML所示:

复制代码
<div class="container">
    <div class="row">
        <div class="col-md-3" style="background-color: green;">
            <h3>green</h3>
        </div>
        <div class="col-md-6" style="background-color: red;">
            <h3>red</h3>
        </div>
        <div class="col-md-3" style="background-color: blue;">
            <h3>blue</h3>
        </div>
    </div>
</div>
复制代码

注:Bootstrap 需要为页面内容和栅格系统包裹一个 .container 容器。

在上述代码中,我添加了一个class为 container的div容器,并且包含了一个子的div元素row(行)。row div元素依次有3列。其中2列包含了col-md-3的class、一列包含了col-md-6的class。当他们组合在一起时,他们加起来总和是 12.但这段HTML代码只作用于显示器分辨率>=992的设备。所以为了更好的响应低分辨率的设备,我们需要结合不同的CSS栅格class。故 添加对平板、手机、低分辨率的PC的支持,需要加入如下class:

复制代码
<div class="container">
    <div class="row">
        <div class="col-xs-3 col-sm-3 col-md-3" style="background-color: green;">
            <h3>green</h3>
        </div>
        <div class="col-xs-6 col-sm-6 col-md-6" style="background-color: red;">
            <h3>red</h3>
        </div>
        <div class="col-xs-3 col-sm-3 col-md-3" style="background-color: blue;">
            <h3>blue</h3>
        </div>
    </div>
</div>
复制代码

Bootstrap HTML元素

Bootstrap已经为我们准备好了一大堆带有样式的HTML元素,如:

  • Tables
  • Buttons
  • Forms
  • Images

Bootstrap Tables(表格)

Bootstrap为HTML tables提供了默认的样式和自定义他们布局和行为的选项。为了更好的演示,我使用精典的Northwind示例数据库以及如下技术:

  • ASP.NET MVC来作为Web应用应用程序
  • Bootstrap前端框架
  • Entity Framework来作为ORM框架
  • StructureMap执行我们项目的依赖注入和控制反转,使用Nuget来安装
  • AutoMapper自动映射Domain Model到View Model,使用Nuget来安装

打开Visual Studio,创建一个ASP.NET MVC的项目,默认情况下,VS已经为我们添加了Bootstrap的文件。为了查看效果,按照如下的步骤去实施:

  • ASP.NET MVC项目中的Models文件下添加一个ProductViewModel
复制代码
   public class ProductViewModel
    {
        public int ProductId { get; set; }
        public string ProductName { get; set; }
        public decimal? UnitPrice { get; set; }
        public int? UnitsInStock { get; set; }
        public bool Discontinued { get; set; }
        public string Status { get; set; }
    }
复制代码
  • 在APP_Data文件夹中添加 AutoMapperConfig类,通过AutoMapper,为ProductViewModel的Status属性创建了一个条件映射,如果 Product是discontinued,那么Status为danger;如果UnitPrice大于50,则设置Status属性为info;如果 UnitInStock小于20,那么设置Status为warning。代码的逻辑如下:
复制代码
    Mapper.CreateMap<Product, ProductViewModel>()
                .ForMember(dest => dest.Status,
                    opt => opt.MapFrom
                        (src => src.Discontinued
                            ? "danger"
                            : src.UnitPrice > 50
                                ? "info"
                                : src.UnitsInStock < 20 ? "warning" : ""));
复制代码
  • 添加一个ProductController并且创建名为Index的Action
复制代码
 public class ProductController : Controller
    {
        //
        // GET: /Product/
        private readonly ApplicationDbContext _context;

        public ProductController(ApplicationDbContext context)
        {
            this._context = context;
        }
        public ActionResult Index()
        {
            var products = _context.Products.Project().To<ProductViewModel>().ToArray();
            return View(products);
        }
    }
复制代码
  1. 上述代码使用依赖注入获取Entity Framework DbContext对象,Index Action接受从数据库中返回Products 集合然后使用AutoMapper映射到每一个ProductViewModel对象中,最后为View返回数据。
  2. 在视图上使用Bootstrap HTML table来显示数据

复制代码
<div class="container">
    <h3>Products</h3>
    <table class="table">
        <thead>
            <tr>
                <th>
                    Product Name
                </th>
                <th>
                    Unit Price
                </th>
                <th>
                    Units In Stock
                </th>
                <th>
                    Discontinued
                </th>
            </tr>
        </thead>
        <tbody>
            @foreach (var item in Model)
            {
                <tr>
                    <td>
                        @Html.DisplayFor(modelItem => item.ProductName)
                    </td>
                    <td>
                        @Html.DisplayFor(modelItem => item.UnitPrice)
                    </td>
                    <td>
                        @Html.DisplayFor(modelItem => item.UnitsInStock)
                    </td>
                    <td>
                        @Html.DisplayFor(modelItem => item.Discontinued)
                    </td>
                </tr>
            }
        </tbody>
    </table>

</div>
复制代码

呈现的数据如下所示:

Bootstrap Tables 其余样式

Bootstrap提供了额外的样式来修饰table。比如使用table-bordered来显示边框,table-striped显示奇偶数行间颜色不同(斑马条纹状),table-hover顾名思义,当鼠标移动行时高亮,通过添加 .table-condensed 类可以让表格更加紧凑,单元格中的内补(padding)均会减半,修改后的代码如下所示:

<table class="table table-bordered table-striped table-hover">
</table>

显示的效果如下:

Bootstrap上下文Table 样式

Bootstrap提供了额外的class能让我们修饰<td>和<tr>的样式,提供的class如下:

  • Active
  •  Success 
  • Info
  • Warning
  • Danger

修改上述代码,为<tr>动态添加样式:

复制代码
@foreach (var item in Model)
{
    <tr class="@item.Status">
        <td>
            @Html.DisplayFor(modelItem => item.ProductName)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.UnitPrice)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.UnitsInStock)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Discontinued)
        </td>
    </tr>
}
复制代码

更新过后的效果如下所示:

Bootstrap Buttons

Bootstrap提供了许多各种不同颜色和大小的buttons,为核心的buttons提供6种颜色和4种尺寸可以选择,同样通过设置class属性来显示不同的风格:

btn btn-primary btn-xs

btn btn-default btn-sm

btn btn-default

btn btn-sucess btn-lg

可以为Button设置颜色的class:

btn-default

btn-primary

btn-success

btn-info

btn-warning

btn-danger

所以可以使用如下代码来呈现效果:

复制代码
<div class="row">
    <!-- default按钮 -->
    <button type="button" class="btn btn-default btn-xs">
        Default & Size=Mini
    </button>
    <button type="button" class="btn btn-default btn-sm">
        Default & Size=Small
    </button>
    <button type="button" class="btn btn-default">Default</button>
    <button type="button" class="btn btn-default btn-lg">
        Default & Size=Large
    </button>
</div>
复制代码

显示效果如下:

Bootstrap Form(表单)

表单常见于大多数业务应用程序里,因此统一的样式有助于提高用户体验,Bootstrap提供了许多不同的CSS样式来美化表单。

  • 水平表单

使用ASP.NET MVC的HTML.BeginForm可以方便的创建一个表单,通过为<form>添加名为form-horizontal的class来创建一个Bootstrap 水平显示表单。

View Code

上述代码中,使用class为form- group的<div>元素包裹了2个Html方法(Html.LabelFor、Html.TextboxFor),这能让 Bootstrap 验证样式应用在form 元素上,当然你也可以使用Bootstrap 栅格col-* class来指定form 中元素的宽度,效果如下显示:

  • 垂直表单

Bootstrap基础表单默认情况下是垂直显示内容,在Html.BeginForm帮助方法里移除class为form-horizontal和class col-*后,显示的效果如下:

  • 内联表单

内联表单表示所有的form 元素一个接着一个水平排列,只适用于视口(viewport)至少在 768px 宽度时(视口宽度再小的话就会使表单折叠)。

记得一定要添加 label 标签,如果你没有为每个输入控件设置 label 标签,屏幕阅读器将无法正确识别。对于这些内联表单,你可以通过为label 设置 .sr-only 类将其隐藏。详细代码如下:

复制代码
@using (Html.BeginForm("Login", "Account", FormMethod.Post, new { @class = "form-inline", role = "form" }))
{
    <div class="form-group">
        @Html.LabelFor(m => m.UserName, new { @class = "sr-only" })
        @Html.TextBoxFor(m => m.UserName, new { @class = "form-control", placeholder = "Enter your username" })
        @Html.ValidationMessageFor(m => m.UserName)
    </div>
    <div class="form-group">
        @Html.LabelFor(m => m.Password, new { @class = "sr-only" })
        @Html.PasswordFor(m => m.Password, new { @class = "form-control", placeholder = "Enter your username" })
        @Html.ValidationMessageFor(m => m.Password)
    </div>
    <div class="form-group">
        <input type="submit" value="Log in" class="btn btn-default" />
    </div>
}
复制代码

显示效果如下:

Bootstrap Image

在 Bootstrap 3.0中,通过为图片添加 .img-responsive 类可以让图片支持响应式布局。其实质是为图片设置了 max-width: 100%;、 height: auto;  display: block; 属性,从而让图片在其父元素中更好的缩放。

通过为 <img> 元素添加以下相应的类,可以让图片呈现不同的形状。

  • img-rounded
  • img-circle
  • img-thumbnail

请看如下代码:

复制代码
<div class="row">
    <h3>Our Team</h3>
    @foreach (var item in Model)
    {
        <div class="col-md-4">
            <img src="@Url.Content("~/Images/employees/" + item.EmployeeID + ".png")" alt="@item.FirstName@item.LastName" class="img-circle img-responsive">
            <h3>
                @item.FirstName @item.LastName <small>@item.Title</small>
            </h3>
            <p>@item.Notes</p>
        </div>
    }
</div>
复制代码

Bootstrap 验证样式

默认情况下ASP.NET MVC项目模板支持unobtrusive 验证并且会自动添加需要的JavaScript库到项目里。然而默认的验证不使用Bootstrap指定的CSS。当一个input元素验证失 败,JQuery validation插件会为元素添加input-validation-error class(存在Site.css中)。那怎样不修改JQuery Validation插件而且使用Bootstrap内置的错误样式呢?

Bootstrap提供了class为has-error中的样式(label字体变为红色,input元素加上红色边框)来显示错误:

复制代码
<div class="form-group has-error">
    @Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" })
    <div class="col-md-10">
        @Html.TextBoxFor(m => m.UserName, new { @class = "form-control" })
    </div>
</div>
复制代码

所以,我需要动态来为<div class= from-group>元素动态绑定/移除has-error。为了不修改jQuery.validation插件,我在Scripts文件夹中添加jQuery.validate.bootstrap文件:

复制代码
$.validator.setDefaults({
    highlight: function (element) {
        $(element).closest('.form-group').addClass('has-error');
    },
    unhighlight: function (element) {
        $(element).closest('.form-group').removeClass('has-error');
    },
});
复制代码

这段脚本的通过调用setDefaults方法来修改默认的JQuery validation 插件设置。看以看到我使用highlight和unhighlight方法来动态添加/移除has-error class。

最后将它添加到打包文件中

bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.validate.js",
"~/Scripts/jquery.validate.unobtrusive.js",
"~/Scripts/jquery.validate.bootstrap.js"));

注:默认情况下,ASP.NET MVC使用通配符*来将jquery.validate*文件打包到jqueryval文件中,如下所示:

bundles.Add(new ScriptBundle(“~/bundles/jqueryval”).Include(

“~/Scripts/jquery.validate*”));

但是jquery.validate.bootstrap.js必须在jquery validate 插件后加载,所以我们只能显式的指定文件顺序来打包,因为默认情况下打包加载文件的顺序是按通配符代表的字母顺序排列的。

ASP.NET MVC创建包含Bootstrap样式编辑模板

  • 基元类型

编辑模板(Editor Template)指的是在ASP.NET MVC应用程序中,基于对象属性的数据类型通过Razor视图渲染后,自动产生表单Input元素。ASP.NET MVC包含了若干的编辑模板,当然我们也可以实现扩展。编辑模板类似于局部视图,不同的是,局部视图通过name来渲染,而编辑模板通过类型来渲染。

举个栗 子,@Html.EditorFor(model => model.Property),如果Property类型为string,那么@Html.Editor 会创建一个Type=Text的Input元素;如果Property类型为Password,那么会创建一个Type=Password的Input元 素。所以EditorFor helper是基于model 属性的数据类型来渲染生成HTML。

不过美中不足的是,默认产生的HTML如下所示:

 

可以看到class=”text-box single-line”,但先前提到过,Bootstrap Form元素class必须是 form-control。

所以,为了让Editor helper生成class为form-control的表单元素,我们需要创建一个自定义的编辑模板来重写旧的模板。你需要如下操作:

  • 在Shared文件夹中创建名为EditorTemplates(注意要一样的名称)的文件夹
  • 添加名为string.cshtml(注意要一样的名称)文件,并添加如下代码:
@model string
@Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new
{
    @class = "form-control"
})

在上述代码中,我们调用 @Html.TextBox方法,并且传递了一个空的字符串作为textbox的name。这将会让model的属性名作为生成的textbox的name,并且textbox显示的内容是model的值,最后追加了名为class的attribute,而且其值为”form-control”。

重新生成项目,发现新生成的input元素它的class已经改为”form-control“了。如下所示:

 

  • 非基元类型

ASP.NET MVC能让开发者创建根据自定义DataType的编辑模板,比如自动生成多行文本框并且规定行数为3行,也是同样的操作:

  • 添加MultilineText. Cshtml(注意名称相同)文件到EditorTemplates中
  • 添加如下代码:
@model string
@Html.TextArea("", ViewData.TemplateInfo.FormattedModelValue.
ToString(), new { @class = "form-control", rows = 3 })
  • 为了让我们的Model的属性在渲染时采用MultilineText.cshtml编辑模板,我们需要为属性指定DataType attribute为MultilineText:
 [DataType(DataType.MultilineText)]
  public string Description { get; set; }

最终显示如下所示:

小结

这篇文章为大家介绍了Bootstrap的响 应式布局(grid),并且简单介绍了Bootstrap中的HTML元素,包括Table、Button、Form、Image…。然后修改了 JQuery validate插件默认的的设置,使其友好支持Bootstrap中的错误提示样式。最后探索了ASP.NET MVC中的编辑模板,能让产生的input元素自动包含form-control样式。

Bootstrap样式上更多的内容,参考Bootstrap官网全局CSS这一章节内容(http://v3.bootcss.com/css/),谢谢大家支持。

本博客为木宛城主原创,基于Creative Commons Attribution 2.5 China Mainland License发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名木宛城主(包含链接)。如您有任何疑问或者授权方面的协商,请给我留言。

[转载]ASP.NET MVC使用Bootstrap系列(3)——使用Bootstrap 组件 - 木宛城主 - 博客园

mikel阅读(1158)

[转载]ASP.NET MVC使用Bootstrap系列(3)——使用Bootstrap 组件 – 木宛城主 – 博客园.

Bootstrap为我们提供了十几种的可复用组件,包括字体图标、下拉菜单、导航、警告框、弹出框、输入框组等。在你的Web Application中使用这些组件,将为用户提供一致和简单易用的用户体验。

Bootstrap组件本质上是结合了各种现有Bootstrap元素以及添加了一些独特Class来实现。Bootstrap元素我在上一篇文章中涉及到,具体可以参考《ASP.NET MVC使用Bootstrap系列(2)——使用Bootstrap CSS和HTML元素》。

在这篇博客中,我将继续探索Bootstrap丰富的组件以及将它结合到ASP.NET MVC项目中。

Bootstrap 导航条

Bootstrap导航条作为”明星组件”之一,被使用在大多数基于Bootstrap Framework的网站上。为了更好的展示Bootstrap导航条,我在ASP.NET MVC的_Layout.cshtml布局页创建一个fixed-top导航条,当然它是响应式的——在小尺寸、低分辨率的设备上打开时,它将会只展示一 个按钮并带有3个子菜单,当点击按钮时垂直展示他们。在网页上显示如下:

在移动设备上显示如下:

ASP.NET MVC默认的_Layouts.cshtml布局页中已经帮我们实现了上述功能,打开它对其稍作修改,如下代码片段所示:

复制代码
<div class="navbar navbar-inverse navbar-fixed-top">

    <div class="container">

        <div class="navbar-header">

            <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">

            <span class="icon-bar"></span>

            <span class="icon-bar"></span>

            <span class="icon-bar"></span>

            </button>

            @Html.ActionLink("Northwind Traders", "Index", "Home", null, new { @class = "navbar-brand" })

        </div>

        <div class="navbar-collapse collapse">

            @Html.Partial("_BackendMenuPartial")

            @Html.Partial("_LoginPartial")

        </div>

    </div>

</div>
复制代码

其中class为.navbar-fixed-top可以让导航条固定在顶部,还可包含一个 .container 或 .container-fluid 容器,从而让导航条居中,并在两侧添加内补(padding)

注意,我使用了2个局部视图(_BackendMenuPartial和LoginPartial)来生成余下的导航条(使用.navbar-collapse类在低分辨率设备中折叠),其中局部视图逻辑是基于当前访问的用户是否登陆来控制是否显示。

首先,添加如下代码在_BackendMenuPartial视图中,这将会在导航条中产生一个搜索框:

复制代码
@using (Html.BeginForm("Index", "Search", FormMethod.Post, new { @class = "navbar-form navbar-left", role = "search" }))

{

    <div class="form-group">

        @Html.TextBox("searchquery", "", new { @id = "searchquery", @class = "form-control input-sm", placeholder = "Search" })

        @Html.Hidden("fromcontroller", @ViewContext.RouteData.Values["controller"], new { @id = "fromcontroller" })

    </div>

    <button type="submit" class="btn btn-default btn-xs">GO</button>

}
复制代码

因为Bootstrap导航条作为整个网站的公共部分,要实现快速搜索那么必须要知道当前所处于哪个Controller,这样才能提高检索效率。所以上述代码中,增加了一个Id为fromcontroller隐藏字段,代表当前访问的Controller。

当点击搜索时,发送HTTP POST请求到Index Action下。然后根据传递过来的fromcontroller来swith到具体的Action来执行搜索,具体的搜索逻辑代码如下:

复制代码
public ActionResult Index(string searchquery, string fromcontroller)

{

    switch (fromcontroller)

    {

        case "Products":

        return RedirectToAction("SearchProductsResult", new { query = searchquery });

         

        case "Customers":

        return RedirectToAction("SearchCustomersResult", new { query = searchquery });

         

        case "Employees":

        return RedirectToAction("SearchEmployeesResult", new { query = searchquery });

    }

    return View();

}
复制代码

具体搜索的Action如下:

复制代码
public ActionResult SearchProductsResult(string query)

{

    ViewBag.SearchQuery = query;

    var results = _context.Products.Where(p => p.ProductName.Contains(query)).ToList();

    return View(results);

}

 

public ActionResult SearchCustomersResult(string query)

{

    ViewBag.SearchQuery = query;

    var results = _context.Customers.Where(p => p.CompanyName.Contains(query)

    || p.ContactName.Contains(query)

    || p.City.Contains(query)

    || p.Country.Contains(query)).ToList();

    return View(results);

}

 

public ActionResult SearchEmployeesResult(string query)

{

    ViewBag.SearchQuery = query;

    var results = _context.Employees.Where(p => p.FirstName.Contains(query)

    || p.LastName.Contains(query)

    || p.Notes.Contains(query)).ToList();

    return View(results);

}
复制代码

列表组

列表组是灵活又强大的组件,不仅能用于显示一组简单的元素,还能结合其他元素创建一组复杂的定制内容。上面的搜索为我们重定向到Result视图,在此视图中,它为我们显示了搜索结果,为了更好的展示结果,我们可以使用列表组来显示搜索到的产品,视图中的代码如下所示:

复制代码
@model IEnumerable<Bootstrap.Data.Models.Products>

@{

    ViewBag.Title = "搜索产品";

}

<div class="container">

    <div class="page-header">

        <h1>产品结果 <small>搜索条件: "@ViewBag.SearchQuery"</small></h1>

    </div>

    <ul class="list-group">

        @foreach (var item in Model)

        {

        <a href="@Url.Action("Edit","Products", new { id=@item.ProductID})" class="list-group-item">@item.ProductName <span class="badge">@item.UnitsInStock</span></a>

        }

    </ul>

</div>
复制代码

在上述代码中,为无序列表(<ul>)的class设置为list-group,并且每一个<li>的class为list-group-item,这是一个最简单的列表组。

徽章

徽章用来高亮条目,可以很醒目的显示新的或者未读的条目数量,为一个元素设置徽章仅仅只需要添加<span>元素并设置它的class为badge。所以,在上述代码的基础上稍作修改,添加徽章,表示库存个数,如下HTML所示:

 

<a href="@Url.Action("Edit","Products", new { id=@item.ProductID})" class="list-group-item">
    @item.ProductName <span class="badge">@item.UnitsInStock</span>
</a>

显示的结果为如下截图:

媒体对象

媒体对象组件被用来构建垂直风格的列表比如博客的回复或者推特。在Northwind数据库中包含一个字段ReportTo表示Employee向另一个Employee Report。使用媒体对象可以直观来表示这种关系。在视图中的代码如下所示:

复制代码
<div class="container">

<div class="page-header">

    <h1>员工搜索结果: <small>搜索条件: "@ViewBag.SearchQuery"</small></h1>

</div>

@foreach (var item in Model)

{

    <div class="media">

        <a class="pull-left" href="@Url.Action("Edit", "Employees", new { id = @item.EmployeeID })">

            <img class="media-object" src="@Url.Content("~/Images/employees/" + @item.EmployeeID + ".png")" alt="@item.FirstName" width="64" height="64">

        </a>

        <div class="media-body">

            <h4 class="media-heading">@item.FirstName @item.LastName</h4>

            @item.Notes

            @foreach (var emp in @item.ReportingEmployees)

            {

                <div class="media">

                    <a href="#" class="pull-left">

                        <img alt="@emp.FirstName" src="@Url.Content("~/Images/employees/" + @emp.EmployeeID + ".png")" class="media-object" width="64" height="64">

                    </a>

                    <div class="media-body">

                        <h4 class="media-heading">@emp.FirstName @emp.LastName</h4>

                        @emp.Title

                    </div>

                </div>

            }

        </div>

    </div>

 

}

</div>
复制代码

显示结果如下:

可以看到,媒体对象组件是由一系列class为media、media-heading、media-body、media-object的元素组合而成,其中media-object用来表示诸如图片、视频、声音等媒体对象。

注:.pull-left 和 .pull-right 这两个类以前也曾经被用在了媒体组件上,但是,从 v3.3.0 版本开始,他们就不再被建议使用了。.media-left 和 .media-right 替代了他们,不同之处是,在 html 结构中, .media-right 应当放在 .media-body 的后面。

页头

当用户访问网页时,Bootstrap页头可以为用户提供清晰的指示。Bootstrap页头本质上是一个<h1>元素被封装在 class为page-header的<div>元素中。当然你也可以利用<small>元素来提供额外的关于页面的信息,同时 Bootstrap为页头添加了水平分隔线用于分隔页面,如下HTML即为我们构建了页头:

<div class="page-header">

    <h1>员工搜索结果: <small>搜索条件: "@ViewBag.SearchQuery"</small></h1>

</div>

路径导航

路径导航(面包屑)在Web 设计中被用来表示用户在带有层次的导航结构中当前页面的位置。类似于Windows资源管理器。如下HTML所示:

复制代码
<ol class="breadcrumb">

    <li>@Html.ActionLink("Home", "Index", "Home")</li>

    <li>@Html.ActionLink("Manage", "Index", "Manage")</li>

    <li class="active">Products</li>

</ol>
复制代码

在上面HTML代码中,通过指定有序列表(<ol>)的class为breadcrumb,每一个子路径用<li>来表示,其中通过设置<li>的class为active代表当前所处的位置。

各路径间的分隔符已经自动通过 CSS  :before  content 属性添加了。

分页

分页用来分隔列表内容,特别是显示大量数据时通过分页可以有效的减少服务器压力和提高用户体验,如下截图使用分页来显示产品列表:

要完成上述的分页,需要安装PagedList.Mvc程序包,在NuGet控制台中安装即可:Install-PackagePagedList.Mvc

然后修改Action,它需要接受当然的页码,它是一个可空的整数类型变量,然后设置PageSize等于5,表示每页显示5条记录,如下代码所示:

 

复制代码
public ActionResult Index(int? page)

{

    var models = _context.Products.Project().To<ProductViewModel>().OrderBy(p => p.ProductName);

    int pageSize = 5;

    int pageNumber = (page ?? 1);

    return View(models.ToPagedList(pageNumber, pageSize));

}
复制代码

View中,使用PagedList动态生成分页控件

复制代码
<div class="container">

    <div class="page-header">

        <h1>Products <small>Page @(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) of @Model.PageCount</small></h1>

    </div>

 

    <ol class="breadcrumb">

        <li>@Html.ActionLink("Home", "Index", "Home")</li>

        <li>@Html.ActionLink("Manage", "Index", "Manage")</li>

        <li class="active">Products</li>

    </ol>

    <table class="table table-striped table table-hover">

        <thead>

        <tr>

            <th>

            Product Name

            </th>

            <th>

            Unit Price

            </th>

            <th>

            Units In Stock

            </th>

            <th>

            Discontinued

            </th>

            <th></th>

        </tr>

        </thead>

    <tbody>

    @foreach (var item in Model)

    {

    <tr class="@item.Status">

        <td>

            @Html.ActionLink(item.ProductName, "Edit", new { id = item.ProductID })

        </td>

        <td>

            @Html.DisplayFor(modelItem => item.UnitPrice)

        </td>

        <td>

            @Html.DisplayFor(modelItem => item.UnitsInStock)

        </td>

        <td>

            @Html.DisplayFor(modelItem => item.Discontinued)

        </td>

        <td>

            @Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })

        </td>

    </tr>

    }

    </tbody>

    </table>

    @Html.PagedListPager(Model, page => Url.Action("Index", new { page }), PagedListRenderOptions.ClassicPlusFirstAndLast)

    <p>

        <a href="@Url.Action("Create","Products")" class="btn btn-sm btn-primary" role="button">Create New</a>

    </p>

</div>
复制代码

输入框组

输入框组为用户在表单输入数据时可以提供更多的额外信息。Bootstrap的输入框组为我们在Input元素的前面或者后面添加指定class的块,这些块可以是文字或者字体图标,如下所示:

复制代码
<div class="form-group">

    <div class="col-sm-2 input-group">

        <span class="input-group-addon">

            <span class="glyphicon glyphicon-phone-alt"></span>

        </span>

        @Html.TextBox("txtPhone","1194679215",new { @class = "form-control" })

    </div>

</div>
复制代码

上面的输入框组合中,在Textbox的左边放置了一个带有字体图标Phone的灰色块,结果如下所示:

不仅可以使用字体图标,还可以使用纯文本来显示信息,如下所示在Textbox右边放置了固定的邮箱域名:

复制代码
<div class="form-group">

    <div class="col-sm-4 input-group">

        @Html.TextBox("txtEmail","1194679215", new { @class = "form-control" })

        <span class="input-group-addon">@@qq.com</span>

    </div>

</div>
复制代码

当然也可以在Input元素的两边同时加上块,如下代码所示:

 

复制代码
<div class="form-group">

    <div class="col-sm-2 input-group">

        <span class="input-group-addon"></span>

            @Html.TextBox("txtMoney","100",new { @class = "form-control" })

        <span class="input-group-addon">.00</span>

    </div>

</div>
复制代码

 

按钮式下拉菜单

按钮式下拉菜单顾名思义,一个按钮可以执行多种action,比如既可以Save,也可以Save之后再打开一个新的Form继续添加记录,如下所示:

复制代码
<div class="form-group">

    <div class="col-sm-offset-2 col-sm-10">

        <div class="btn-group">

            <button type="submit" class="btn btn-primary btn-sm">Save</button>

            <button type="button" class="btn btn-primary btn-sm dropdown-toggle" data-toggle="dropdown">

                <span class="caret"></span>

                <span class="sr-only">Toggle Dropdown</span>

            </button>

            <ul class="dropdown-menu" role="menu">

                <li><a href="#" id="savenew">Save & New</a></li>

                <li class="divider"></li>

                <li><a href="#" id="duplicate">Duplicate</a></li>

            </ul>

        </div>

    </div>

</div>
复制代码

警告框

Bootstrap警告组件通常被用作给用户提供可视化的反馈,比如当用户Save成功后显示确认信息、错误时显示警告信息、以及其他的提示信息。

Bootstrap提供了4中不同风格的警告,如下所示:

复制代码
<div class="container">

    <div class="page-header">

        <h1>Alerts </h1>

    </div>


    <ol class="breadcrumb">

        <li>@Html.ActionLink("Home", "Index", "Home")</li>

        <li>Bootstrap</li>

        <li class="active">Alerts</li>

    </ol>


    <div class="alert alert-success"><strong>Success. </strong></div>

    <div class="alert alert-info"><strong>Info.</strong></div>

    <div class="alert alert-warning"><strong>Warning!</strong></div>

    <div class="alert alert-danger"><strong>Danger!</strong></div>


</div>
复制代码

可关闭的警告框可以让用户点击右上角的X来关闭,你可以使用alter-dismissible 类:

复制代码
<div class="alert alert-warning alert-Dismissible" role="alert">

    <button type="button" class="close" data-dismiss="alert">

        <span aria-hidden="true">&times;</span><span class="sr-only">Close</span>

    </button>

    <strong>Alert!</strong>这是可关闭的Alter

</div>
复制代码

进度条

进度条在传统的桌面应用程序比较常见,当然也可以用在Web上。通过这些简单、灵活的进度条,可以为当前工作流程或动作提供实时反馈。Bootstrap为我们提供了许多样式的进度条。

  • 基本进度条

基本进度条是一种纯蓝色的进度条,添加一个class 为sr-only 的<span>元素在进度条中是比较好的实践,这样能让屏幕更好的读取进度条的百分比。

复制代码
<div class="row">

<    h4>基本进度条</h4>

    <div class="progress">

        <div class="progress-bar" role="progressbar" aria-valuenow="80" aria-valuemin="0" aria-valuemax="100" style="width: 80%;">

            <span class="sr-only">80%完成</span>

        </div>

    </div>

</div>
复制代码

  • 上下文情景变化进度条

上下文情景变化进度条组件使用与按钮和警告框相同的类,根据不同情境展现相应的效果。

 

  • progress-bar-success
  • progress-bar-info
  • progress-bar-warning
  • progress-bar-danger

 

  • 条纹动画效果进度条

为了让进度条更加生动,可以为其添加条纹效果,在进度条<div>中添加class为progress-striped。当然让进度条看起来有动画,可以再为其指定active的class在<div>上,如下所示:

 

复制代码
<div class="progress progress-striped active">

    <div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="40" aria-valuemin="0" aria-valuemax="100" style="width: 40%">

        <span class="sr-only">40% 完成 (success)</span>

    </div>

</div>
复制代码

  • 使用SignalR动态更新进度条

SignalR是ASP.NET的库,可以用来双向实时通信,在ASP.NET MVC项目中使用SignalR:1.首先通过NuGet来安装SignalR Nuget Package:

Install-Package Microsoft.AspNet.SignalR

2.在项目里新建一个名为Hubs的文件夹,并添加ProgressbarHub类,如下代码所示:

复制代码
public class ProgressbarHub : Hub

{

    public void SendProgress()

    {

        for (int i = 0; i <= 100; i++)

        {

            Thread.Sleep(50);

            Clients.Caller.sendMessage(i + "%");

        }

    }

}
复制代码

SendProgress方法很简单,向客户端发送消息(通过自增变量输出0—100)

3.在布局页添加对SignalR虚拟路径的引用:

<script src="~/signalr/hubs"></script>

4.在打包文件中,包含 SignalR.js

复制代码
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(

"~/Scripts/jquery-{version}.js",

"~/Scripts/jquery.signalR-2.2.0"

));
复制代码

5.在客户端建立与服务端的双向通信:

复制代码
@section scripts

{

<script type="text/javascript" language="javascript">

 

    $(document).ready(function () {

    //$(".progress-bar").width('0%');

    });

 

    $("#start").click(function () {

        $(".progress-bar").width('0%');

        var progressNotifier = $.connection.progressbarHub;

        progressNotifier.client.sendMessage = function (message) {

            updateProgress(message);

        };


        $.connection.hub.start().done(function () {

            progressNotifier.server.sendProgress();

        });

    });

     

    function updateProgress(message) {

        $(".progress-bar").width(message);

        $("#progressbarTitle").html(message + ' Complete');

    }

</script>

}
复制代码

你可以看到,上述代码先初始化和服务端的连接,然后设置被服务端调用的客户端方法updateProgress。

最后,我们建立了和服务端的连接并且启动了服务端的操作,你可以看到,我们调用了sendProgress方法——这已经在ProgressbarHub类中定义好了。这样当点击ID为start的按钮时动态为进度条更新了0-100的数值。

小结

在这篇博客中,探索了Bootstrap中丰富的组件,并将它结合到ASP.NET MVC项目中。通过实例可以发现,这类组件本质上是结合了各种现有Bootstrap元素以及添加了一些独特Class来实现。

更多的Bootstrap组件请参见:http://v3.bootcss.com/components/

源代码下载

本博客为木宛城主原创,基于Creative Commons Attribution 2.5 China Mainland License发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名木宛城主(包含链接)。如您有任何疑问或者授权方面的协商,请给我留言。

[转载]ASP.NET MVC使用Bootstrap系列(4)——使用JavaScript插件 - 木宛城主 - 博客园

mikel阅读(1208)

[转载]ASP.NET MVC使用Bootstrap系列(4)——使用JavaScript插件 – 木宛城主 – 博客园.

序言

Bootstrap的JavaScript插件是以JQuery为基础,提供了全新的功能并且还可以扩展现有的Bootstrap组件。通过添加data attribute(data 属性)可以轻松的使用这些插件,当然你也可以使用编程方式的API来使用。

为了使用Bootstrap插件,我们需要添加Bootstrap.js或者Bootstrap.min.js文件到项目中。这两个文件包含了所有 的Bootstrap插件,推荐引用Bootstrap.min.js。当然你也可以包含指定的插件来定制化Bootstrap.js,已达到更好的加载 速度。

Data属性 VS 编程API

Bootstrap提供了完全通过HTML标记的方式来使用插件,这意味着,你可以不写任何JavaScript代码,事实上这也是Bootstrap推荐的使用方式。

举个栗子,若要使Alert组件支持关闭功能,你可以通过添加data-dismiss=”alert”属性到按钮(Botton)或者链接(A)元素上,如下代码所示:

  1. <div class=”alert alert-danger”>
  2.     <button datadismiss=”alert” class=“close” type=“button”>x</button>
  3.     <strong>警告</strong>10秒敌人到
  4. </div>

当然,你也可以通过编程方式的API来实现同样的功能:

  1. <div class=”alert alert-danger” id=“myalert”>
  2.     <button datadismiss=”alert” class=“close” type=“button” onclick=“$(‘#myalert’).alert(‘close’)”>x</button>
  3.     <strong>警告</strong>10秒敌人到
  4. </div>

下拉菜单(dropdown.js)

使用dropdown插件(增强交互性),你可以将下拉菜单添加到绝大多数的Bootstrap组件上,比如navbar、tabs等。注:将下拉菜单触发器和下拉菜单都包裹在 .dropdown 里,或者另一个声明了 position: relative; 的元素中

如下是一个地域的菜单,通过Razor引擎动态绑定菜单元素:

  1. <div class=”form-group”>
  2.             @Html.LabelFor(model => model.TerritoryId, new { @class = “control-label col-md-2” })
  3.             @Html.HiddenFor(model => model.TerritoryId)
  4.             <div class=”col-md-10″>
  5.                 <div id=”territorydropdown” class=“dropdown”>
  6.                     <button id=”territorybutton” class=“btn btn-sm btn-info” datatoggle=“dropdown”>@Model.Territory.TerritoryDescription</button>
  7.                     <ul id=”territories” class=“dropdown-menu”>
  8.                         @foreach (var item in ViewBag.TerritoryId)
  9.                         {
  10.                             <li><a href=”#” tabindex=“-1” datavalue=“@item.Value”>@item.Text</a></li>
  11.                         }
  12.                     </ul>
  13.                 </div>
  14.             </div>
  15.         </div>

注意:通过添加data属性:data-toggle=”dropdown” 到Button或者Anchor上,可以切换dropdown下拉菜单,增加了交互性。其中菜单的<a>元素设置tabindex=-1,这样做是为了防止元素成为tab次序的一部分。

模态框(modal.js)

模态框以弹出框的形式可以为用户提供信息亦或者在此之中完成表单提交功能。Bootstrap的模态框本质上有3部分组成:模态框的头、体和一组放置于底部的按钮。你可以在模态框的Body中添加任何标准的HTML标记,包括Text或者嵌入的Youtube视频。

一般来说,务必将模态框的 HTML 代码放在文档的最高层级内(也就是说,尽量作为 body 标签的直接子元素),以避免其他组件影响模态框的展现和/或功能。

如下即为一个标准的模态框,包含Header、Body、Footer:

将下面这段HTML标记放在视图的Top或者Bottom:

  1. <div class=”modal fade” id=“deleteConfirmationModal” tabindex=“-1” role=“dialog” ariahidden=“true”>
  2.     <div class=”modal-dialog”>
  3.         <div class=”modal-content”>
  4.             <div class=”modal-header”>
  5.                 <button type=”button” class=“close” datadismiss=“modal” ariahidden=“true”>&times;</button>
  6.                 <h4 class=”modal-title”>删除</h4>
  7.             </div>
  8.             <div class=”modal-body”>
  9.                 <p>即将删除 ‘@Model.CompanyName’. </p>
  10.                 <p>
  11.                     <strong>你确定要继续吗</strong>
  12.                 </p>
  13.                 @using (Html.BeginForm(“Delete”, “Customers”, FormMethod.Post, new { @id = “delete-form”, role = “form” }))
  14.                 {
  15.                     @Html.HiddenFor(m => m.CustomerId)
  16.                     @Html.AntiForgeryToken()
  17.                 }
  18.             </div>
  19.             <div class=”modal-footer”>
  20.                 <button type=”button” class=“btn btn-default” onclick=“$(‘#delete-form’).submit();”>是.删除</button>
  21.                 <button type=”button” class=“btn btn-primary” datadismiss=“modal”>取消</button>
  22.             </div>
  23.         </div>
  24.     </div>
  25. </div>

注意:通过在Button上添加data属性:data-dismiss=”modal”可以实现对模态框的关闭,当然你也可以使用编程方式API来完成:

  1. <button type=”button” class=“btn btn-primary” onclick=“$(‘#deleteConfirmationModal’).modal(‘hide’)”>取消</button>

为了展示模态框,我们可以不写任何JavaScript代码,通过添加data-toggle=”modal”属性到Button或者Anchor元素上即可,同时,为了表示展示哪一个模态框,需要通过data-target来指定模态框的Id。

  1. <a href=”#” datatoggle=“modal” datatarget=“#deleteConfirmationModal”>Delete</a>

同样,也可以使用编程方式API来打开一个模态框:

  1. $(document).ready(function () {
  2. $(‘#deleteConfirmationModal’).modal(‘show’);
  3. });

标签页(tab.js)

Tabs可以使用在大的表单上,通过Tabs分割成若干部分显示局部信息,比如在Northwind数据库中存在Customer顾客信息,它包含了基本信息和地址,可以通过Tabs进行Split,如下所示:

要使用Tabs也是非常简单的:首先创建标准的无序列表<ul>元素,需要为它的class设置为nav nav-tabs 或者nav nav-pills。其中<li>包含的<a>元素即为Tab元素,需要设置其data-toggle为tab或者pill属性以及点击它指向的<div>内容:

  1. <ul id=”customertab” class=“nav nav-tabs”>
  2.     <li class=”active”><a href=”#info” datatoggle=“tab”>Customer Info</a></li>
  3.     <li><a href=”#address” datatoggle=“tab”>Address</a></li>
  4. </ul>

为了设置Tabs的内容,需要创建一个父<div>元素并设置class为tab-content,在父的div容器中为每一个Tab内容创建div元素,并设置class为tab-pane和标识的Id,通过添加active来激活哪一个Tab内容的显示。

  1. <div class=”tab-content well”>
  2. <div class=”tab-pane active” id=“info”>
  3. Customer Info
  4. </div>
  5. <div class=”tab-pane” id=“address”>
  6. Customer Address
  7. </div>
  8. </div>

当然也可以通过编程方式的API来激活:

  1. $(document).ready(function () {
  2. $(‘.nav-tabs a[href=”#address“]’).tab(‘show’);
  3. });

工具提示(tooltip.js)

Tooltip能为用户提供额外的信息,Boostrap Tooltip能被用在各种元素上,比如Anchor:

  1. <a datatoggle=”tooltip” dataplacement=“top” dataoriginaltitle=“这是提示内容” href=“#” >工具提示?</a>

你可以添加data-toggle=”tooltip”来使用tooltip,当然你也可以设置内容的显示位置,通过添加data-placement属性来实现,Bootstrap为我们提供了4种位置:

  • top
  • bottom
  • left
  • right

最后,通过设置data-original-title设置了需要显示的Title。

注意:为了性能的考虑,Tooltip的data-api是可选的,这意味着你必须手动初始化tooltip插件:

  1. <script type=”text/javascript“>
  2. $(document).ready(function () {
  3. $(‘[data-toggle=”tooltip“]’).tooltip();
  4. });
  5. </script>

弹出框(popover.js)

弹出框和Tooltip类似,都可以为用户提供额外的信息,但弹出框可以展示更多的信息,比如允许我们展示一个Header和Content,如下所示:

  1. <a datacontent=”关于基础建设问题需要有具体的研讨和商量….” dataplacement=“bottom” title=“” datatoggle=“popover” href=“#” dataoriginaltitle=“转至百度”>城市规划</a>

和Tooltip一样,为了性能的考虑,data-api是可选的,这意味着你必须手动初始化popover插件:

  1. <script type=”text/javascript“>
  2. $(document).ready(function () {
  3. $(‘[data-toggle=”popover“]’).popover();
  4. });
  5. </script>

显示的结果如下所示:

手风琴组件(collapse.js)

手风琴组件有若干panel groups组成,每一个panel group依次包含了若干header和content 元素,最常见的使用场景就是FAQ,如下所示:

为了使用手风琴组件,需要对Panel Heading中的<a>设置data-toggle=”collapse”和点击它展开的容器(Div)Id,具体如下所示:

  1. <div class=”row”>
  2.         <div class=”panel-group” id=“accordion”>
  3.             <div class=”panel panel-default”>
  4.                 <div class=”panel-heading”>
  5.                     <h4 class=”panel-title”>
  6.                         <a datatoggle=”collapse” dataparent=“#accordion”
  7.                            href=“#questionOne”>
  8.                             问题 1:什么是 Microsoft MVP 奖励
  9.                         </a>
  10.                     </h4>
  11.                 </div>
  12.                 <div id=”questionOne” class=“panel-collapse collapse in”>
  13.                     <div class=”panel-body”>
  14.                         解 答 1:Microsoft 最有价值专家 (MVP) 奖励是一项针对 Microsoft 技术社区杰出成员的年度奖励,根据 Microsoft 技术社区的成员在过去 12 个月内对 Microsoft 相关离线和在线技术社区所作贡献的大小而确定
  15.                     </div>
  16.                 </div>
  17.             </div>
  18.             <div class=”panel panel-default”>
  19.                 <div class=”panel-heading”>
  20.                     <h4 class=”panel-title”>
  21.                         <a datatoggle=”collapse” dataparent=“#accordion”
  22.                            href=“#questionTwo”>
  23.                             问题 2:MVP 奖励存在的意义何在
  24.                         </a>
  25.                     </h4>
  26.                 </div>
  27.                 <div id=”questionTwo” class=“panel-collapse collapse”>
  28.                     <div class=”panel-body”>
  29.                         解 答 2:我们相信,技术社区可以促进自由且客观的知识交流,因此可以构建出一个可靠、独立、真实且可使每个人获益的专业知识来源。Microsoft MVP 奖励旨在表彰那些能积极与其他技术社区成员分享自身知识的全球杰出技术社区领导者
  30.                     </div>
  31.                 </div>
  32.             </div>
  33.         </div>
  34.     </div>

旋转木马组件(carousel.js)

Carousel它本质上是一个幻灯片,循环展示不同的元素,通常展示的是图片,就像旋转木马一样。你可以在许多网站上看到这种组件,要使用它也是非常方便的:

  • Carousel组件被包含在一个classcarousel以及data-ride“carousel”<div>元素中。
  • 在上述容器里添加一个有序列表<ol>,它将渲染成小圆点代表当前激活的幻灯片(显示在右下角)。
  • 紧接着,添加一个classcarousel-inner<div>,这个<div>容器包含了实际的幻灯片
  • 然后,添加左右箭头能让用户自由滑动幻灯片
  • 最后,设置滑动切换的时间间隔,通过设置data- interval来实现。当然也可以通过编程方式API来实现
  1. $(‘#myCarousel’).carousel({
  2. interval: 10000
  3. })

将下面HTML标识放在View中即可:

View Code

展示的效果如下:

小结

在这篇博客中介绍了常见的Bootstrap插件,通过使用数据属性和编程方式的API来使用这些插件,更多插件访问:http://v3.bootcss.com/javascript/ 获取。

本博客为木宛城主原创,基于Creative Commons Attribution 2.5 China Mainland License发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名木宛城主(包含链接)。如您有任何疑问或者授权方面的协商,请给我留言。

[转载]ASP.NET MVC使用Bootstrap系列(5)——创建ASP.NET MVC Bootstrap Helpers - 木宛城主 - 博客园

mikel阅读(1087)

[转载]ASP.NET MVC使用Bootstrap系列(5)——创建ASP.NET MVC Bootstrap Helpers – 木宛城主 – 博客园.

序言

ASP.NET MVC允许开发者创建自定义的HTML Helpers,不管是使用静态方法还是扩展方法。一个HTML Helper本质上其实是输出一段HTML字符串。

HTML Helpers能让我们在多个页面上公用同一段HTML标记,这样不仅提高了稳定性也便于开发者去维护。当然对于这些可重用的代码,开发者也方便对他们进行单元测试。所以,创建ASP.NET MVC Bootstrap Helpers是及其有必要的。

内置的HTML Helpers

ASP.NET MVC内置了若干标准HTML Helpers,通过@HTML来调用这些方法在视图引擎中解析、渲染输出HTML内容,这允许开发者在多个视图中重用公共的方法。

举个栗子,以下代码产生一个type等于text的Input ,并且其id和name都等于CustomerName,其Value等于Northwind Traders:

  1. @Html.TextBox(“CustomerName”,”Northwind Traders”);

大多数内置的HTML helpers提供传入匿名类型为元素产生指定HTML属性的选项,对上述的@HTML.TextBox方法稍作修改,通过传入匿名类型设置输出元素的style属性:

  1. @Html.TextBox(“CustomerName”,”Northwind Traders”, new { style=”background-color:Blue;” })

创建自定义的Helpers

因为Bootstrap提供了大量不同的组件,所以创建Bootstrap helpers可以在多个视图上快速使用这些组件。在ASP.NET MVC中最简单创建Bootstrap helpers是通过@helper语法来实现。一个自定义的helper可以包含任何HTML标记甚至Razor标记,你可以通过如下步骤来创建:

  • 在项目的根目录创建文件夹App_Code
  • 在App_Code文件夹中新建BootstrapHelpers.cshtml文件并加入如下代码
  1. @helper PrimaryButtonSmall(string id,string caption)
  2. {
  3.     <button id=”@id” type=”buttonclass=”btn btn-primary btn-sm“>@caption</button>
  4. }

上述代码使用@helper创建了一个新的名为PrimaryButtonSmall helper,它接受2个参数,分别是Id和caption。其中,它产生一个Button类型的HTML标记并设置了Bootstrap的样式。

注意:任何自定义的helpers必须存在App_Code文件夹中,这样才能被ASP.NET MVC视图识别。

  • 在视图中通过 @BootstrapHelpers.PrimaryButtonSmall(“btnSave”,“保存”)来使用新创建的helper。
  • 它将产生如下Bootstrap HTML元素:

当然,为了让我们的helper更加通用性,比如指定大小、样式等,对上述稍作如下修改,增加传入的参数:

  1. @helper Button(string style, string size, string caption, string id)
  2. {
  3.     <button id=”@id” type=“button” class=“btn btn-@style btn-@size”>@caption </button>
  4. }

现在我们可以这样去使用:

  1. @BootstrapHelpers.Button(“danger”,”lg”,”危险”,”btnDanger”)

它将产生如下样式的按钮:

不过,这种方式的helper唯一的不足是你需要”hard code”传入样式和尺寸,这可能需要你非常熟悉Bootstrap的样式。

使用静态方法创建Helpers

通过静态方法同样也能快速方便的创建自定义Bootstrap helpers,同样它也是返回了HTML标记,要创建静态方法,你可以按照如下步骤来实现:

  • 添加命了Helpers的文件夹
  • 创建如下枚举类
  1. public class ButtonHelper
  2.    {
  3.        public static MvcHtmlString Button(string caption, Enums.ButtonStyle style, Enums.ButtonSize size)
  4.        {
  5.            if (size != Enums.ButtonSize.Normal)
  6.            {
  7.                return new MvcHtmlString(string.Format(“<button type=\”button\” class=\”btn btn-{0} btn-{1}\”>{2}</button>“, style.ToString().ToLower(), ToBootstrapSize(size), caption));
  8.            }
  9.            return new MvcHtmlString(string.Format(“<button type=\”button\” class=\”btn btn-{0}\”>{1}</button>“, style.ToString().ToLower(), caption));
  10.        }
  11.        private static string ToBootstrapSize(Enums.ButtonSize size)
  12.        {
  13.            string bootstrapSize = string.Empty;
  14.            switch (size)
  15.            {
  16.                case Enums.ButtonSize.Large:
  17.                    bootstrapSize = “lg“;
  18.                    break;
  19.                case Enums.ButtonSize.Small:
  20.                    bootstrapSize = “sm“;
  21.                    break;
  22.                case Enums.ButtonSize.ExtraSmall:
  23.                    bootstrapSize = “xs“;
  24.                    break;
  25.            }
  26.            return bootstrapSize;
  27.        }
  28.    }

Button方法返回了一个MvcHtmlString对象,它代表了编码过后的HTML字符。

  • 通过使用静态方法来调用:
  1. @ButtonHelper.Button(“危险“, Enums.ButtonStyle.Danger, Enums.ButtonSize.Large)

你可以和之前的”hard code”写法进行比较,尽管他们产生相同的结果:

  1. @BootstrapHelpers.Button(“danger“,”lg“,”危险“,”btnDanger“)

使用扩展方法创建Helpers

内置的ASP.NET MVC helper(@HTML)是基于扩展方法的,我们可以再对上述的静态方法进行升级——使用扩展方法来创建Bootstrap helpers。

  • 在Helpers文件夹下创建ButtonExtensions类
  • 修改ButtonExtensions为Static类型
  • 修改Namespace为System.Web.Mvc.Html,这样方便@HTML调用扩展方法
  • 添加扩展方法,返回MvcHtmlString
  1. public static MvcHtmlString BootstrapButton(this HtmlHelper helper, string caption, Enums.ButtonStyle style, Enums.ButtonSize size)
  2.         {
  3.             if (size != Enums.ButtonSize.Normal)
  4.             {
  5.                 return new MvcHtmlString(string.Format(“<button type=\”button\” class=\”btn btn-{0} btn-{1}\”>{2}</button>“, style.ToString().ToLower(), ToBootstrapSize(size), caption));
  6.             }
  7.             return new MvcHtmlString(string.Format(“<button type=\”button\” class=\”btn btn-{0}\”>{1}</button>“, style.ToString().ToLower(), caption));
  8.         }

因为BootstrapButton方法是扩展方法,通过如下方式去调用:

  1. @Html.BootstrapButton(“很危险“,Enums.ButtonStyle.Danger,Enums.ButtonSize.Large)

写法虽不同,但返回的结果都是一致的。

创建Fluent Helpers

Fluent Interface(参考:http://martinfowler.com/bliki/FluentInterface.html)用于软件开发实现 了一种面向对象的API,以这种方式,它提供了更多的可读性代码,易于开发人员理解。通常通过链式编程来实现。

举个栗子,我们将创建一个HTML helper来产生一个可关闭的警告框,使用Fluent Interface可以这样来调用:

  1. @Html.Alert(“警告“).Warning().Dismissible()

所以要创建Fluent helpers,需要实现如下步骤:

  • 创建IFluentAlert实现IHtmlString接口,这是非常重要的一步,对于ASP.NET MVC Razor视图引擎,如果@之后返回的类型实现了IHtmlString接口,那么视图引擎会自动调用ToHtmlString()方法,返回实际的HTML标记。
  1. public interface IAlertFluent : IHtmlString
  2.     {
  3.         IAlertFluent Dismissible(bool canDismiss = true);
  4.     }
  • 创建IAlert实现IFluentAlert接口
  1. public interface IAlert : IAlertFluent
  2. {
  3.     IAlertFluent Danger();
  4.     IAlertFluent Info();
  5.     IAlertFluent Success();
  6.     IAlertFluent Warning();
  7. }
  • 创建Alert继承IAlert接口
  1. public class Alert : IAlert
  2.    {
  3.        private Enums.AlertStyle _style;
  4.        private bool _dismissible;
  5.        private string _message;
  6.        public Alert(string message)
  7.        {
  8.            _message = message;
  9.        }
  10.        public IAlertFluent Danger()
  11.        {
  12.            _style = Enums.AlertStyle.Danger;
  13.            return new AlertFluent(this);
  14.        }
  15.        public IAlertFluent Info()
  16.        {
  17.            _style = Enums.AlertStyle.Info;
  18.            return new AlertFluent(this);
  19.        }
  20.        public IAlertFluent Success()
  21.        {
  22.            _style = Enums.AlertStyle.Success;
  23.            return new AlertFluent(this);
  24.        }
  25.        public IAlertFluent Warning()
  26.        {
  27.            _style = Enums.AlertStyle.Warning;
  28.            return new AlertFluent(this);
  29.        }
  30.        public IAlertFluent Dismissible(bool canDismiss = true)
  31.        {
  32.            this._dismissible = canDismiss;
  33.            return new AlertFluent(this);
  34.        }
  35.        public string ToHtmlString()
  36.        {
  37.            var alertDiv = new TagBuilder(“div“);
  38.            alertDiv.AddCssClass(“alert“);
  39.            alertDiv.AddCssClass(“alert-” + _style.ToString().ToLower());
  40.            alertDiv.InnerHtml = _message;
  41.            if (_dismissible)
  42.            {
  43.                alertDiv.AddCssClass(“alert-dismissable“);
  44.                alertDiv.InnerHtml += AddCloseButton();
  45.            }
  46.            return alertDiv.ToString();
  47.        }
  48.        private static TagBuilder AddCloseButton()
  49.        {
  50.            var closeButton = new TagBuilder(“button“);
  51.            closeButton.AddCssClass(“close“);
  52.            closeButton.Attributes.Add(“data-dismiss“, “alert“);
  53.            closeButton.InnerHtml = “&times;“;
  54.            return closeButton;
  55.        }
  56.    }

上述代码中,通过TagBuilder可以快速的创建HTML元素。

  • 创建AlertFluent继承IAlertFluent
  1. public class AlertFluent : IAlertFluent
  2.     {
  3.         private readonly Alert _parent;
  4.         public AlertFluent(Alert parent)
  5.         {
  6.             _parent = parent;
  7.         }
  8.         public IAlertFluent Dismissible(bool canDismiss = true)
  9.         {
  10.             return _parent.Dismissible(canDismiss);
  11.         }
  12.         public string ToHtmlString()
  13.         {
  14.             return _parent.ToHtmlString();
  15.         }
  16.     }
  • 最后创建静态方法
  1. public static class AlertHelper
  2. {
  3.     public static Alert Alert(this HtmlHelper html,string message)
  4.     {
  5.         return new Alert(message);
  6.     }
  7. }

通过构建这种Fluent API,我们可以链式的去创建Bootstrap 组件,这对于不熟悉Bootstrap Framework的人来说是非常方便的,我们可以使用@HTML.Alert(“Title”).Danger().Dismissible()来创建如下风格的警告框:

创建自动闭合的Helpers

在ASP.NET MVC中,内置的@HTML.BeginForm() helper就是一个自动闭合的helper。当然我们也能自定义自动闭合的helpers,只要 实现IDisposable接口即可。使用IDisposable接口,当对象Dispose时我们输出元素的闭合标记,具体按照如下步骤:

  • 所以在Helpers文件夹下创建一个名为Panel的文件夹
  • 添加Panel,并实现IDisposable接口
  1. public class Panel : IDisposable
  2.     {
  3.         private readonly TextWriter _writer;
  4.         public Panel(HtmlHelper helper, string title, Enums.PanelStyle style = Enums.PanelStyle.Default)
  5.         {
  6.             _writer = helper.ViewContext.Writer;
  7.             var panelDiv = new TagBuilder(“div“);
  8.             panelDiv.AddCssClass(“panel-” + style.ToString().ToLower());
  9.             panelDiv.AddCssClass(“panel“);
  10.             var panelHeadingDiv = new TagBuilder(“div“);
  11.             panelHeadingDiv.AddCssClass(“panel-heading“);
  12.             var heading3Div = new TagBuilder(“h3“);
  13.             heading3Div.AddCssClass(“panel-title“);
  14.             heading3Div.SetInnerText(title);
  15.             var panelBodyDiv = new TagBuilder(“div“);
  16.             panelBodyDiv.AddCssClass(“panel-body“);
  17.             panelHeadingDiv.InnerHtml = heading3Div.ToString();
  18.             string html = string.Format(“{0}{1}{2}“, panelDiv.ToString(TagRenderMode.StartTag), panelHeadingDiv, panelBodyDiv.ToString(TagRenderMode.StartTag));
  19.             _writer.Write(html);
  20.         }
  21.         public void Dispose()
  22.         {
  23.             _writer.Write(“</div></div>“);
  24.         }
  25.     }

上述代码中利用Write属性可以在当前视图中输出HTML标记,并在Dispose方法里输出2个闭合的<div>标签。

注意,我们重写了TagBuilder的ToString()方法,只让它生成<div>元素的开始标签。

  • 在View中使用自动闭合的helpers
  1. @using (Html.Panel(“Title“, Enums.PanelStyle.Success))
  2. {
  3.     <p>这是自动闭合的Helpers</p>
  4.     <p>panel..</p>
  5. }

产生的结果如下:

小结

在这篇博客中,为了减少书写HTML标记,我们创建了若干Bootstrap helpers来实现。这些helpers的意义在于能让不了解Bootstrap Framework的人也能快速上手Bootstrap。

参考代码下载

本博客为木宛城主原创,基于Creative Commons Attribution 2.5 China Mainland License发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名木宛城主(包含链接)。如您有任何疑问或者授权方面的协商,请给我留言。