اشتراک‌ها
بالاخره پیشنهاد یک المان <select> قابل سفارشی سازی با CSS
Request for developer feedback: customizable select

Styling form controls like the <select> element has been reported as a top developer pain point for years, and we've been working on a solution. While this work is complex and has taken a long time to get right, we're getting very close to landing this feature. A customizable version of the select element is officially in Stage 2 in the WHATWG, with strong cross-browser interest and a prototype for you to test out from Chrome Canary 130.
بالاخره پیشنهاد یک المان <select> قابل سفارشی سازی با CSS
بازخوردهای دوره
مدیریت نگاشت ConnectionIdها در SignalR به کاربران واقعی سیستم
واقعا سپاسگذارم . ولی مشکلم و حل نمیکنه این موارد.
یکم گیج و سردرگم شدم.
ببینید من یه هاب ایجاد کردم به شکل زیر
 public class User
    {
        public string UserName { get; set; }
        public bool IsRole { get; set; }
        public HashSet<string> ConnectionIds { get; set; }
    }

    [Authorize]
    [HubName("userActivityHub")]
    public class UserActivityHub : Hub
    {
        private static readonly ConcurrentDictionary<string, User> Users = new ConcurrentDictionary<string, User>();
     

        public void AdminJoin()
        {
            Groups.Add(Context.ConnectionId, "admins");
        }

        public void Join()
        {
            var userName = Context.User.Identity.Name;
            var connectionId = Context.ConnectionId;
            var isAdmin = Context.User.IsInRole("Admin");

            var user = Users.GetOrAdd(userName, _ => new User
            {
                UserName = userName,
                IsRole = isAdmin,
                ConnectionIds = new HashSet<string>()
            });

            if (user.IsRole == true)
            {
                Groups.Add(user.ConnectionIds.ToString(), "admins");
            }
            else
            {
                lock (user.ConnectionIds)
                {
                    user.ConnectionIds.Add(connectionId);
                }
                Clients.Group("admins").showUserCount(Users.Count(a => a.Value.IsRole != true));
            }

        }

        public void GetUserCount()
        {
            Clients.Group("admins").showUserCount(Users.Count(a => a.Value.IsRole != true));
        }

        public override System.Threading.Tasks.Task OnDisconnected(bool stopCalled)
        {
            if (stopCalled)
            {
                var userName = Context.User.Identity.Name;
                var connectionId = Context.ConnectionId;

                User user;
                Users.TryGetValue(userName, out user);
                if (user != null)
                {
                    lock (user.ConnectionIds)
                    {
                        user.ConnectionIds.RemoveWhere(cid => cid.Equals(connectionId));
                        if (!user.ConnectionIds.Any())
                        {
                            User removeUser;
                            Users.TryRemove(userName, out removeUser);
                        }
                    }
                }
                return Clients.Group("admins").showUserCount(Users.Count(a => a.Value.IsRole != true));
            }
            else
            {
                return base.OnDisconnected(false);
            }
        }
    }

اینک کد ویو Index
 <script type="text/javascript">
        var userHub = $.connection.userActivityHub;
        $.connection.hub.logging = true;
        $.connection.hub.start().done(function() {
            userHub.server.join();
        });

        $(function() {
            window.onbeforeunload = function() {
                $.connection.hub.stop();
            };
        });
    </script>

اینم ویو Index مربوط به Area Admin 
    <script type="text/javascript">
        var userHub = $.connection.userActivityHub;
        userHub.client.showUserCount = function (message) {
            $('#userOnlineCount').html(message);
        };
        $.connection.hub.start().done(function() {
            userHub.server.adminJoin().done(function() {
                userHub.server.getUserCount();
            });
        });
    </script>
وقتی کاربر لاگین میکنه یه کانتر تو ویو ادمین دارم که بهش اضافه میشه و برای لاگین کاربران درست کار میکنه.
مشکلم اینه که وقتی کاربر دکمه خروج و میزنه یا مرورگر رو میبنده رویداد OnDisconnected اجرا نمیشه! در نتیجه کانتر موجود در ویو ادمین هم کم نمیشه.
اینم کد دکمه خروج که البته تو یه پارشال ویو وجود داره
<script>
    $('a.btn.btn-danger.btn-block').click(function(e) {
        e.preventDefault();
        $('#logoutForm').submit();
        $.connection.userActivityHub.connection.stop();
    });
    $(function() {
        window.onbeforeunload = function(e) {
            $.connection.hub.stop();
        };
    });

</script>
نمیدونم مشکل کجاست! آیا باید برای دکمه خروج هم یه متد تو هاب ایجاد کنم یا همون Ondisconnected کافیه!
واقعا ممنون از زحمات شما.
نظرات مطالب
استفاده از چند فرم در کنار هم در ASP.NET MVC
ممنون از اینکه پاسخ دادید.

