Skip to content

Instantly share code, notes, and snippets.

@dinowang
Last active November 13, 2019 03:08
Show Gist options
  • Save dinowang/6302408 to your computer and use it in GitHub Desktop.
Save dinowang/6302408 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;
});
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment