Skip to content

Instantly share code, notes, and snippets.

@demofan
Forked from dinowang/Order.cs
Last active January 8, 2016 10:02
Show Gist options
  • Save demofan/d73786a6625064681dfe to your computer and use it in GitHub Desktop.
Save demofan/d73786a6625064681dfe to your computer and use it in GitHub Desktop.
ASP.NET MVC QueryOption<T> implementation. depend on PagedList, PagedList.Mvc.
public enum Order
{
Ascending,
Descending
}
using PagedList.Mvc;
public class PagingOptions
{
public static PagedListRenderOptions Standard
{
get
{
var options = new PagedListRenderOptions
{
DisplayLinkToFirstPage = PagedListDisplayMode.Always,
DisplayLinkToPreviousPage = PagedListDisplayMode.Always,
LinkToPreviousPageFormat = "上一頁",
DisplayLinkToNextPage = PagedListDisplayMode.Always,
LinkToNextPageFormat = "下一頁",
DisplayLinkToLastPage = PagedListDisplayMode.Always
};
return options;
}
}
}
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using PagedList;
public class QueryOption<T>
{
public string Keyword { get; set; }
public int Page { get; set; }
public int PageSize { get; set; }
public string Column { get; set; }
public Order Order { get; set; }
public IPagedList<T> Result { get; set; }
public QueryOption()
{
Page = 1;
PageSize = 20;
}
public void SetSource(IEnumerable<T> source)
{
SetSource(source.AsQueryable());
}
public void SetSource(IQueryable<T> source)
{
if (string.IsNullOrEmpty(Column))
{
Column = typeof (T).GetProperties().First().Name;
}
var param = Expression.Parameter(typeof (T));
Expression parent = param;
foreach (var column in Column.Split(new[] { '.' }))
{
parent = Expression.Property(parent, column);
}
dynamic keySelector = Expression.Lambda(parent, param);
IOrderedQueryable<T> query = null;
if (Order == Order.Ascending)
{
query = Queryable.OrderBy(source, keySelector);
}
else
{
query = Queryable.OrderByDescending(source, keySelector);
}
Result = query.ToPagedList(Page, PageSize);
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Web.Mvc;
using System.Web.Routing;
using Workshop.ViewModels;
public static class QueryOptionHtmlExtensions
{
public static MvcHtmlString SortableFor<TEntity, TProperty>(
this HtmlHelper<QueryOption<TEntity>> htmlHelper,
Expression<Func<TEntity, TProperty>> expression,
string tagName,
string text = null,
object htmlAttributes = null)
{
var column = GetFullPropertyName(expression);
var tag = new TagBuilder(tagName);
tag.AddCssClass("sortable");
tag.Attributes["data-column"] = column;
if (text == null)
{
var memberAccess = expression.Body as MemberExpression;
text = memberAccess.Member.Name;
var displayAttr = (DisplayNameAttribute)memberAccess.Member.GetCustomAttributes(typeof (DisplayNameAttribute), true).FirstOrDefault();
if (displayAttr != null)
{
text = displayAttr.DisplayName;
}
}
tag.SetInnerText(text);
var queryOption = htmlHelper.ViewData.Model;
if (queryOption.Column == column)
{
var order = queryOption.Order;
tag.Attributes["data-direction"] = order.ToString();
var icon = new TagBuilder("i");
icon.AddCssClass("icon");
switch (order)
{
case Order.Ascending:
icon.AddCssClass("icon-chevron-up");
break;
case Order.Descending:
icon.AddCssClass("icon-chevron-down");
break;
}
tag.InnerHtml += " " + icon.ToString();
}
if (htmlAttributes != null)
{
var attributes = new RouteValueDictionary(htmlAttributes);
tag.MergeAttributes(attributes);
}
return new MvcHtmlString(tag.ToString());
}
// code adjusted to prevent horizontal overflow
static string GetFullPropertyName<TEntity, TProperty>(
Expression<Func<TEntity, TProperty>> expression)
{
MemberExpression memberExpression;
if (!TryFindMemberExpression(expression.Body, out memberExpression))
{
return string.Empty;
}
var memberNames = new Stack<string>();
do
{
memberNames.Push(memberExpression.Member.Name);
}
while (TryFindMemberExpression(memberExpression.Expression, out memberExpression));
return string.Join(".", memberNames.ToArray());
}
// code adjusted to prevent horizontal overflow
private static bool TryFindMemberExpression(
Expression expression,
out MemberExpression memberExpression)
{
memberExpression = expression as MemberExpression;
if (memberExpression != null)
{
// heyo! that was easy enough
return true;
}
// if the compiler created an automatic conversion,
// it'll look something like...
// obj => Convert(obj.Property) [e.g., int -> object]
// OR:
// obj => ConvertChecked(obj.Property) [e.g., int -> long]
// ...which are the cases checked in IsConversion
if (IsConversion(expression) && expression is UnaryExpression)
{
memberExpression = ((UnaryExpression)expression).Operand as MemberExpression;
if (memberExpression != null)
{
return true;
}
}
return false;
}
private static bool IsConversion(Expression exp)
{
return (
exp.NodeType == ExpressionType.Convert ||
exp.NodeType == ExpressionType.ConvertChecked
);
}
}
;
$(function () {
// jQuery Selector (DOM 關聯) 需根據不同的 View layout 調整
$("#wrap .form-search")
.each(function (i, el) {
var $form = $(this),
$submit = $form.find("[type=submit]"),
$toolbar = $form.parent(".btn-toolbar"),
$table = $toolbar.next().find("table:first"),
$sortable = $table.find("tr:first").find(".sortable"),
$pager = $toolbar.next().find(".pagination");
$sortable.click(function () {
var $this = $(this),
$icon = $this.find("i.icon"),
column = $this.data("column"),
order = $this.data("direction");
switch (order) {
case "Ascending":
nextSort(column, "Descending");
$icon.attr("class", "icon icon-chevron-down");
break;
case "Descending":
nextSort(null, null);
$icon.removeClass("icon");
break;
default:
nextSort(column, "Ascending");
$this.append(" <i class='icon icon-chevron-up' />");
break;
}
return false;
});
var nextSort = function (column, order) {
$form.find("[name=Column]").val(column);
$form.find("[name=Order]").val(order);
$form.trigger("submit");
};
$submit.click(function () {
$form.find("[name=Page]").val(1);
$form.trigger("submit");
});
$pager.on("click", "a", function (evt) {
var $page = $form.find("[name=Page]"),
page = parseInt(this.href.match(/(\d+)$/)[1]);
if (isNaN(page))
return false;
$page.val(page);
$form.trigger("submit");
return false;
});
});
});
module demo {
enum Order { Ascending , Descending }
export class sortable {
form: JQuery;
submit: JQuery;
toolbar: JQuery;
table: JQuery;
sortable: JQuery;
pager: JQuery;
pageIndex: JQuery;
constructor(form: JQuery) {
this.form = form;
this.submit = this.form.find("[type=submit]");
this.toolbar = this.form.parent();
this.table = this.toolbar.next().find("table:first");
this.sortable = this.table.find("tr:first").find(".sortable");
this.pager = this.toolbar.next().find(".pagination");
this.pageIndex = this.form.find("[name=page]");
this.submitBefore();
this.paginationClick();
this.sorting();
}
/**
* 排序事件綁定
*/
sorting() {
var self = this;
function nextSort(column: string, order: string) {
self.form.find("[name=column]").val(column);
self.form.find("[name=order]").val(order);
self.submit.click();
};
this.sortable.click(function () {
var current = $(this),
icon = current.find("i"),
column: string = current.data("column"),
order: Order = Order[<string>current.data("direction")];
switch (order) {
case Order.Ascending:
nextSort(column, Order[Order.Descending]);
icon.attr("class","desc");
break;
case Order.Descending:
nextSort(column, Order[Order.Ascending]);
icon.attr("class", "asc");
break;
}
return false;
});
}
/**
* 按搜尋之前的處理
* @returns {}
*/
submitBefore() {
var self = this;
self.submit.click(function () {
self.pageIndex.val(1);
self.submit.submit();
});
}
/**
* 綁定分頁列按下的行為
*/
paginationClick() {
var self = this;
self.pager.on("click", "a", function (e) {
e.preventDefault();
var page = parseInt($.url().param("page"));
if (isNaN(page))
return false;
self.pageIndex.val(page);
self.submit.click();
return false;
});
}
}
}
$(function () {
var sortable = new demo.sortable($(".container"));
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment