Thứ Ba, 7 tháng 7, 2015

C# – Tìm hiểu về Anonymous method

C# – Tìm hiểu về Anonymous method

CSharp_iconAnonymous method (tạm dịch là phương thức vô danh) là phương thức không có tên được khai báo với từ khóa delegate. Anonymous method cho phép bạn tạo ra các hành động cho một cho delegate với cách viết inline.
(Để nắm bắt được các vấn đề trình bày trong bài này, bạn cần có sẵn các kiến thức về delegate và generic method)

1. Giới thiệu

Giả sử bạn có một phương thức AddListItem() dùng để thêm một ListViewItem vào ListView. Bạn khai báo một delegete và tạo một thể hiện của nó để sử dụng:
delegate void AddListItemHandler(ListViewItem item);
private void AddListItem(ListViewItem item)
{
listView1.Items.Add(item);
}
…
AddListItemHandler addItemHandler = new AddListItemHandler(AddListItem);
Việc khai báo phương thức AddListItem nếu chỉ để sử dụng một lần cho delegate này thì có vẻ như không cần thiết. Thay vì viết như trên ta có thể sử dụng anonymous method để rút ngắn lại mã lệnh cần viết và hạn chế số lượng phương thức không cần thiết:
delegate void AddListItemHandler(ListViewItem item);
AddListItemHandler addItemHandler = delegate(ListViewItem item)
{
listView1.Items.Add(item);
};
Trong trường hợp viết mã lệnh xử lý cho các sự kiện của một đối tượng nào đó, bạn có thể sử dụng phương pháp này nếu như thấy thích hợp, chú ý các tham số phải khớp với delegate được khai báo:
button1.Click += delegate(object sender, EventArgs e)
{
MessageBox.Show(“Bạn vừa click button1″);
};

2.     Sử dụng Predicate<T>, Action<T> và Comparison<T>

Đây là ba delegate được xây dựng sẵn và dùng khá nhiều trong C# 2. Chúng được khai báo như sau:
public delegate bool Predicate<T> (T obj)
public delegate void Action<T> (T obj)
public delegate int Comparison<T> (T x, T y)
Predicate<T> được sử dụng để kiểm tra các giá trị có thỏa mãn một điều kiện nào đó không, như bạn cũng có thể nó trả về kiểu bool.
Action<T> sử dụng để thực hiện các hành động với đối tượng mà bạn truyền vào, và không trả về giá trị nào cả.
Comparison<T> dùng để so sánh hai đối tượng cùng kiểu, thường sử dụng trong các trường hợp sắp xếp.
  • Predicate<T>
Để thấy được công dụng của nó, chúng ta thử đi  khảo sát lớp Array với các phương thức tĩnh của nó.  Bạn có thể thấy là lớp Array này cung cấp khá nhiều phương thức generic. Ta lấy ví dụ về phương thức thường được sử dụng là FindAll dùng để tìm tất cả các phần tử thỏa mãn điều kiện, nó được định nghĩa như sau:
T[] Array.FindAll<T>(T[] array, Predicate<T> match)
Một phương thức generic có thể làm việc với bất kì kiểu nào, điều này rõ ràng là cần thiết vì nó làm việc với bất kì mảng thuộc kiểu dữ liệu nào mà bạn truyền vào. Ở đây ta quan tâm tới tham số thứ hai Predicate<T> match là một delegate xác định điều kiện cho phần tử cần tìm kiếm. Ví dụ ta có một mảng int[]:
int[] array ={ 10, 4, 3, 2, 8, 6, 5, 7, 9, 1 };
Nhiệm vụ của ta là lấy ra tất cả phần tử chẵn trong mảng. Bằng cách sử dụng anonymous method ta có thể viết như sau:
int[] evens = Array.FindAll<int>(array, delegate(int n)
{
return n % 2 == 0;
});
Theo cách thông thường bạn không cần viết <int> vào sau tên phương thức, trình biên dịch sẽ tự động xác định kiểu của tham số.
int[] evens = Array.FindAll(array, delegate(int n)
{
return n % 2 == 0;
});
  • Action<T>
Bây giờ ta đã có một mảng evens chứa các số chẵn trong mảng array. Công việc tiếp theo là in chúng ra màn hình để xem kết quả. Bạn nghĩ tới việc dùng cấu trúc lặp foreach để làm việc này. Rất tốt nhưng vì đang bàn về chủ đề này nên ta sẽ tìm kiếm một cách thức khác tiện lợi hơn. Thật may mắn là nó nằm sẵn trong lớp Array với tên phương thức là ForEach (bạn cũng có thể tìm thấy phương thức này trong các kiểu collection khác).
Cú pháp của phương thức:
void Array.ForEach<T>(T[] array, Action<T> action)
Ở đây nếu bạn đã hiểu được các vấn đề được nói tới ở trên thì công việc lúc này trở nên rất đơn giản. Bạn có thể tự viết được mã lệnh để sử dụng phương thức ForEach này vớianonymous method. Nếu chưa bạn cũng có thể xem cách làm tương tự như dưới đây:
Array.ForEach(evens, delegate(int n) { Console.WriteLine(n); });
Như bạn có thể thấy trong phần định nghĩa Predicate<T> và Action<T>, chúng yêu cầu duy nhất một tham số với kiểu bất kì. Ở trên vì đang làm việc với kiểu int nên ta sẽ truyền vào delegate kiểu int và tên biến tự đặt là n. Sau đó chỉ việc dùng tham số n này trong khối lệnh của anonymous method.
  • Comparison<T>
Một ngày đẹp trời bạn coi lại ví dụ này và thấy các phần tử của mảng sắp xếp quá lộn xộn, và bạn muốn sắp xếp lại chúng nhưng lại không muốn sử dụng các thuật toán sắp xếp. Bạn tìm và thấy phương thức Sort trong lớp Array và bắt đầu thực hiện, kết quả thật mĩ mãn. Bạn chú ý đến overload thứ 5 của phương thức Sort này, nó được định nghĩa:
void Array.Sort<TT[] array, Comparison<T> comparison)
Bạn thấy thích thú và muốn sử dụng nó, chú ý rằng nó yêu cầu hai tham số.
Array.Sort(evens, delegate(int x, int y)
{
return x.CompareTo(y);
});
Rất đơn giản nhưng bạn tự hỏi tại sao phải sử dụng cách này trong khi các overload khác của nó dễ sử dụng hơn nhiều? Đặt trường hợp bạn có một danh sách các đối tượng kiểu NhanVien có nhiều thuộc tính, và trong nhiều ngữ cảnh khác nhau, bạn cần sắp xếp danh sách này theo một thuộc tính riêng (chẳng hạn xếp theo tên, tuổi, lương,…) thì rõ ràng cách dùng Comparison<T> là linh hoạt và dễ dàng nhất.

3. Phần kết

Bạn đã làm quen và nắm được cơ bản cách sử dụng anonymous method, đây là một bước cần thiết để giúp bạn tiếp thu một kiến thức khác mà tôi sẽ giới thiệu trong các bài sau, đó là về Lambda Expression. Hy vọng bạn có thể áp dụng các kiến thức trong bài này trong việc giải quyết các vấn đề và phát triển ứng dụng của mình.
Read More »

Thứ Hai, 6 tháng 7, 2015

Generate fusion chart from controller MVC