خیر از Httpget و NonAction استفاده نکردم ولی از ChildActionOnly بالای اکشنها استفاده کردم
در کنترلر به صورت زیر اکشنها رو نوشتم:
        [Authorize(Roles = "Administrator")]
        public ActionResult AddUserRole()
        {
            return View();
        }
       
         //////////////////////////////////////////////////////////

        //اضافه کردن نقش////////////
        [Authorize(Roles = "Administrator")]
        [HttpGet]
        [ChildActionOnly]
        
        public ActionResult AddRole()
        {
          
            return PartialView("PartialRole");
        }

        [ChildActionOnly]
        [HttpPost]
        //[ValidateAntiForgeryToken]
        public ActionResult AddRole(tblRole rl, string submitValue)
        {
            if (submitValue == "ذخیره نقش")
            {
                //تبدیل تاریخ میلادی به شمسی
                rl.RoleDateCreate = Utility.ToPersianDate(DateTime.Now);

                UserRepository user = new UserRepository();
                //برای وارد کردن خودکار شناسه کاربر وارد شده
                rl.UserIDCreate_FK = Convert.ToInt64(user.FindUserID(User.Identity.Name));

                if (ModelState.IsValid)
                {
                    RoleRepository acr = new RoleRepository();
                    var model = acr.Add(rl);
                    ViewBag.Message = "نقش با موفقیت ثبت شد";
                    ModelState.Clear();
                }
            }
            else
            {
                ModelState.Clear();
            }
            return PartialView("PartialRole", rl);

        }
        //////////////////////////////////////////////////////////

        //اضافه کردن کاربر//////////
        [Authorize(Roles = "Administrator")]
        [HttpGet]
        [ChildActionOnly]
       
        public ActionResult AddUser()
        {
            return PartialView("PartialUser");
        }
        
        [ChildActionOnly]
        [HttpPost]
     
        public ActionResult AddUser(tblUser rl, string submitValue)
        {
            if (submitValue == "ثبت نام")
            {
                if (ur.ExistUserName(rl.UserName))//برای اینکه نام کاربری تکراری نباشد
                {
                    ViewBag.Warning = "این نام کاربری تکراری میباشد";
                 
                }
                else
                {
                   
                    //برای وارد کردن خودکار شناسه کاربر وارد شده
                    rl.UserIDCreate = Convert.ToInt64(ur.FindUserID(User.Identity.Name));
                   
                    //تبدیل میلادی به شمسی
                    rl.UserDateCreate = Utility.ToPersianDate(DateTime.Now);

                    if (ModelState.IsValid)
                    {
                        UserRepository acr = new UserRepository();
                        var model = acr.Add(rl);
                        ViewBag.Message = "کاربر با موفقیت ثبت شد";
                        ModelState.Clear();
                    }
                }
            }
            else
            {
                ModelState.Clear();
            }
            return PartialView("PartialUser", rl);

        }

        //////////////////////////////////////////////////////////

        // دادن نقش به کاربر///////

        [ChildActionOnly]
        
        [HttpGet]
        [Authorize(Roles = "Administrator")]
        public ActionResult RoleOfUser()
        {
            var blUser = new UserRepository();
            var blRole = new RoleRepository();
            var model = new RoleUserViewModel();

            model.username = blUser.Select().ToList();
            model.rolename = blRole.Select().ToList();
           
            return PartialView("PartialRoleOfUser", model);
        }

        [ChildActionOnly]
        [HttpPost]      
        public ActionResult RoleOfUser(tblRoleUser rl, string submitValue)
        {
            if (submitValue == "ذخیره نقش کاربر")
            {
                //برای وارد کردن خودکار شناسه کاربر وارد شده
                rl.UserIDCreate_FK = Convert.ToInt64(ur.FindUserID(User.Identity.Name));

                //تبدیل میلادی به شمسی
                rl.RoleUserDateCreate = Utility.ToPersianDate(DateTime.Now);

                if (ModelState.IsValid)
                {
                    RoleUserRepository acr = new RoleUserRepository();
                    var model = acr.Add(rl);
                    ViewBag.Message = "نقش کاربر با موفقیت ثبت شد";

                    ModelState.Clear();
                }
            }
            else
            {
                ModelState.Clear();
            }
            return PartialView("PartialRoleOfUser", rl);

        }
در پارشیال ویو Role
@model MeterControl.ViewModels.RoleUserViewModel
<script src="~/Scripts/jquery-1.8.2.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>

@using (Ajax.BeginForm("AddUserRole", "Home", new AjaxOptions { HttpMethod = "Post", Url = "/Home/AddUserRole" })){ 
    <fieldset>
        <legend>tblRole</legend>

        

        <div>
            @Html.LabelFor(model => model.role.RoleName)
        </div>
        <div>
            @Html.EditorFor(model => model.role.RoleName)
            @Html.ValidationMessageFor(model => model.role.RoleName)
        </div>

       

        @ViewBag.Message
        <p>
            <input type="submit" value="ذخیره نقش" name="submitValue" />
        </p>
    </fieldset>
}

و در ویو کلی هم به صورت زیر اکشنها صدا زده شده اند:
@{
    ViewBag.Title = "AddUserRole";
}

<h2>AddUserRole</h2>
<div>
    <div>
        @Html.Action("AddRole", "Home")
    </div>
    <div>
        @Html.Action("AddUser", "Home")
        @Html.Partial("PartialUser")
    </div>
    <div>
        @Html.Action("RoleOfUser","Home")
    </div>
