• 依赖注入(DI)和Ninject


    本文目录:

    1.为什么需要依赖注入

    2.什么是依赖注入

    3.使用NuGet安装库

    4.使用Ninject的一般步骤

    5.Ninject如何提高编码效率

    6.Ninject的绑定方式

    7.在ASP.NET MVC中使用Ninject

    为什么需要依赖注入

    在[ASP.NET MVC 小牛之路]系列的理解MVC模式文章中,我们提到MVC的一个重要特征是关注点分离(separation of concerns)。我们希望应用程序的各部分组件尽可能多的相互独立、尽可能少的相互依赖。

    我们的理想情况是:一个组件可以不知道也可以不关心其他的组件,但通过提供的公开接口却可以实现其他组件的功能调用。这种情况就是所谓的松耦合

    举个简单的例子。我们要为商品定制一个“高级”的价钱计算器LinqValueCalculator,这个计算器需要实现IValueCalculator接口。如下代码所示:

    复制代码
    public interface IValueCalculator {
        decimal ValueProducts(params Product[] products);
    }
    
    public class LinqValueCalculator : IValueCalculator {
        public decimal ValueProducts(params Product[] products) {
            return products.Sum(p => p.Price);
        }
    }

    Product类和前两篇博文中用到的是一样的。现在有个购物车ShoppingCart类,它需要有一个能计算购物车内商品总价钱的功能。但购物车本身没有计算的功能,因此,购物车要嵌入一个计算器组件,这个计算器组件可以是LinqValueCalculator组件,但不一定是LinqValueCalculator组件(以后购物车升级,可能会嵌入别的更高级的计算器)。那么我们可以这样定义购物车ShoppingCart类:

    复制代码
     1 public class ShoppingCart {
     2     //计算购物车内商品总价钱
     3     public decimal CalculateStockValue() {
     4         Product[] products = { 
     5             new Product {Name = "西瓜", Category = "水果", Price = 2.3M}, 
     6             new Product {Name = "苹果", Category = "水果", Price = 4.9M}, 
     7             new Product {Name = "空心菜", Category = "蔬菜", Price = 2.2M}, 
     8             new Product {Name = "地瓜", Category = "蔬菜", Price = 1.9M} 
     9         };
    10         IValueCalculator calculator = new LinqValueCalculator();
    11 
    12         //计算商品总价钱 
    13         decimal totalValue = calculator.ValueProducts(products);
    14 
    15         return totalValue;
    16     }
    17 }

    ShoppingCart类是通过IValueCalculator接口(而不是通过LinqValueCalculator)来计算商品总价钱的。如果以后购物车升级需要使用更高级的计算器,那么只需要改变第10行代码中new后面的对象(即把LinqValueCalculator换掉),其他的代码都不用变动。这样就实现了一定的松耦合。这时三者的关系如下图所示:

    这个图说明,ShoppingCart类既依赖IValueCalculator接口又依赖LinqValueCalculator类。这样就有个问题,用现实世界的话来讲就是,如果嵌入在购物车内的计算器组件坏了,会导致整个购物车不能正常工作,岂不是要把整个购物车要换掉!最好的办法是将计算器组件和购物车完全独立开来,这样不管哪个组件坏了,只要换对应的组件即可。即我们要解决的问题是,要让ShoppingCart组件和LinqValueCalculator组件完全断开关系,而依赖注入这种设计模式就是为了解决这种问题。

    什么是依赖注入

    上面实现的部分松耦合显然并不是我们所需要的。我们所需要的是,在一个类内部,不通过创建对象的实例而能够获得某个实现了公开接口的对象的引用。这种“需要”,就称为DI(依赖注入,Dependency Injection),和所谓的IoC(控制反转,Inversion of Control )是一个意思。

    DI是一种通过接口实现松耦合的设计模式。初学者可能会好奇网上为什么有那么多技术文章对DI这个东西大兴其笔,是因为DI对于基于几乎所有框架下,要高效开发应用程序,它都是开发者必须要有的一个重要的理念,包括MVC开发。比如其优点之一是它可以使应用程序的各个部分更方便地进行测试。

    DI模式可分为两个部分。一是移除对组件(上面示例中的LinqValueCalculator)的依赖,二是通过类的构造函数(或类的Setter访问器)来传递实现了公开接口的组件的引用。如下面代码所示:

    复制代码
    public class ShoppingCart {
        IValueCalculator calculator;
        
        //构造函数,参数为实现了IEmailSender接口的类的实例
        public ShoppingCart(IValueCalculator calcParam) {
            calculator = calcParam;
        }
    
        //计算购物车内商品总价钱
        public decimal CalculateStockValue() {
            Product[] products = { 
                new Product {Name = "西瓜", Category = "水果", Price = 2.3M}, 
                new Product {Name = "苹果", Category = "水果", Price = 4.9M}, 
                new Product {Name = "空心菜", Category = "蔬菜", Price = 2.2M}, 
                new Product {Name = "地瓜", Category = "蔬菜", Price = 1.9M} 
            };
    
            //计算商品总价钱 
            decimal totalValue = calculator.ValueProducts(products);
    
            return totalValue;
        }
    }

    这样我们就彻底断开了ShoppingCart和LinqValueCalculator之间的依赖关系。某个实现了IValueCalculator接口的类(示例中的MyEmailSender)的实例引用作为参数,传递给ShoppingCart类的构造函数。但是ShoppingCart类不知道也不关心这个实现了IValueCalculator接口的类是什么,更没有责任去操作这个类。 这时我们可以用下图来描述ShoppingCart、LinqValueCalculator和IValueCalculator之间的关系:

    在程序运行的时候,依赖被注入到ShoppingCart,这个依赖就是,通过ShoppingCart构造函数传递实现了IValueCalculator接口的类的实例引用。在程序运行之前(或编译时),ShoppingCart和任何实现IValueCalculator接口的类没有任何依赖关系。(注意,程序运行时是有具体依赖关系的。)

    注意,上面示例使用的注入方式称为“构造注入”,我们也可以通过属性来实现注入,这种注入被称为“setter 注入”,就不举例了,朋友们可以看看T2噬菌体的文章依赖注入那些事儿来对DI进行更多的了解。

    由于经常会在编程时使用到DI,所以出现了一些DI的辅助工具(或叫DI容器),如Unity和Ninject等。由于Ninject的轻量和用简单,加上本人只用过Ninject,所以本系列文章选择用它来开发MVC应用程序。下面开始介绍Ninject,但在这之前,先来介绍一个安装Ninject需要用到的插件-NuGet。

    使用NuGet安装库

    NuGet 是一种 Visual Studio 扩展,它能够简化在 Visual Studio 项目中添加、更新和删除库(部署为程序包)的操作。比如你要在项目中使用Log4Net这个库,如果没有NuGet这个扩展,你可能要先到网上搜索Log4Net,再将程序包的内容解压缩到解决方案中的特定位置,然后在各项目工程中依次添加程序集引用,最后还要使用正确的设置更新 web.config。而NuGet可以简化这一切操作。例如我们在讲依赖注入的项目中,若要使用一个NuGet库,可直接右击项目(或引用),选择“管理NuGet程序包”(VS2010下为“Add Library Package Reference”),如下图:

    在弹出如下窗口中选择“联机”,搜索“Ninject”,然后进行相应的操作即可:

    在本文中我们只需要知道如何使用NuGet来安装库就可以了。NuGet的详细使用方法可查看MSDN文档:使用 NuGet 管理项目库

    使用Ninject的一般步骤

    在使用Ninject前先要创建一个Ninject内核对象,代码如下:

    复制代码
    class Program { 
        static void Main(string[] args) { 
            //创建Ninject内核实例
            IKernel ninjectKernel = new StandardKernel(); 
        } 
    }

    使用Ninject内核对象一般可分为两个步骤。第一步是把一个接口(IValueCalculator)绑定到一个实现该接口的类(LinqValueCalculator),如下:

    ...
    //绑定接口到实现了该接口的类 ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator<();
    ...

    这个绑定操作就是告诉Ninject,当接收到一个请求IValueCalculator接口的实现时,就返回一个LinqValueCalculator类的实例。

    第二步是用Ninject的Get方法去获取IValueCalculator接口的实现。这一步,Ninject将自动为我们创建LinqValueCalculator类的实例,并返回该实例的引用。然后我们可以把这个引用通过构造函数注入到ShoppingCart类。如下代码所示:

    复制代码
    ...
    // 获得实现接口的对象实例
    IValueCalculator calcImpl = ninjectKernel.Get<IValueCalculator>(); 
    // 创建ShoppingCart实例并注入依赖
    ShoppingCart cart = new ShoppingCart(calcImpl); 
    // 计算商品总价钱并输出结果
    Console.WriteLine("Total: {0:c}", cart.CalculateStockValue());
    ...

    Ninject的使用的一般步骤就是这样。该示例可正确输出如下结果:

    但看上去Ninject的使用好像使得编码变得更加烦琐,朋友们会问,直接使用下面的代码不是更简单吗:

    复制代码
    ...
    IValueCalculator calcImpl = new LinqValueCalculator();
    ShoppingCart cart = new ShoppingCart(calcImpl);
    Console.WriteLine("Total: {0:c}", cart.CalculateStockValue());
    ...

    的确,对于单个简单的DI,用Ninject确实显得麻烦。但如果添加多个复杂点的依赖关系,使用Ninject则可大大提高编码的工作效率。

    Ninject如何提高编码效率

    当我们请求Ninject创建某个类型的实例时,它会检查这个类型和其它类型之间的耦合关系。如果存在依赖关系,那么Ninject会根据依赖处理理它们,并创建所有所需类的实例。为了解释这句话和说明使用Ninject编码的便捷,我们再创建一个接口IDiscountHelper和一个实现该接口的类DefaultDiscountHelper,代码如下:

    复制代码
    //折扣计算接口
    public interface IDiscountHelper {
        decimal ApplyDiscount(decimal totalParam);
    }
    
    //默认折扣计算器
    public class DefaultDiscountHelper : IDiscountHelper {
        public decimal ApplyDiscount(decimal totalParam) {
            return (totalParam - (1m / 10m * totalParam));
        }
    }

    IDiscounHelper接口声明了ApplyDiscount方法,DefaultDiscounterHelper实现了该接口,并定义了打9折的ApplyDiscount方法。然后我们可以把IDiscounHelper接口作为依赖添加到LinqValueCalculator类中。代码如下:

    复制代码
    public class LinqValueCalculator : IValueCalculator { 
        private IDiscountHelper discounter; 
     
        public LinqValueCalculator(IDiscountHelper discountParam) { 
            discounter = discountParam; 
        } 
     
        public decimal ValueProducts(params Product[] products) { 
            return discounter.ApplyDiscount(products.Sum(p => p.Price)); 
        } 
    }

    LinqValueCalculator类添加了一个用于接收IDiscountHelper接口的实现的构造函数,然后在ValueProducts方法中调用该接口的ApplyDiscount方法对计算出的商品总价钱进行打折处理,并返回折后总价。

    到这,我们先来画个图理一理ShoppingCart、LinqValueCalculator、IValueCalculator以及新添加的IDiscountHelper和DefaultDiscounterHelper之间的关系:

    以此,我们还可以添加更多的接口和实现接口的类,接口和类越来越多时,它们的关系图看上去会像一个依赖“链”,和生物学中的分子结构图差不多。

    按照前面说的使用Ninject的“二个步骤”,现在我们在Main中的方法中编写用于计算购物车中商品折后总价钱的代码,如下所示:

    复制代码
     1 class Program {
     2     static void Main(string[] args) {
     3         IKernel ninjectKernel = new StandardKernel();
     4 
     5         ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator>();
     6         ninjectKernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>();
     7 
     8         IValueCalculator calcImpl = ninjectKernel.Get<IValueCalculator>();
     9         ShoppingCart cart = new ShoppingCart(calcImpl);
    10         Console.WriteLine("Total: {0:c}", cart.CalculateStockValue());
    11         Console.ReadKey();
    12     }
    13 }

    输出结果:

    代码一目了然,虽然新添加了一个接口和一个类,但Main方法中只增加了第6行一句代码,获取实现IValueCalculator接口的对象实例的代码不需要做任何改变。
    定位到代码的第8行,这一行代码,Ninject为我们做的事是:
      当我们需要使用IValueCalculator接口的实现时(通过Get方法),它便为我们创建LinqValueCalculator类的实例。而当创建LinqValueCalculator类的实例时,它检查到这个类依赖IDiscountHelper接口。于是它又创建一个实现了该接口的DefaultDiscounterHelper类的实例,并通过构造函数把该实例注入到LinqValueCalculator类。然后返回LinqValueCalculator类的一个实例,并赋值给IValueCalculator接口的对象(第8行的calcImpl)。

    总之,不管依赖“链”有多长有多复杂,Ninject都会按照上面这种方式检查依赖“链”上的每个接口和实现接口的类,并自动创建所需要的类的实例。在依赖“链”越长越复杂的时候,更能显示使用Ninject编码的高效率。

    Ninject的绑定方式

    我个人将Ninject的绑定方式分为:一般绑定、指定值绑定、自我绑定、派生类绑定和条件绑定。这样分类有点牵强,只是为了本文的写作需要和方便读者阅读而分,并不是官方的分类

    1、一般绑定

    在前文的示例中用Bind和To方法把一个接口绑定到实现该接口的类,这属于一般的绑定。通过前文的示例相信大家已经掌握了,在这就不再累述。

    2、指定值绑定

    我们知道,通过Get方法,Ninject会自动帮我们创建我们所需要的类的实例。但有的类在创建实例时需要给它的属性赋值,如下面我们改造了一下的DefaultDiscountHelper类:

    复制代码
    public class DefaultDiscountHelper : IDiscountHelper { 
        public decimal DiscountSize { get; set; } 
     
        public decimal ApplyDiscount(decimal totalParam) { 
            return (totalParam - (DiscountSize / 10m * totalParam)); 
        } 
    }

    给DefaultDiscountHelper类添加了一个DiscountSize属性,实例化时需要指定折扣值(DiscountSize属性值),不然ApplyDiscount方法就没意义。而实例化的动作是Ninject自动完成的,怎么告诉Ninject在实例化类的时候给某属性赋一个指定的值呢?这时就需要用到参数绑定,我们在绑定的时候可以通过给WithPropertyValue方法传参的方式指定DiscountSize属性的值,如下代码所示:

    复制代码
    public static void Main() {
        IKernel ninjectKernel = new StandardKernel();
    
        ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator>();
        ninjectKernel.Bind<IDiscountHelper>()
            .To<DefaultDiscountHelper>().WithPropertyValue("DiscountSize", 5M);
    
        IValueCalculator calcImpl = ninjectKernel.Get<IValueCalculator>();
        ShoppingCart cart = new ShoppingCart(calcImpl);
        Console.WriteLine("Total: {0:c}", cart.CalculateStockValue());
        Console.ReadKey();
    }

    只是在Bind和To方法后添加了一个WithPropertyValue方法,其他代码都不用变,再一次见证了用Ninject编码的高效。
    WithPropertyValue方法接收了两个参数,一个是属性名(示例中的"DiscountSize"),一个是属性值(示例中的5)。运行结果如下:

    如果要给多个属性赋值,则可以在Bind和To方式后添加多个WithPropertyValue(<属性名>,<属性值>)方法。

    我们还可以在类的实例化的时候为类的构造函数传递参数。为了演示,我们再把DefaultDiscountHelper类改一下:

    复制代码
    public class DefaultDiscountHelper : IDiscountHelper { 
        private decimal discountRate; 
     
        public DefaultDiscountHelper(decimal discountParam) { 
            discountRate = discountParam; 
        } 
     
        public decimal ApplyDiscount(decimal totalParam) { 
            return (totalParam - (discountRate/ 10m * totalParam)); 
        } 
    }

    显然,DefaultDiscountHelper类在实例化的时候必须给构造函数传递一个参数,不然程序会出错。和给属性赋值类似,只是用的方法是WithConstructorArgument(<参数名>,<参数值>),绑定方式如下代码所示:

    复制代码
    ...
    ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator>(); 
    ninjectKernel.Bind<IDiscountHelper>() 
        .To< DefaultDiscountHelper>().WithConstructorArgument("discountParam", 5M);
    ...

    同样,只需要更改一行代码,其他代码原来怎么写还是怎么写。如果构造函数有多个参数,则需在Bind和To方法后面加上多个WithConstructorArgument即可。

    3.自我绑定

    Niject的一个非常好用的特性就是自绑定。当通过Bind和To方法绑定好接口和类后,可以直接通过ninjectKernel.Get<类名>()来获得一个类的实例。

    在前面的几个示例中,我们都是像下面这样来创建ShoppingCart类实例的:

    ...
    IValueCalculator calcImpl = ninjectKernel.Get<IValueCalculator>();
    ShoppingCart cart = new ShoppingCart(calcImpl);
    ...

    其实有一种更简单的定法,如下:

    ... 
    ShoppingCart cart = ninjectKernel.Get<ShoppingCart>(); 
    ... 

    这种写法不需要关心ShoppingCart类依赖哪个接口,也不需要手动去获取该接口的实现(calcImpl)。当通过这句代码请求一个ShoppingCart类的实例的时候,Ninject会自动判断依赖关系,并为我们创建所需接口对应的实现。这种方式看起来有点怪,其实中规中矩的写法是:

    ...
    ninjectKernel.Bind<ShoppingCart>().ToSelf();
    ShoppingCart cart = ninjectKernel.Get<ShoppingCart>();
    ...

    这里有自我绑定用的是ToSelf方法,在本示例中可以省略该句。但用ToSelf方法自我绑定的好处是可以在其后面用WithXXX方法指定构造函数参数、属性等等的值。

    注意,自我绑定只可用于实体类,不能用于抽象类和接口。

    4.派生类绑定

    通过一般绑定,当请求一个接口的实现时,Ninject会帮我们自动创建实现接口的类的实例。我们说某某类实现某某接口,也可以说某某类继承某某接口。如果我们把接口当作一个父类,是不是也可以把父类绑定到一个继承自该父类的子类呢?我们来实验一把。先改造一下ShoppingCart类,给它的CalculateStockValue方法改成虚方法:

    复制代码
    public class ShoppingCart {
        protected IValueCalculator calculator;
        protected Product[] products;
    
        //构造函数,参数为实现了IEmailSender接口的类的实例
        public ShoppingCart(IValueCalculator calcParam) {
            calculator = calcParam;
            products = new[]{ 
                new Product {Name = "西瓜", Category = "水果", Price = 2.3M}, 
                new Product {Name = "苹果", Category = "水果", Price = 4.9M}, 
                new Product {Name = "空心菜", Category = "蔬菜", Price = 2.2M}, 
                new Product {Name = "地瓜", Category = "蔬菜", Price = 1.9M} 
            };
        }
    
        //计算购物车内商品总价钱
        public virtual decimal CalculateStockValue() {
            //计算商品总价钱 
            decimal totalValue = calculator.ValueProducts(products);
            return totalValue;
        }
    }

    再添加一个ShoppingCart类的子类:

    复制代码
    public class LimitShoppingCart : ShoppingCart {
        public LimitShoppingCart(IValueCalculator calcParam)
            : base(calcParam) {
        }
    
        public override decimal CalculateStockValue() {
            //过滤价格超过了上限的商品
            var filteredProducts = products.Where(e => e.Price < ItemLimit);
    return calculator.ValueProducts(filteredProducts.ToArray()); } public decimal ItemLimit { get; set; } }

    然后把父类ShoppingCart绑定到子类LimitShoppingCart:

    复制代码
    public static void Main() {
        IKernel ninjectKernel = new StandardKernel();
    
        ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator>();
        ninjectKernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>()
            .WithPropertyValue("DiscountSize", 5M);
        //派生类绑定
        ninjectKernel.Bind<ShoppingCart>().To<LimitShoppingCart>()
            .WithPropertyValue("ItemLimit", 3M);
    
        ShoppingCart cart = ninjectKernel.Get<ShoppingCart>();
        Console.WriteLine("Total: {0:c}", cart.CalculateStockValue());
        Console.ReadKey();
    }

    运行结果:

    从运行结果可以看出,cart对象调用的是子类的CalculateStockValue方法,证明了可以把父类绑定到一个继承自该父类的子类。通过派生类绑定,当我们请求父类的时候,Ninject自动帮我们创建一个对应的子类的实例,并将其返回。由于抽象类不能被实例化,所以派生类绑定在使用抽象类的时候非常有用。

    5.条件绑定

    当一个接口有多个实现或一个类有多个子类的时候,我们可以通过条件绑定来指定使用哪一个实现或子类。为了演示,我们给IValueCalculator接口再添加一个实现,如下:

    复制代码
    public class IterativeValueCalculator : IValueCalculator { 
     
        public decimal ValueProducts(params Product[] products) { 
            decimal totalValue = 0; 
            foreach (Product p in products) { 
                totalValue += p.Price; 
            } 
            return totalValue; 
        } 
    }

    IValueCalculator接口现在有两个实现:IterativeValueCalculator和LinqValueCalculator。我们可以指定,如果是把该接口的实现注入到LimitShoppingCart类,那么就用IterativeValueCalculator,其他情况都用LinqValueCalculator。如下所示:

    复制代码
    public static void Main() {
        IKernel ninjectKernel = new StandardKernel();
    
        ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator>();
        ninjectKernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>()
            .WithPropertyValue("DiscountSize", 5M);
        //派生类绑定
        ninjectKernel.Bind<ShoppingCart>().To<LimitShoppingCart>()
            .WithPropertyValue("ItemLimit", 3M);
        //条件绑定
        ninjectKernel.Bind<IValueCalculator>()
            .To<IterativeValueCalculator>().WhenInjectedInto<LimitShoppingCart>();
    
        ShoppingCart cart = ninjectKernel.Get<ShoppingCart>();
        Console.WriteLine("Total: {0:c}", cart.CalculateStockValue());
        Console.ReadKey();
    }

    运行结果:

    运行结果是6.4,说明没有打折,即调用的是计算方法是IterativeValueCalculator的ValueProducts方法。可见,Ninject会查找最匹配的绑定,如果没有找到条件绑定,则使用默认绑定。在条件绑定中,除了WhenInjectedInto方法,还有When和WhenClassHas等方法,朋友们可以在使用的时候再慢慢研究。

    在ASP.NET MVC中使用Ninject

    本文用控制台应用程序演示了Ninject的使用,但要把Ninject集成到ASP.NET MVC中还是有点复杂的。首先要做的事就是创建一个继承System.Web.Mvc.DefaultControllerFactory的类,MVC默认使用这个类来创建Controller类的实例(后续博文会专门讲这个)。代码如下:

     NinjectControllerFactory

    现在暂时不解释这段代码,大家都看懂就看,看不懂就过,只要知道在ASP.NET MVC中使用Ninject要做这么一件事就行。

    添加完这个类后,还要做一件事,就是在MVC框架中注册这个类。一般我们在Global.asax文件中的Application_Start方法中进行注册,如下所示:

    复制代码
    protected void Application_Start() {
        AreaRegistration.RegisterAllAreas();
    
        WebApiConfig.Register(GlobalConfiguration.Configuration);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
    
        ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
    }

    注册后,MVC框架就会用NinjectControllerFactory类去获取Cotroller类的实例。在后续博文中会具体演示如何在ASP.NET MVC中使用Ninject,这里就不具体演示了,大家知道需要做这么两件事就行。

    虽然我们前面花了很大功夫来学习Ninject就是为了在MVC中使用这样一个NinjectControllerFactory类,但是了解Ninject如何工作是非常有必要的。理解好了一种DI容器,可以使得开发和测试更简单、更高效。

    参考:

    《Pro ASP.NET MVC 3 Framework》

     
     

    介绍一款网站前台图片滚动利器之"switchable"

     

    一、简单介绍:jQuery.Switchable是一款整合了Tabs、Slide、Scrollable等常见UI组件的jQuery插件,在这里,简答说说他的Slide。像Tabs,在Jquery-UI和Jquery-EasyUI中也有Tabs。在我的博客中也使用到了,请看完整“信息发布系统”系列,下面就是Jquery-EasyUI的Tabs。

                           

                                                            下面 这些是使用Switchable实现的效果:

                                                  

               1、今天就说说前台图片滚动的“Slide”,大家应该并不陌生,因为大部分网站都用到了这种效果。如下:官方地址下载

                                    

              2、今天就带着大家完成如上图的功能,包括添加完图片后,自动添加数字(1、2、3、4),导向相应的页面。              

                  (1)项目结构如下:

                                                                   

                     (2)、首先得在Scripts文件夹下加入jquery.switchable[all].min.js。如果没有的话,请留言。

                     (3)、在Home/Index中View中:

                           1、$("#trigger1").switchable("#slide1 > div > img", { effect: "scroll", speed: .2 }).autoplay(2).carousel();

                                   属性的简单介绍:

                                        effect:切换效果。内置效果有:default":最简单的显/隐效果"还有“fade”,也可以自己手动添加效果

                                        speed:动画的速度,单位是秒。默认值即700毫秒。

                                        delay:触发延迟,单位是秒。默认值即100毫秒。

                                        easing:"swing":动画的效果。

                                        circular:false,是否循环。

                                        vertical:是否纵向切换。

                                    方法的简答介绍:

                                        autoplay:是否自动播放。

                                        interval:自动播放间隔,单位是秒。默认值即3000毫秒。当插件的参数是数字时,就是配置这个属性。例如:
                                                      $("trigger-container-selector").switchable("panel-selector").autoplay(1.8);

                                        autopause:通过该属性可以控制鼠标 mouseenter 进 panel 区域是否暂停动画。mouseleave 后将自动恢复动画。                                     

                            Home/View

                             2、因为有一个<input type="file" />上传文件。所以得添加这种格式:{enctype = "multipart/form-data" },简单解释一下:是设置表单的MIME编码。默认情况,这个编码格式是application/x-www-form-urlencoded,不能用于文件上传;只有使用了multipart/form-data,才能完整的传递文件数据,进行下面的操作。

                             3、 @Html.Raw(Server.HtmlDecode(Html.Action("PictureRoll", "Extensions").ToHtmlString())),图片这部分是在Extensions中添加了一个Action,渲染出来图片。下面看看Extensions/PictureRoll中的内容,使用的是:XElement,进行XML的操作。读取XML的数据,不介绍它的使用了,都能看懂。使用System.Xml.Linq.最后ViewBag.picture=picture。把图片渲染出来。前台进行接收。

                             PictureRoll Action
                             PictureRoll View

                             4、还得添加一个CSS。改变图片和1234链接的样式。

                     SwichTab Css

                             5、现在运行代码:出现之前介绍的效果了吧。

                                        

                     (4)添加:上传图片的功能。

                              1、遍历上传上来的文件,使用HttpPostedFileBase进行接收。注意:参数 photo得和前台的name得一致。

                              2、判断上传文件的类型,防止一些恶意用户,上传木马。

                              3、item.SaveAs(Server.MapPath("~/images/" + Path.GetFileName(item.FileName)));进行保存文件。

                              4、接下来又是XElement的使用,SetElementValue,为节点进行赋值。最后也得保存!Save();

                              5、出现上图效果!

                             AddImage Action

       二、总结:一个图片焦点轮换的效果的实现,如果对您有一点点帮助的话,右下角”推荐“支持一下,让更多的朋友来交流学习!如有问题,请留言! 今天下午有中国男篮亚锦赛,得赶快回家看了。支持一下中国男篮吧。

    作者:鑫中有志
    出处:http://www.cnblogs.com/tianxinbest/
    关于作者:专注于MVC、NHibernate、EF的学习。如有问题或建议,请多多赐教!
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,如有问题,可以通tianxinbest@163.com 联系我,非常感谢。
     
     
    标签: MVC
  • 相关阅读:
    Java程序设计:最大连续1的个数(LeetCode:485)
    LeetCode刷题经验2
    LeetCode刷题经验
    Java程序设计:区域和检索—数组不可变(LeetCode:303)
    Java程序设计:在排序数组中查找元素的第一个和最后一个位置(LeetCode:34)
    解决安装python3后yum不能使用情况
    使用shell做数据库备份的时候,遇到了以下问题,原因未知
    使用windows系统编写shell代码,在linux执行后的报错
    Apache URL重写规则
    PHP中的环境变量$_ENV, $_SERVER 及getenv
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3244785.html
Copyright © 2020-2023  润新知