We all know FusionCharts is a javascript charting library which is used by many developer in the world. FusionCharts is very good, but it's licensing is too expensive. In this article, we see the way to generate fusion charts from controller mvc using free version of FusionCharts.
The first, let go to FusionCharts website and download free trial (it's free and no limit time to use). Here is example of one fusion chart (json)
{
    "chart": {
        "palette": "3",
        "caption": "Production Report",
        "yaxisname": "Units",
        "showvalues": "0",
        "numvdivlines": "10",
        "divlinealpha": "30",
        "drawanchors": "0",
        "labelpadding": "10",
        "yaxisvaluespadding": "10",
        "useroundedges": "1",
        "legendborderalpha": "0",
        "showborder": "0"
    },
    "categories": [
        {
            "category": [
                {
                    "label": "2009"
                },
                {
                    "label": "2010"
                },
                {
                    "label": "2011"
                },
                {
                    "label": "2012"
                },
                {
                    "label": "2013"
                },
                {
                    "label": "2014 (Projected)"
                }
            ]
        }
    ],
    "dataset": [
        {
            "seriesname": "Model A12",
            "color": "A66EDD",
            "data": [
                {
                    "value": "35"
                },
                {
                    "value": "42"
                },
                {
                    "value": "31"
                },
                {
                    "value": "28"
                },
                {
                    "value": "34"
                },
                {
                    "value": "30",
                    "dashed": "1"
                }
            ]
        },
        {
            "seriesname": "Model A15",
            "color": "F6BD0F",
            "data": [
                {
                    "value": "22"
                },
                {
                    "value": "25"
                },
                {
                    "value": "18"
                },
                {
                    "value": "22"
                },
                {
                    "value": "17"
                },
                {
                    "value": "16",
                    "dashed": "1"
                }
            ]
        }
    ]
}

To generate fusion charts from controller mvc, we will transform our chart data to form above.

1. Create a class named FusionChartData, it express for dataset fusion chart.

public class FusionChartData { /// <summary> /// data array title /// </summary> public string Seriesname { get; set; } /// <summary> /// Color show on chart (column format) /// </summary> public string Color { get; set; } /// <summary> /// Tooltip text when hover on chart (this data) /// </summary> public string[] ToolTips { get; set; } /// <summary> /// Mảng label tương ứng cho từng value mảng Data /// </summary> public string[] Labels { get; set; } /// <summary> /// Dữ liệu mảng data /// </summary> public string[] Datas { get; set; } /// <summary> /// Color serial pie chart /// </summary> public string[] Colors { get; set; } /// <summary> /// Contructor chart which have column form /// </summary> /// <param name="Seriesname"></param> /// <param name="Color"></param> /// <param name="Datas"></param> public FusionChartData(string Seriesname, string Color, string[] Datas) { this.Seriesname = Seriesname; this.Color = Color; this.Datas = Datas; } /// <summary> ///Contructor chart which have column form + column tooltip /// </summary> /// <param name="Seriesname">The seriesname.</param> /// <param name="Color">The color.</param> /// <param name="ToolTips">The tool tips.</param> /// <param name="Datas">The datas.</param> /// <Modified> /// Name Date Comments /// phongnc 1/30/2015 created /// </Modified> public FusionChartData(string Seriesname, string Color, string[] ToolTips, string[] Datas) { this.Seriesname = Seriesname; this.Color = Color; this.ToolTips = ToolTips; this.Datas = Datas; } /// <summary> /// Contructor Pie chart /// </summary> /// <param name="Labels"></param> /// <param name="Datas"></param> public FusionChartData(string[] Labels, string[] Datas) { this.Labels = Labels; this.Datas = Datas; } /// <summary> /// Contructor Pie chart /// </summary> /// <param name="Labels"></param> /// <param name="Datas"></param> public FusionChartData(string[] Labels, string[] Datas, string[] Colors) : this(Labels, Datas) { this.Colors = Colors; } }
2. Create a class named FusionChartHelper to generate chart script from our data: 
We have chart base properties ex: width, height, background color...And we can add more properties with "string[] Properties". Base on FussionChart format above we will generate script "chart",  "categories", "dataset" through methods RenderFusionChart, RenderCategories, RenderDataset and from that we generate final script RenderChart.

public class FusionChartHelper
    {
        private string _bgColor = "ffffff";
        private string _width = "500";
        private string _height = "300";
        private string _idDivContainer = "chartContainer";
        /// <summary>
        /// Chart type
//// http://docs.fusioncharts.com/flex/charts/Contents/introduction_chartTypes.html
        /// </summary>
        public string ChartType { get; set; }
        /// <summary>
        /// ID div container
        /// </summary>
        public string IDDivContainer { get { return _idDivContainer; } set { _idDivContainer = value; } }
        /// <summary>
        /// Chiều rộng biểu đồ
        /// </summary>
        public string Width { get { return _width; } set { _width = value; } }
        /// <summary>
        /// Chiều cao biểu đồ
        /// </summary>
        public string Height { get { return _height; } set { _height = value; } }
        /// <summary>
        /// Background color
        /// default: ffffff
        /// </summary>
        public string BgColor { get { return _bgColor; } set { _bgColor = value; } }
        /// <summary>
        /// chart title
        /// </summary>
        public string Caption { get; set; }
        /// <summary>
        /// horizontal axis title
        /// </summary>
        public string XAxisName { get; set; }
        /// <summary>
        /// vertical axis title
        /// </summary>
        public string YAxisName { get; set; }
        /// <summary>
        /// proprerty array
        /// Format : 'property name':'value'
        /// Ex: 'useroundedges': '1'
        /// </summary>
        public string[] Properties { get; set; }
        /// <summary>
        /// label category array
        /// </summary>
        public string[] Category { get; set; }
        /// <summary>
        /// chart style
        /// </summary>
        public string Style { get; set; }

        //Nếu dùng 1 mầu
        public string Palettecolors { get; set; }
        /// <summary>
        /// Chart data
        /// </summary>
        public List<FusionChartData> Data { get; set; }

        public bool IsGenExport { get; set; }

        /// <summary>
        /// Render 'chart'
        /// </summary>
        /// <returns></returns>
        private string RenderFusionChart()
        {
            StringBuilder chart = new StringBuilder("'chart' : {");
            chart.AppendFormat("'bgcolor': '{0}',", this.BgColor);
            chart.AppendFormat("'caption': '{0}',", this.Caption);
            //chèn mảng property
            if (this.Properties.Length > 0)
            {
                foreach (var p in Properties)
                {
                    chart.Append(p + ",");
                }
            }
            chart.AppendFormat("'xaxisname': '{0}',", this.XAxisName);
            chart.AppendFormat("'yaxisname': '{0}',", this.YAxisName);
            if (!string.IsNullOrEmpty(Palettecolors)) chart.AppendFormat(string.Format("'palettecolors':'{0}',", Palettecolors));
            //xuất image, pdf
            chart.Append("'exportenabled': '1',");
            chart.Append("'exportatclient': '0',");
            chart.Append("'exporthandler': 'http://107.21.74.91/',");
            chart.Append("'html5exporthandler': 'http://107.21.74.91/',");
            chart.Append("'exportFormats': 'PNG=" + Resources.Resources.Common_ExportPNG + "|JPG=" + Resources.Resources.Common_ExportJPEG + "|PDF=" + Resources.Resources.Common_ExportPdf + "',");
            chart.AppendFormat("'exportFileName': '{0}'", this.Caption);
            //chart.Append("'useroundedges': '1',");
            //if (this.Formatnumberscale)
            //{
            //    chart.Append("'formatnumberscale': '1'");
            //}
            //else
            //{
            //    chart.Append("'formatnumberscale': '0'");
            //}
            chart.Append("}");
            return chart.ToString();
        }

        /// <summary>
        /// Render categories
        /// </summary>
        /// <returns></returns>
        private string RenderCategories()
        {
            StringBuilder cate = new StringBuilder();
            try
            {
                if (this.Category.Length > 0) //nếu mà có category
                {
                    if (this.Category.Length == 1 && this.Category[0].Equals(""))
                    {
                        return string.Empty;
                    }
                    foreach (var c in this.Category)
                    {
                        cate.AppendFormat("{{ 'label': '{0}' }},", c);
                    }
                    cate.Remove(cate.Length - 1, 1); //xóa ký tự ',' ở cuối chuỗi
                    cate.Append("] } ]");
                }
            }
            catch (Exception ex)
            {
                return string.Empty;
            }
            return "'categories': [ { 'category': [" + cate.ToString();
        }

        /// <summary>
        /// Renders dataset
        /// </summary>
        /// <returns></returns>
        /// <Modified>
        /// Name     Date         Comments
        /// phongnc  1/26/2015   created
        /// </Modified>
        protected virtual string RenderDataset()
        {
            StringBuilder data = new StringBuilder("'dataset': [" + Environment.NewLine);
            if (this.Data != null && this.Data.Count > 0) //nếu có dữ liệu
            {
                foreach (var fd in this.Data)
                {
                    data.Append("{");
                    data.AppendFormat("'seriesname' : '{0}','color': '{1}', 'data' : [" + Environment.NewLine, fd.Seriesname, fd.Color);
                    //kiểm tra xem data của thành phần có dữ liệu ko
                    if (fd.Datas.Length > 0)
                    {
                        for (int i = 0; i < fd.Datas.Length; i++)
                        {
                            if (fd.ToolTips != null && fd.ToolTips.Length > 0)
                            {
                                if (fd.ToolTips.Length > i)
                                {
                                    if (string.IsNullOrEmpty(fd.ToolTips[i]))
                                    {
                                        data.AppendFormat("{{'value' : '{0}'}},", fd.Datas[i]);
                                    }
                                    else //if (!fd.ToolTips[i].Contains("{0}"))
                                    {
                                        data.AppendFormat("{{'tooltext': '" + fd.ToolTips[i] + "', 'value' : '{0}'}},", fd.Datas[i]);
                                    }
                                    //else
                                    //{
                                    //    data.AppendFormat("{{'tooltext': '" + fd.ToolTips[i] + "', 'value' : '{1}'}},", fd.Datas[i], fd.Datas[i]);
                                    //}
                                }
                                else
                                {
                                    data.AppendFormat("{{'value' : '{0}'}},", fd.Datas[i]);
                                }
                            }
                            else
                            {
                                data.AppendFormat("{{'value' : '{0}'}},", fd.Datas[i]);
                            }
                        }
                        data.Remove(data.Length - 1, 1); //xóa ký tự ',' ở cuối chuỗi
                    }
                    data.Append("] },");
                }
                data.Remove(data.Length - 1, 1); //xóa ký tự ',' ở cuối chuỗi
                data.Append("]");
            }
            else
            {
                return string.Empty;
            }
            return data.ToString();
        }

        /// <summary>
        /// Render chart script
        /// </summary>
        /// <returns></returns>
        public string RenderChart()
        {
            StringBuilder chart = new StringBuilder();
            chart.AppendLine(@"<script type=""text/javascript"">");
            chart.AppendLine(@"FusionCharts.ready(function () { var revenueChart" + IDDivContainer + " = new FusionCharts({");
            chart.AppendFormat("type: '{0}',", this.ChartType);
            chart.AppendFormat("renderAt: '{0}',", this.IDDivContainer);
            chart.AppendFormat("width: '{0}',", this.Width);
            chart.AppendFormat("height: '{0}',", this.Height);
            chart.Append("dataFormat: 'json', dataSource: {");
            chart.Append(this.RenderFusionChart());
            if (!string.IsNullOrEmpty(this.Style))
            {
                chart.Append(',' + this.Style);
            }
            string cate = this.RenderCategories();
            if (!string.IsNullOrEmpty(cate))
            {
                chart.Append(',' + cate);
            }
            string dataset = this.RenderDataset();
            if (!string.IsNullOrEmpty(dataset))
            {
                chart.Append(',' + dataset);
            }
            chart.AppendLine("}});");
            chart.AppendFormat("revenueChart" + IDDivContainer + ".render('{0}');", this.IDDivContainer);
            chart.AppendLine("}); </script>");
            return chart.ToString();
        }

        public FusionChartHelper() { }

        public FusionChartHelper(string ChartType, string Caption, string XAxisName, string YAxisName,
            string IDDivContainer, string Width, string Height, string[] Properties,
            string[] Category, List<FusionChartData> Data)
        {
            this.ChartType = ChartType;
            this.Caption = Caption;
            this.XAxisName = XAxisName;
            this.YAxisName = YAxisName;
            this.IDDivContainer = IDDivContainer;
            this.Width = Width;
            this.Height = Height;
            this.Properties = Properties;
            this.Category = Category;
            this.Data = Data;
        }
    }
3. Using:
- Controller: controller bellow show how to generate ScrollColumn2D
public ActionResult GenChart(){
FusionChartData fcd = new FusionChartData("", "00CC00", new string[] {"Data 01","Data 02","Data 03"}, new string[]{"100","200","300"});
FusionChartHelper fc = new FusionChartHelper("ScrollColumn2D" ,"Demo ScrollColumn2D"
, "horizontal axis name", "vertical axis name", "chartContainer", "95%", "400",
new string[] { "'useroundedges': '1', 'numberScaleValue' : '1000', 'numberScaleUnit':' '" }, new string[] {"01","02","03"}, new List<FusionChartData>() { fcd }); chart = fc.RenderChart();
return Json(chart, JsonRequestBehavior.AllowGet);
}
- Index:
<html>
<head><title>Demo generate fusion chart</title>
<script>
function Search(){
$.ajax({ type: "POST", dataType: "json", contentType: 'application/json; charset=utf-8', url: "/Chart/GenChart",, success: function (result) { $('#chartContainer').html(result); } });
}
</script>
</head>
<body>
<a id="btnSearch" href="javascript:void(0);" onclick="Search()"> @Resources.Common_ViewDiagram </a>
<div id="chartContainer" style="text-align:center;">
</div>
</body>
</html>

Click btnSearch -> generate chart. Here is a example:

Read More »