</div>

به خط زیر خطا میدهد
@Html.Action("AddRole", "Home")
نظرات مطالب
ASP.NET MVC #13
با سلام.
در حالت زیر در هنگام submit همواره صفحه رفرش می‌شود و بعد از رفرش صفحه خطاها را نشان می‌دهد و
اعتبارسنجی سمت کلاینت کار نمی‌کند؟
@model Models.Account
@{
    Layout = null;
    ViewBag.Title = "ورود به سیستم";
}

<script src="@Url.Content("~/Scripts/jquery-1.7.1.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

@using (Html.BeginForm())
{      
     @Html.ValidationSummary(true)
    <fieldset>
        <legend>Login</legend>
        <table style="font-size: 8pt">
            <tr>
                <td style="width: 100px; text-align: left">نام کاربری :</td>
                <td>@Html.EditorFor(model => model.Username)</td>
            </tr>
            <tr>
                <td></td>
                <td style="color: red">@Html.ValidationMessageFor(model => model.Username)</td>
            </tr>
            <tr>
                <td style="width: 100px; text-align: left">کلمه عبور :</td>
                <td>@Html.EditorFor(model => model.Password)</td>
            </tr>
            <tr>
                <td></td>
                <td style="color: red">@Html.ValidationMessageFor(model => model.Password)</td>
            </tr>
            
            <tr>
                <td></td>
                <td>
                    <input type="submit" value="ورود به سیستم" /></td>
            </tr>
        </table>
    </fieldset>
}
public class Account
    {
        [Required(ErrorMessage = "نام کاربری باید وارد شود.")]
        [StringLength(20)]
        public string Username { get; set; }
        [Required(ErrorMessage = "کلمه عبور باید وارد شود.")]
        [DataType(DataType.Password)]
        public string Password { get; set; }
    }
[HttpGet]
        public ActionResult LogOn(string returnUrl)
        {
            if (User.Identity.IsAuthenticated) //remember me
            {
                if (shouldRedirect(returnUrl))
                {
                    return Redirect(returnUrl);
                }
                return Redirect(FormsAuthentication.DefaultUrl);
            }
            return View(); // show the login page
        }
 [HttpPost]
        public ActionResult LogOn(Account loginInfo, string returnUrl)
        {
            if (this.ModelState.IsValid)
            {
                List<User> users = _userService.GetUser(loginInfo.Username, loginInfo.Password);
                if (users != null && users.Count == 1)
                {
                    FormsAuthentication.SetAuthCookie(loginInfo.Username,false);// loginInfo.RememberMe);
                    //-- کاربر برنامه ریزی
                    if (users.First().UserType_Id == 1)
                    {
                        return RedirectToAction("Index", "Programming", new { u = loginInfo.Username });
                    }
                    else if (users.First().UserType_Id == 2)
                    {

                    }
                    else if (users.First().UserType_Id == 3)
                    {

                    }
                    else if (users.First().UserType_Id == 4)
                    {

                    }
                }
            }
            this.ModelState.AddModelError("", "نام کاربری یا کلمه عبور اشتباه وارد شده اند.");
            ViewBag.Error = "";
            return View(loginInfo);
        }
 <appSettings>
    <add key="webpages:Version" value="2.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="PreserveLoginUrl" value="true" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
  </appSettings>
با تشکر.
مطالب
نمایش بلادرنگ اعلامی به تمام کاربران در هنگام درج یک رکورد جدید
در ادامه می‌خواهیم اعلام عمومی نمایش افزوده شدن یک پیام جدید را بعد از ثبت رکوردی جدید، به تمامی کاربران متصل به سیستم ارسال کنیم. پیش نیاز مطلب جاری موارد زیر می‌باشند:
namespace ShowAlertSignalR.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public float Price { get; set; }
        public Category Category { get; set; }

    }

    public enum Category
    {
        [Display(Name = "دسته بندی اول")]
        Cat1,
        [Display(Name = "دسته بندی دوم")]
        Cat2,
        [Display(Name = "دسته بندی سوم")]
        Cat3
    }
}
در اینجا مدل ما شامل عنوان، توضیح، قیمت و یک enum برای دسته‌بندی یک محصول ساده می‌باشد.
کلاس context نیز به صورت زیر می‌باشد:
namespace ShowAlertSignalR.Models
{
    public class ProductDbContext : DbContext
    {
        public ProductDbContext() : base("productSample")
        {
            Database.Log = sql => Debug.Write(sql);
        }
        public DbSet<Product> Products { get; set; }
    }
}
همانطور که در ابتدا عنوان شد، می‌خواهیم بعد از ثبت یک رکورد جدید، پیامی عمومی به تمامی کاربران متصل به سایت نمایش داده شود. در کد زیر اکشن متد Create را مشاهده می‌کنید: 
[HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create(Product product)
        {
            if (ModelState.IsValid)
            {
                db.Products.Add(product);
                db.SaveChanges();
                return RedirectToAction("Index");
            }

            return View(product);
        }
می‌توانیم از ViewBag برای اینکار استفاده کنیم؛ به طوریکه یک پارامتر از نوع bool برای متد Index تعریف کرده و سپس مقدار آن را درون این شیء ViewBag انتقال دهیم، این متغییر بیانگر حالتی است که آیا اطلاعات جدیدی برای نمایش وجود دارد یا خیر؟ بنابراین اکشن متد Index را به اینصورت تعریف می‌کنیم:
public ActionResult Index(bool notifyUsers = false)
        {
            ViewBag.NotifyUsers = notifyUsers;
            return View(db.Products.ToList());
        }
در اینجا مقدار پیش‌فرض این متغیر، false می‌باشد. یعنی اطلاعات جدیدی برای نمایش موجود نمی‌باشد. در نتیجه اکشن متد Create را به صورتی تغییر می‌دهیم که بعد از درج رکورد موردنظر و هدایت کاربر به صفحه‌ی Index، مقدار این متغییر به true تنظیم شود:
[HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create(Product product)
        {
            if (ModelState.IsValid)
            {
                db.Products.Add(product);
                db.SaveChanges();
                return RedirectToAction("Index", new { notifyUsers = true });
            }

            return View(product);
        }
قدم بعدی ایجاد یک هاب SignalR می‌باشد:
namespace ShowAlertSignalR.Hubs
{
    public class NotificationHub : Hub
    {
        public void SendNotification()
        {
            Clients.Others.ShowNotification();
        }
    }
}
در ادامه کدهای سمت کلاینت را برای هاب فوق، داخل ویوی Index اضافه می‌کنیم:
@section scripts
{
    
    <script src="~/Scripts/jquery.signalR-2.0.2.min.js"></script>
    <script src="~/signalr/hubs"></script>
    <script>

        var notify = $.connection.notificationHub;
        notify.client.showNotification = function() {
            $('#result').append("<div class='alert alert-info alert-dismissable'>" +
                "<button type='button' class='close' data-dismiss='alert' aria-hidden='true'>&times;</button>" +
            "رکورد جدیدی هم اکنون ثبت گردید، برای مشاهده آن صفحه را بروزرسانی کنید" + "</div>");
        };
        $.connection.hub.start().done(function() {
            @{
                if (ViewBag.NotifyUsers)
                {
                    <text>notify.server.sendNotification();</text>
                }
            }
        });
    </script>
}
همانطور که در کدهای فوق مشاهده می‌کنید، بعد از اینکه اتصال با موفقیت برقرار شد (درون متد done) شرط چک کردن متغییر NotifyUsers را بررسی کرده‌ایم. یعنی در این حالت اگر مقدار آن true بود، متد درون هاب را فراخوانی کرده‌ایم. در نهایت پیام به یک div با آی‌دی result اضافه شده است.
لازم به ذکر است برای حالت‌های حذف و به‌روزرسانی نیز روال کار به همین صورت می‌باشد.
سورس مثال جاری : ShowAlertSignalR.zip
مطالب
توسعه سرویس‌های Angular به روش OOP
یک نکته‌ای که در توسعه سیستم‌ها و نرم افزار‌ها تاکید فراوانی به آن می‌شود استفاده مجدد از کد‌های نوشته شده قبلی است. یعنی تا جای ممکن باید ساختار پروژه به گونه‌ای نوشته شود که از تکرار کد‌ها در جای جای پروژه جلوگیری شود. این مورد به خوبی در زبان‌های شیء‌گرا نظیر #C رعایت می‌شود اما در پروژه‌هایی که مبتنی بر Javascript هستند نظیر angular، باید با استفاده از خاصیت prototype جاوا اسکریپ این مورد را رعایت نمود. در  مقاله  Dr. Axel Rauschmayer،  قدم به قدم و به خوبی روش‌های وراثت در Javascript توضیح داده شده است.
در این پست با روش‌های وراثت در کنترلر‌های انگولاری آشنا شدید. این وراثت محدود به ارث بری scope‌ها می‌شود. اما یکی از بخش‌های بسیار مهم پروژه‌های انگولار نوشتن سرویس‌هایی با قابلیت توسعه مجدد در سایر بخش‌های پروژه می‌باشد. معادل آن، مفهوم Overriding در OOP است. با ذکر مثالی این مورد را با هم بررسی خواهیم کرد.
ابتدا یک سرویس به نام BaseService ایجاد کنید:
angular.module('myApp').service('BaseService', function() {

    var BaseService = function(title) {
        this.title = title;
    };

    BaseService.prototype.getMessage = function() {
        var self = this;
        return 'Hello ' + self.title;
    };

    return BaseService;
});
سرویس بالا دارای سازنده‌ای است که مقدار title باید در اختیار آن قرار گیرد. با استفاده از خاصیت prototype تابعی تعریف می‌کنیم که این تابع خروجی مورد نظر را برای ما تامین خواهد نمود.
حال اگر ماژول و کنترلری جهت نمایش خروجی به صورت زیر ایجاد کنیم:
var app= angular.module('myApp', []);


app.controller('myCtrl', function ($scope,BaseService) {

    var instance = new BaseService('Masoud');
    $scope.title = instance.getMessage();

});
با کدهای Html زیر:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" ng-app="myApp">
<head>
    <title></title>
</head>
    <body ng-controller="myCtrl">

        <div>
            {{title}}
        </div>

    </body>
<script src="Scripts/jquery-2.1.1.min.js"></script>
<script src="Scripts/angular.js"></script>
<script src="App/app.js"></script>
</html>
در نهایت خروجی به صورت زیر قابل مشاهده است:

تا اینجای کار روال معمول تعاریف سرویس در انگولار بوده است. اما قصد داریم سرویس جدیدی را ایجاد نمایم تا خروجی سرویس قبلی را اندکی تغییر دهد. به جای اینکه سرویس قبلی را تغییر دهیم یا بدتر از آن سرویس جدیدی بسازیم و کدهای قبلی را در آن کپی کنیم کافیست به صورت زیر عمل نماییم:

app.service('ExtService', function(BaseService) {

    var ExtService = function() {
        BaseService.apply(this, arguments);
    };

    ExtService.prototype = new BaseService();
    
    ExtService.prototype.getMessage = function() {
        var self = this;
        return BaseService.prototype.getMessage.apply(this, arguments) + ' From Ext Service';
        
    };

    return ExtService;
});
حال می‌توان کنترلر را به صورت زیر بازنویسی کرد.
app.controller('myCtrl', function ($scope,BaseService , ExtService) {

    var baseInstance = new BaseService('Masoud');
    var extInstance = new ExtService('Dotnettips');
    $scope.title = baseInstance.getMessage() + ' and ' + extInstance.getMessage();

});
در کنترلر بالا هر دو سرویس تزریق شده‌اند. خروجی سرویس دوم متن From Ext Service را نیز به همراه خواهد داشت. پس از اجرای برنامه خروجی زیر قابل مشاهده است:

مطالب
افزونه rtlColResizable جهت تغییر سایز ستون‌های جدول‌های راست به چپ HTML
برای تغییر سایز ستون‌های جداول HTML با استفاده از ماوس، افزونه‌های زیادی تدارک دیده شده است که از جمله مطرح‌ترین آن‌ها می‌توان به colResizable اشاره کرد. حتی اگر از DataGrid‌های مطرح وب هم استفاده کرده باشید، اکثر آن‌ها از تغییر سایز ستون‌ها توسط کاربر پشتیبانی می‌کنند. اما مشکل بزرگی که در همه‌ی آن‌ها مشترک است  این است که فقط از چیدمان‌های چپ به راست پشتیبانی می‌کنند و به محض اینکه شما ساختار راست به چپ را به جدول مورد نظر اعمال کنید، عملکرد تغییر سایز ستون‌ها با اشکال مواجه می‌شود.
به همین جهت برای تغییر سایز ستون‌ها توسط کاربر، افزونه ای برای jQuery تدارک دیدم که با جداول معمول HTML که همان table‌ها هستند سازگار است و به راحتی به هر جدولی می‌توان آن را اعمال کرد.
 
کدهای افزونه‌ی rtlColResizable:
(function ($, undefined) {

    $.fn.extend({
        'rtlColResizable': function (options) {
            var defaults = {
                //Default values for the plugin's options here
            };

            options = $.extend(defaults, options);

            var isMouseButtonPressed;
            var $resizingElement = undefined;
            var resizingElementStartWidth;
            var mouseCursorStartX;
            var isCursorInResizingPosition;

            var addResizingCursorStyle = function ($element) {
                $element.css({
                    'cursor': 'col-resize',
                    'user-select': 'none',
                    '-o-user-select': 'none',
                    '-ms-user-select': 'none',
                    '-moz-user-select': 'none',
                    '-khtml-user-select': 'none',
                    '-webkit-user-select': 'none',
                });
            };

            var removeResizingCursorStyle = function ($element) {
                $element.css({
                    'cursor': 'default',
                    'user-select': 'text',
                    '-o-user-select': 'text',
                    '-ms-user-select': 'text',
                    '-moz-user-select': 'text',
                    '-khtml-user-select': 'text',
                    '-webkit-user-select': 'text',
                });
            };

            var canResize = function (e) {
                return (e.offsetX || e.clientX - $(e.target).offset().left) < 10;
            };

            return this.each(function () {

                var opts = options;
                var tableColumns = $(this).find('th');

                tableColumns.filter(':not(:last-child)').mousedown(function (e) {

                    $resizingElement = $(this);
                    isMouseButtonPressed = true;
                    mouseCursorStartX = e.pageX;
                    resizingElementStartWidth = $resizingElement.width();

                });

                tableColumns.mousemove(function (e) {

                    if (canResize(e)) {
                        addResizingCursorStyle($(e.target));
                        isCursorInResizingPosition = true;

                    } else if (!isMouseButtonPressed) {
                        removeResizingCursorStyle($(e.target));
                        isCursorInResizingPosition = false;
                    }

                    if (isCursorInResizingPosition && isMouseButtonPressed) {

                        $resizingElement.width(resizingElementStartWidth + (mouseCursorStartX - e.pageX));
                    }

                });

                $(document).mouseup(function () {
                    if (isMouseButtonPressed) {
                        removeResizingCursorStyle($resizingElement);
                        isMouseButtonPressed = false;
                    }

                });

            });
        }
    });

})(jQuery);
 
نحوه‌ی استفاده:
این افزونه با تگ Table در HTML سازگار است. فقط تنها موردی که باید رعایت شود این است که در هنگام تعریف ساختار جدول، باید استاندارد تعریف ستون‌ها یا همان Header‌ها را رعایت کنید و از تگ‌های thead و th استفاده کنید.
نمونه ای از نحوه‌ی استفاده از آن را در کدهای زیر می‌بینید:
<!DOCTYPE html>
<html>
<head>
    <title>rtlColResizable Sample</title>
</head>
<body>
    <table id="myTable">
        <thead>
            <tr>
                <th>ستون1</th> <!-- نام ستون‌ها را حتما در این تگ باید تعریف کنید -->
                <th>ستون2</th>
                <th>ستون3</th>
            </tr>
        </thead>
        <tr>
            <td>داده مربوط به ستون 1</td>
            <td>داده مربوط به ستون 2 </td>
            <td>داده مربوط به ستون 3</td>
        </tr>
    </table>

    <script type="text/javascript" src="jquery-1.9.1.min.js"></script>
    <script type="text/javascript" src="jquery.rtlColResizable.js"></script>
    <script>

        $('table#myTable').rtlColResizable();

    </script>
</body>
</html>
     
دریافت نمونه کدی از نحوه‌ی استفاده از این افزونه
 
مطالب
توسعه کنترلر و مدل در F# MVC4
در پست قبلی با F# MVC4 Template آشنا شدید. در این پست به توسعه کنترلر و مدل در قالب مثال خواهم پرداخت. برای شروع ابتدا یک پروژه همانند مثال ذکر شده در پست قبلی ایجاد کنید. در پروژه #C ساخته شده که صرفا برای مدیریت View‌ها است یک View جدید به صورت زیر ایجاد نمایید:
@model IEnumerable<FsWeb.Models.Book>
<!DOCTYPE html>
<html>
<head>
    <title>@ViewBag.Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="stylesheet" 
        href="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.css" />
</head>
 
<body>
    <div data-role="page" data-theme="a" id="booksPage">
        <div data-role="header">
            <h1>Guitars</h1>
        </div>
        <div data-role="content">
        <ul data-role="listview" data-filter="true" data-inset="true"> 
            @foreach(var x in Model) {
                <li><a href="#">@x.Name</a></li>
            }
        </ul>
        </div>
    </div>
    <script src="http://code.jquery.com/jquery-1.6.4.min.js">
    </script>
    <script src="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.js">
    </script>

    <script>
        $(document).delegate("#bookPage", 'pageshow', function (event) {
            $("div:jqmData(role='content') > ul").listview('refresh');
        });
    </script>
</body>
</html>
از آنجا که هدف از این پست آشنایی با بخش #F پروژه‌های وب است در نتیجه نیازی به توضیح کد‌های بالا دیده نمی‌شود. 
برای ساخت کنترلر جدید، در پروژه #F ساخته شده یک Source File ایجاد نمایید و کد‌های زیر را در آن کپی نمایید:
namespace FsWeb.Controllers

open System.Web.Mvc
open FsWeb.Models

[<HandleError>]
type BooksController() =
    inherit Controller()
    member this.Index () =   
        seq { yield  Book(Name = "My F# Book")
              yield Book(Name = "My C# Book") }
        |> this.View
در کد‌های بالا ابتدا یک کنترلر به نام BookController ایجاد کردیم که از کلاس Controller ارث برده است(با استفاده از inherit).  سپس یک تابع به نام Index داریم(به عنوان Action مورد نظر در کنترلر) که آرایه ای از کتاب‌ها به عنوان پارامتر به تابع View می‌فرستد.( توسط اپراتور Pipe - <|). در نهایت دستور this.View  معادل فراخوانی اکشن ()View در پروژه‌های #C است که View متناظر با اکشن را فراخوانی می‌کند. همان طور که ملاحظه می‌نمایید بسیار شبیه به پیاده سازی #C است.
اما نکته ای که در مثال بالا وجود دارد این است که دو نمونه از نوع Book را برای ساخت seq وهله سازی می‌کند. در نتیجه باید Book Type را به عنوان مدل تعریف کنیم. به صورت زیر:
namespace FsWeb.Models
 type Book = { Id : Guid; Name : string }
البته در F# 3.0 امکانی فراهم شده است به نام Auto-Properties که شبیه تعریف خواص در #C است. در نتیجه می‌توان تعریف بالا را به صورت زیر نیز بازنویسی کرد:
namespace FsWeb.Models 
type Book() = member val Name = "" with get, set
Attribute‌ها مدل
اگر همچون پروژه‌های #C قصد دارید با استفاده از Attribute‌ها مدل خود را اعتبارسنجی نمایید می‌توانید به صورت زیر اقدام نمایید:
open System.ComponentModel.DataAnnotations
 type Book() = [<Required>] member val Name = "" with get, set
هم چنین می‌توان Attribute‌های مورد نظر برای مدل EntityFramework را نیز اعمال نمود(نظیر Key):
namespace FsWeb.Models 
open System 
open System.ComponentModel.DataAnnotations 
type Book() = [<Key>] member val Id = Guid.NewGuid() with get, set 
[<Required>] member val Name = "" with get, set

نکته: دستور open معادل با using در #C است.
در پست بعدی برای تکمیل مثال جاری، روش طراحی Repository با استفاده از EntityFramework بررسی خواهد شد.
مطالب
ویرایش قالب پیش فرض Add View در ASP.NET MVC برای سازگار سازی آن با Twitter bootstrap
همانطور که در مطلب «اعمال کلاس‌های ویژه اعتبارسنجی Twitter bootstrap به فرم‌های ASP.NET MVC» ملاحظه کردید، برای سازگار سازی یک فرم جدید ایجاد شده ASP.NET MVC با پیش فرض‌های Twitter bootstrap، حداقل 8 مرحله باید طی شود و ... چقدر خوب می‌شد اگر این‌کارها به صورت خودکار توسط VS.NET بجای قالب پیش فرض ایجاد فرم آن، تولید می‌شد. در ادامه قصد داریم این سفارشی سازی را انجام دهیم.


مراحل کلی سفارشی سازی قالب‌های Scaffolding پیش فرض ASP.NET MVC

قالب‌های Scaffolding پیش فرض ASP.NET در مسیر Microsoft Visual Studio X\Common7\IDE\ItemTemplates\CSharp\Web\MVC X\CodeTemplates قرار دارند. برای نمونه اگر بخواهیم پیش فرض‌های تولید فرم‌های MVC4 را تغییر دهیم، باید به پوشه MVC 4\CodeTemplates\AddView\CSHTML مراجعه و فایل Create.tt را ویرایش کنیم.
اینکار هرچند عملی است اما آنچنان جالب نیست؛ از این جهت که تاثیری کلی و سراسری خواهد داشت.
برای اعمال محلی این تغییرات فقط به یک پروژه خاص، تنها کافی است همین مسیر CodeTemplates\AddView\CSHTML به همراه تمام فایل‌های tt آن، در پوشه جاری پروژه مدنظر ما کپی شود. به این ترتیب ابتدا به این پوشه محلی مراجعه خواهد شد.
روش دوم کپی کردن این فایل‌ها، استفاده از بسته نیوگت ذیل است:
 PM> Install-Package Mvc4CodeTemplatesCSharp


سفارشی سازی فایل Create.tt پیش فرض ASP.NET MVC جهت سازگار سازی آن با Twitter bootstrap

در اینجا قصد داریم همان 8 مرحله مطلب «اعمال کلاس‌های ویژه اعتبارسنجی Twitter bootstrap به فرم‌های ASP.NET MVC» را به فایل Create.tt که اکنون در پوشه CodeTemplates\AddView\CSHTML\Create.tt ریشه پروژه جاری قرار دارد، اعمال کنیم.
الف) ابتدا نام این فایل را به CreateBootstrapForm.tt تغییر می‌دهیم. از این لحاظ که این نام جدید در drop down مرتبط با scaffold template صفحه Add view ظاهر خواهد شد. به علاوه نیازی نیست تا این فایل tt در همان لحظه اجرا شود، بنابراین به خواص آن در VS.NET مراجعه کرده و مقدار گزینه custom tool آن‌را خالی می‌کنیم (مانند سایر فایل‌های tt اضافه شده).
ب) قسمت ابتدایی فایل CreateBootstrapForm.tt را که همان کپی مطابق اصل فایل Create.tt است، به نحو ذیل تغییر می‌دهیم:
<#
    if (!mvcHost.IsContentPage) {
#>
<script src="~/Scripts/jquery-1.9.1.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>

<#
    }
}
#>
@using (Html.BeginForm()) {
    @Html.ValidationSummary(true, null, new { @class = "alert alert-error alert-block" })

    <fieldset class="form-horizontal">
        <legend><#= mvcHost.ViewDataType.Name #></legend>

<#
foreach (ModelProperty property in GetModelProperties(mvcHost.ViewDataType)) {
    if (!property.IsPrimaryKey && !property.IsReadOnly && property.Scaffold) {
#>
        <div class="control-group">
<#
        if (property.IsForeignKey) {
#>
            @Html.LabelFor(model => model.<#= property.Name #>, "<#= property.AssociationName #>",new {@class="control-label"})
<#
        } else {
#>
            @Html.LabelFor(model => model.<#= property.Name #>,new {@class="control-label"})
<#
        }
#>
        
           <div class="controls">
<#
        if (property.IsForeignKey) {
#>
            @Html.DropDownList("<#= property.Name #>", String.Empty)
<#
        } else {
#>
            @Html.EditorFor(model => model.<#= property.Name #>)
<#
        }
#>
            @Html.ValidationMessageFor(model => model.<#= property.Name #>,null,new{@class="help-inline"})
</div>
        </div>

<#
    }
}
#>
<div class="form-actions">
            <button type="submit" class="btn btn-primary">ارسال</button>
            <button class="btn">لغو</button>
          </div>
    </fieldset>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>
<#
if(mvcHost.IsContentPage && mvcHost.ReferenceScriptLibraries) {
#>

@section JavaScript {    

}
که حاصل آن به صورت ذیل قابل استفاده و دسترسی خواهد بود:


دریافت فایل CreateBootstrapForm.tt اصلاح شده:
همانطور که عنوان شد، برای استفاده از آن فقط کافی است آن‌را در مسیر CodeTemplates\AddView\CSHTML\CreateBootstrapForm.tt ریشه پروژه جاری خود کپی کنید.
مطالب
سازگار کردن GridView با افزونه‌های jQuery


افزونه‌ها/پلاگین‌های زیادی جهت کار با table استاندارد HTML وجود دارند و خروجی رندر شده‌ی یک ASP.Net GridView هم در نهایت یک جدول است. فرض کنید قصد داریم افزونه زیر را به GridView استاندارد ASP.Net اعمال کنیم.
jQuery quickSearch plug-in

ظاهرا بدون مشکل خاصی اعمال می‌گردد. برای مثال در هدر صفحه داریم: (شبیه به مثال موجود در سایت اصلی آن، جهت اعمال به GridView1)

<script src="jquery.min.js" type="text/javascript"></script>
<script src="jquery.quicksearch.js" type="text/javascript"></script>

<script type="text/javascript">
$(document).ready(function() {
//جستجو در جدول
$('table#<%=GridView1.ClientID %> tbody tr').quicksearch({
position: 'before',
attached: 'span#attachSearch',
labelText: 'جستجو',
isFieldset: true,
loaderText: ' ... ',
fixWidths: true
});
});
</script>

برای اعمال بر:
(در اینجا محل قرارگیری تکست باکس مربوط به جستجو، در span ایی با id مساوی attachSearch تنظیم شده است، می‌توانید از ID خود GridView هم استفاده کنید.)

<span id="attachSearch"></span>
<br />
<asp:GridView ID="GridView1" runat="server"></asp:GridView>

نکته:
1- همانطور که در مقاله مربوط به ClientID ذکر شد، هیچ الزامی ندارد که ID‌ مربوط به GridView‌ شما برای مثال دقیقا همان GridView1 جهت استفاده در سمت کلاینت باشد و بسته به container آن، این نام ترکیبی از ID شیء(های) در بر گیرنده و شیء مورد نظر می‌باشد. به همین جهت از GridView1.ClientID استفاده گردید تا اسکریپت ما با آن مشکلی نداشته باشد.

2- خصوصیات ظاهری افزونه فوق از سلکتور quicksearch فایل css شما دریافت می‌شوند. برای مثال:
.quicksearch
{
width:190px;
}

مشکل!
پس از هر بار جستجو، header مربوط به GridView محو می‌شود، اما نمونه موجود در سایت اصلی افزونه، این مشکل را ندارد. چرا؟!
GridView‌ مربوط به ASP.Net پس از رندر شدن، جدولی است که تگ‌های thead را ندارد. اگر به سورس صفحه سایت افزونه دقت نمائید، هدر جدول با تگ‌های thead محصور شده است اما GridView استاندارد ASP.Net به صورت پیش فرض این‌کار را انجام نمی‌دهد و خروجی آن چیزی شبیه به جدول زیر است: (هدر با th مشخص می‌شود و از تگ thead خبری نیست)

<table >
<tr >
<th scope="col">col1</th>
<th scope="col">col2</th>
<th scope="col">col3</th>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
</tr>
.
.
.

حداقل دو راه حل برای رفع این مشکل وجود دارد:
الف) راه حل سمت سرور
برای اضافه کردن thead باید کمی کد نویسی کرد. پس از اینکه گرید شما بایند شد، چند سطر زیر را اضافه کنید:

//سازگار با افزونه‌های جی کوئری
if (GridView1.Rows.Count > 0)
{
//This replaces <td> with <th> and adds the scope attribute
GridView1.UseAccessibleHeader = true;

//This will add the <thead> and <tbody> elements
GridView1.HeaderRow.TableSection = TableRowSection.TableHeader;

//This adds the <tfoot> element.
//Remove if you don't have a footer row
//GridView1.FooterRow.TableSection = TableRowSection.TableFooter;
}

ب)راه حل سمت کلاینت
سطر مربوط به جستجو را به صورت زیر هم می‌توان نوشت:

$('table#<%=GridView1.ClientID %> tr:has(td)').quicksearch({

در اینجا به دنبال هرچی tr که td دارد می‌گردیم. به صورت پیش فرض در tr مربوط به هدر گرید، ما th داریم و نه td ، بنابراین مشکل محو شدن هدر به این صورت حل خواهد شد.

نکته:
اگر می‌خواهید که ناحیه مربوط به جستجوی افزونه فوق در پرینت صفحه ظاهر نشود به css صفحه چند سطر زیر را اضافه کنید:

@media print
{
.quicksearch
{
display: none;
}
}


تمرین!
افزونه جی کوئری زیر را به یک ASP.Net GridView اعمال کنید:
table sorter