Category: Bootstrap

  • การย่อ-ยุบแถวข้อมูลบน GridView โดยประยุกต์ใช้ร่วมกับ jQuery และ Collapse ใน Bootstrap (C#)

               จากความเดิมตอนที่แล้ว เราได้พูดถึงวิธีการจัดการข้อมูลจำนวนมากด้วยการจัดกลุ่มหมวดหมู่ของข้อมูลใน GridView กันไปแล้ว ซึ่งหากผู้อ่านท่านใดต้องการทราบวิธีการจัดหมวดหมู่สามารถตามดูเนื้อหาในบทความได้จาก การจัดหมวดหมู่แถวของข้อมูลบน GridView ด้วย C# และในส่วนของบทความนี้จะเป็นเนื้อหาต่อยอดการทำงานจากการจัดหมวดหมู่ดังกล่าว โดยเพิ่มความสามารถให้หมวดหมู่หรือกลุ่มเหล่านั้นสามารถย่อ-ยุบได้ เพื่ออำนวยความสะดวกให้กับผู้ใช้ในการดูข้อมูลแยกส่วนกันชัดเจนมากยิ่งขึ้น หรือเพื่อตอบโจทย์ให้กับผู้ใช้บางท่านที่อาจมีความต้องการดูข้อมูลทีละส่วนได้ โดยจะนำ Component ที่ชื่อว่า Collapse ใน Bootstrap มาประยุกต์ใช้ในการแสดงผลร่วมกับ GridView และยังมี jQuery มาเป็นอีกหนึ่งตัวช่วยเพื่อให้สามารถแสดงผลตามที่ต้องการได้ โดยการอธิบายในบทความนี้ ทางผู้เขียนจะขอตัดตอนในส่วนของรายละเอียดขั้นตอนวิธีการจัดหมวดหมู่ไป และข้ามมาพูดถึงขั้นตอนที่ต้องจัดทำเพิ่มเติมในการทำย่อ-ยุบเลยละกันนะคะ

               ก่อนจะไปเริ่มในส่วนของการเขียนโค้ด เรามาทำความรู้จักกับ ค่าที่จำเป็นต้องใช้ในการระบุให้กับแถวหลัก(parent)เพื่อให้สามารถย่อยุบได้ กันก่อนนะคะ

    • data-toggle=”collapse
    • data-target=”.multi-collapse” เพื่อกำหนด target ที่เราต้องการให้ย่อยุบได้ โดยใช้สไตล์ชีทเป็นตัวช่วยเพื่อแยกแต่ละกลุ่มออกจากกัน ซึ่งในที่นี้จะตั้งชื่อสไตล์ชีท multi-collapse ตามด้วยรหัสของประเภทกลุ่มนั้น โดยต้องระบุสไตล์ชีทนี้ให้กับแถวย่อย(child)ด้วย
    • aria-controls=”demo1 demo2 demo3 demo4 demo5” เพื่อกำหนดพื้นที่ที่จะย่อยุบ โดยสามารถกำหนดได้มากกว่า 1 พื้นที่ ซึ่งจะแยกด้วยการเว้นวรรคชื่อ id ของแถวย่อย(child) ในตัวอย่างนี้ คือ แถวย่อยของแถวหลักนี้ ประกอบด้วย 5 แถว คือ แถวที่มี id ชื่อ demo1,demo2,demo3,demo4, demo5 นั่นเอง
    • class = “collapseToggle” เป็นการระบุสไตล์ชีทเพื่อใช้ในการระบุตำแหน่งในการเปลี่ยนไอคอนเวลากดย่อ-ยุบ โดยเรียกใช้งานผ่าน jQuery(ซึ่งจะกล่าวถึงในส่วนถัดไป)

               หลังจากเรารู้จักค่าที่จำเป็นต้องใช้กันไปแล้ว เราก็มาเริ่มปรับแก้โค้ดเพิ่มเติม เพื่อเพิ่มความสามารถให้ GridView ของเรากันเลยค่ะ

    1. ปรับแก้ในส่วนของ GroupGv_DataBound เพิ่มเติม เพื่อกำหนดค่าที่ระบุไว้ข้างต้นในแถวของหมวดหมู่ที่แทรกเข้ามา

    string lastCatName = "";
    string AllName = "";
    
     
    protected void GroupGv_DataBound(object sender, EventArgs e)
            {
                lastCatName = "";
                Table table = (Table)GroupGv.Controls[0];
    
                foreach (GridViewRow row in GroupGv.Rows)
                {
                  
                    HiddenField HidCatName = (HiddenField)row.FindControl("HidCatName");
                    HiddenField HidCatID = (HiddenField)row.FindControl("hdCatID");
                    HiddenField HidID = (HiddenField)row.FindControl("hdID");
    
                    ////ตั้งชื่อ id ให้กับแถวย่อย(child) แต่ละแถว
                    row.Attributes.Add("id", "Name" + HidID.Value);
    
                   ////กำนดสไตล์ชีทของตัวแถวย่อยและระบุเพื่อให้ย่อยุบได้ ด้วย accordian-body collapse และระบุพื้นที่เพื่อให้อ้างถึงจากแถวหลัก(parent)ได้ 
                  /////ซึ่งเป็นในส่วนของ multi-collapse จะเป็น multi-collapse ตามด้วยชื่อรหัสประเภทนั่นเอง 
                  /////โดยแถวย่อย(child) ที่มีแถวหลัก(parent)เดียวกัน ค่าของ multi-collapse จะตรงกันและตรงกับค่า target ที่ระบุไว้ในแถวหลัก(parent)ด้วย
    
                    if (row.RowState == DataControlRowState.Alternate)  ////กรณีที่ต้องการให้แต่ละแถวสลับสีกันเพื่อให้ง่ายต่อการดูข้อมูล 
                       row.CssClass = "Blue accordian-body collapse multi-collapse" + HidCatID.Value;
                    else               
                      row.CssClass = "accordian-body collapse multi-collapse" + HidCatID.Value;
    
                    if (HidCatName.Value != lastCatName)
                    {
    
                       ////////วนเพื่อหาจำนวน child ในแต่ละประเภทเพื่อใช้ในการกำหนด aria-controls
    
                        var strList = GroupData.Select("CategoryID='" + HidCatID.Value + "'");
                        foreach (DataRow dr in strList)
                        {
                            AllName += " Name" + dr["ID"];
                        }
    
                       //// เป็นการหาผลรวมค่าของฟิลด์ Amt ซึ่งหมายถึงจำนวน โดยเป็นการรวมค่าฟิลด์แยกตามแต่ละ CategoryID นั่นเอง
    
                        var sumOfValuesInCategory = GroupData.AsEnumerable().Where(x => x.Field<string>("CategoryID") == HidCatID.Value).Sum(x => x.Field<int>("Amt")).ToString();
                       
                        int realIndex = table.Rows.GetRowIndex(row);
                        string text = HidCatName.Value;
                        GridViewRow newHeaderRow = new GridViewRow(realIndex, 0, DataControlRowType.Header, DataControlRowState.Normal);
    
                        /////กำหนดค่าต่างๆ (data-toggle  data-target  aria-controls )ให้กับแถว เพื่อให้สามารถย่อ-ยุบได้ 
    
                        newHeaderRow.Attributes.Add("data-toggle", "collapse");
                        newHeaderRow.Attributes.Add("data-target", ".multi-collapse" + HidCatID.Value);
                        newHeaderRow.Attributes.Add("aria-expanded", "true");
                        newHeaderRow.Attributes.Add("aria-controls", AllName);
    
                        TableCell newCell = new TableCell();
                        newHeaderRow.Cells.AddAt(0, newCell);
    
                       /////ปรับแก้การระบุค่า ColumnSpan จากเดิมที่รวมกันทุกคอลัมน์(GroupGv.Columns.Count) 
                       //// แต่กรณีนี้ต้องเว้นคอลัมน์ไว้แสดงผลจำนวนรวมแต่ละประเภทด้วย
    
                        newCell.ColumnSpan =1;
                        newCell.BackColor = System.Drawing.Color.FromName("#399ea9"); ;
                        newCell.ForeColor = System.Drawing.Color.White;
                        newCell.Font.Bold = true;
                        newCell.Attributes.Add("class", "collapseToggle");
    
                        /////ใส่ไอคอน + หน้าข้อความชื่อประเภท เพื่อบอกให้ทราบว่าสามารถกดย่อ-ยุบได้
                        newCell.Text = "<i class='fas fa-plus mr-2'></i>" + string.Format(HidCatName.Value, "&nbsp;{0}", text);
    
                        ///สร้าง TableCell หรือคอลัมน์ใหม่ เพื่อแสดงผลข้อมูลจำนวนรวมของแต่ละประเภท 
    
                        TableCell newCellTotal = new TableCell();
    
                        /////เพิ่ม TableCell ในแถวที่กำลังสร้างและระบุค่าต่างๆ
                        newHeaderRow.Cells.AddAt(1, newCellTotal);
                        newCellTotal.ColumnSpan = 1;
                        newCellTotal.BackColor = System.Drawing.Color.FromName("#399ea9"); ;
                        newCellTotal.ForeColor = System.Drawing.Color.White;
                        newCellTotal.Font.Bold = true;
                        newCellTotal.HorizontalAlign = HorizontalAlign.Right;
    
                        ////นำค่าผลรวมในตัวแปร sumOfValuesInCategory ที่คำนวณได้ข้างต้นมาจัดรูปแบบก่อนแสดงผลใน TableCell สร้าง
    
                        newCellTotal.Text = string.Format("{0:#,##0}",int.Parse(sumOfValuesInCategory));
                        newCellTotal.Attributes.Add("class", "collapseToggle");
                        table.Controls.AddAt(realIndex, newHeaderRow);
                    
                          AllName = "";
    
                    }
                    lastCatName = HidCatName.Value;
                }
            }

    2.จัดทำให้ไอคอนสามารถเปลี่ยนเป็น + หรือ – ได้ เมื่อกดย่อ-ยุบ ด้วย jQuery

     <script>
         $(document).ready(function () {
    
    ///เมื่อมีการคลิก element ที่มีสไตล์ชีท collapseToggle จะทำการเปลี่ยนค่าไอคอน หากเป็นภาพบวกจะเปลี่ยนเป็นลบ หากเป็นเครื่องหมายลบจะเปลี่ยนเป็นเครื่องหมายบวก
                $('.collapseToggle').click(function () {
                    $(this).find('i').toggleClass('fa-plus fa-minus');
                });
            });
    </script>

    เพิ่มเติม : ท่านสามารถสร้างสไตล์ชีทตกแต่งเพิ่มเติมให้กับแถวของข้อมูลได้ ในกรณีนี้ได้ทำการสร้างสไตล์ชีทเพื่อไว้สำหรับเวลาเอาเม้าส์ชี้ที่แถวที่สามารถย่อ-ยุบได้ จะแสดงเป็นรูปมือ เพื่อให้ผู้ใช้ทราบว่าสามารถกดได้ค่ะ

        <style>
            .collapseToggle {
                cursor: pointer;
            }
        </style>

    ผลลัพธ์

    เกร็ดความรู้เพิ่มเติม : จากตัวอย่างข้างต้น ตอนเปิดหน้าจอครั้งแรก ทุกหมวดหมู่จะถูกยุบอยู่ หากต้องการให้การแสดงผลครั้งแรก ทุกหมวดหมู่ถูกขยายอยู่ สามารถปรับแก้โค้ดอีกเพียงเล็กน้อย ดังนี้ค่ะ

    1. เพิ่มคำว่า “show” เข้าไปในสไตล์ชีทตอนกำหนดให้แถวย่อย ดังนี้ค่ะ

    แบบเดิม

                    if (row.RowState == DataControlRowState.Alternate)  ////กรณีที่ต้องการให้แต่ละแถวสลับสีกันเพื่อให้ง่ายต่อการดูข้อมูล 
                       row.CssClass = "Blue accordian-body collapse multi-collapse" + HidCatID.Value;
                    else               
                      row.CssClass = "accordian-body collapse multi-collapse" + HidCatID.Value;

    แบบใหม่

                      if (row.RowState == DataControlRowState.Alternate)  ////กรณีที่ต้องการให้แต่ละแถวสลับสีกันเพื่อให้ง่ายต่อการดูข้อมูล 
    
                        row.CssClass = "Blue accordian-body collapse show multi-collapse" + HidCatID.Value;
                    else
                        row.CssClass = "accordian-body collapse show multi-collapse" + HidCatID.Value;

    2.ปรับแก้ให้ไอคอนแรกที่ต้องการแสดงเป็นเครื่องหมายลบ ดังนี้ค่ะ

    แบบเดิม

                       /////ใส่ไอคอน + หน้าข้อความชื่อประเภท เพื่อบอกให้ทราบว่าสามารถกดย่อ-ยุบได้
                        newCell.Text = "<i class='fas fa-plus mr-2'></i>" + string.Format(HidCatName.Value, "&nbsp;{0}", text);

    แบบใหม่

                        /////ใส่ไอคอน + หน้าข้อความชื่อประเภท เพื่อบอกให้ทราบว่าสามารถกดย่อ-ยุบได้
                        newCell.Text = "<i class='fas fa-minus mr-2'></i>" + string.Format(HidCatName.Value, "&nbsp;{0}", text);

    ผลลัพธ์

              จากตัวอย่าง การแสดงผลครั้งแรกก็จะเปลี่ยนเป็นขยายทั้งหมด และแสดงไอคอนเป็นเครื่องหมายลบ(-)ตั้งต้นไว้ให้ และสามารถย่อ-ยุบตามปกติได้แล้วค่ะ

    เพิ่มเติม

              นอกจากนี้ ผู้เขียนขอแถมให้อีกนิดสำหรับท่านที่ต้องการจะย่อประเภทกลุ่มทั้งหมด หรือต้องการให้แสดงประเภทกลุ่มทั้งหมด อาจจะทำเป็นปุ่มให้ผู้ใช้กด ซึ่งมีวิธี ดังนี้ค่ะ

    1. สร้างปุ่ม 2 ปุ่ม เพื่อกดขยายทั้งหมด และย่อทั้งหมด

    <asp:UpdatePanel ID="UpdatePanel1" runat="server">
            <ContentTemplate>
                <div class="col pull-right">
                    <asp:LinkButton ID="lnkShowAll" runat="server" CssClass="btn btn-sm btn-info mb-1" OnClientClick="javascript:ShowHideAll('1');return false;"><i class="fas fa-eye"></i>แสดงทั้งหมด</asp:LinkButton>
                    <asp:LinkButton ID="lnkHideAll" CssClass="btn btn-sm btn-info mb-1" OnClientClick="ShowHideAll('0');return false;" runat="server"><i class="fas fa-eye-slash"></i>ซ่อนทั้งหมด</asp:LinkButton>
                </div>
    
    
            </ContentTemplate>
        </asp:UpdatePanel>

    2. เขียนฟังก์ชั่นในการซ่อน/แสดงทั้งหมด

    <script>
        
     function ShowHideAll(flag) {
    
              /////ล้างค่าการระบุการแสดงผลและการแสดงไอคอนทั้งหมด ทำให้ทุกแถวยุบอยู่ จนกว่าจะมีการสั่งให้ show
                $(".accordian-body").removeClass("show");
                $(".collapseToggle").find('i').removeClass("fa-plus").removeClass("fa-minus");
    
             /////กรณีต้องการให้แสดงจะเพิ่มสไตล์ชีท show ให้กับทุกแถว และแสดงไอคอนเป็น - ทั้งหมด เพื่อให้กดย่อได้
    
                if (flag == '1') {
                    $(".accordian-body").addClass("show");
                    $(".collapseToggle").find('i').addClass("fa-minus");
                }
            /////กรณีต้องการให้แสดงจะแสดงไอคอนเป็น + ทั้งหมด เพื่อให้กดขยายได้
                else
                    $(".collapseToggle").find('i').addClass("fa-plus");
                }
    
    </script>

    ผลลัพธ์

    หมายเหตุ : ในการทำงานนี้จะใช้ jQuery และ Bootstrap ร่วมด้วย ผู้ที่จะนำไปใช้งานอย่าลืมอ้างอิงไฟล์สไตล์ชีทและสคริปท์ของ Bootstrap รวมทั้งไฟล์ของ jQuery เพื่อให้โค้ดข้างต้นสามารถทำงานได้นะคะ

              ทั้งหมดนี้ก็เป็นเพียงหนึ่งในตัวอย่างวิธีการที่จะแก้ปัญหาในการแสดงผลข้อมูลแบบตารางด้วย GridView แบบจัดกลุ่มและสามารถย่อ-ยุบข้อมูลภายในกลุ่มได้ โดยนำความสามารถของ Component อย่าง collapse ใน Bootstrap เข้ามาช่วยเท่านั้น แต่ในส่วนของรูปแบบ วิธีการ แต่ละท่านสามารถปรับเปลี่ยนและพลิกแพลงเพิ่มเติมได้ตามความเหมาะสม อีกทั้งยังสามารถนำเกร็ดความรู้นี้ไปประยุกต์ใช้กับงานของท่านได้อีกด้วย และขอบคุณที่ติดตามนะคะ ^^

    แหล่งอ้างอิง

    https://getbootstrap.com/docs/4.0/components/collapse/
    https://www.geeksforgeeks.org/how-to-change-symbol-with-a-button-in-bootstrap-accordion/

  • การจัดหมวดหมู่แถวของข้อมูลบน GridView ด้วย C#

              ในการดึงข้อมูลมาแสดงผลในรูปแบบตาราง GridView อาจเป็นหนึ่งในทางเลือกของนักพัฒนาที่จะหยิบไปใช้เป็นเครื่องมือในการแสดงผล แต่ในกรณีที่ข้อมูลมีจำนวนมากอาจทำให้ผู้ใช้รู้สึกตาลายและอ่านยากไปสักหน่อย ผู้พัฒนาจึงต้องพยายามหาวิธีจัดการข้อมูลในการแสดงผลให้สามารถอ่านง่ายและสบายตามากยิ่งขึ้น ดังนั้น ในกรณีที่ข้อมูลมีจำนวนมาก การจำแนกประเภท หรือแยกออกเป็นกลุ่มย่อยๆก็เป็นอีกหนึ่งในทางเลือกที่จะช่วยแก้ปัญหาดังกล่าวได้ ดังนั้น ในบทความนี้ผู้เขียนจะขอแนะนำวิธีการแสดงผลของข้อมูลบน GridView แบบจำแนกออกตามกลุ่ม เพื่ออำนวยความสะดวกให้กับผู้ใช้ในการดูข้อมูลแยกส่วนกัน เพื่อเพิ่มความสามารถให้กับ GridView ซึ่งเราเองใช้งานอยู่และน่าจะคุ้นเคยกันดีอยู่แล้ว ซึ่งจะมีขั้นตอนดังนี้ค่ะ

    ขั้นตอนในการพัฒนา

    1. เตรียมข้อมูลในการแสดงผล โดยจากตัวอย่างนี้ จะทำการสมมุติข้อมูลของดอกไม้ ผลไม้ และต้นไม้ และมีข้อมูลเพื่อใช้ในการแยกประเภทไว้ด้วย เพื่อให้เห็นภาพมากขึ้นค่ะ
    //// ประกาศตัวแปร GroupData
     เป็น ViewState เพื่อใช้ในงานข้อมูลในส่วนการแทรกแถวประเภทกลุ่มใน Event อื่นด้วย 
    
    public DataTable  GroupData
    
            {
                get
                {
                    if (ViewState["GroupData"] == null)
                    {
                        ViewState["GroupData"] = new DataTable();
                    }
                    return (DataTable)ViewState["GroupData"];
                }
                set { ViewState["GroupData"] = value; }
            }
    
    //// ผู้อ่านสามารถเรียกใช้ฟังก์ชั่น Getdata() การดึงข้อมูลนี้ในตอน Page_Load เพื่อดูเป็นตัวอย่างได้ค่ะ
    protected void Getdata() 
            {
                
                GroupData.Columns.AddRange(new DataColumn[5] {
                             new DataColumn("CategoryName", typeof(string)),
                             new DataColumn("Name", typeof(string)),
                             new DataColumn("ID", typeof(string)),
                              new DataColumn("Amt", typeof(int)),
                            new DataColumn("CategoryID",typeof(string))});
    
                GroupData.Rows.Add("Flower", "Rose", "1",2500, "01");
                GroupData.Rows.Add("Flower", "Lotus", "3",150, "01");
                GroupData.Rows.Add("Fruit", "Grape", "2",350, "02");
                GroupData.Rows.Add("Fruit", "Mango", "4",1750, "02");
                GroupData.Rows.Add("Fruit", "Orange", "5",2240, "02");
                GroupData.Rows.Add("Tree", "Cactus", "6",370, "03");
                GroupData.Rows.Add("Tree", "Hazelnut Tree", "6",2250, "03");
                ////นำข้อมูลใน Datatable ชื่อ GroupData แสดงผลใน GridView
                GroupGv.DataSource = GroupData;
                GroupGv.DataBind();
            }
    

    หมายเหตุ : การประกาศตัวแปร และการดึงข้อมูลเป็นเพียงการยกตัวอย่างง่ายๆ เพื่อให้ผู้ใช้เข้าใจเท่านั้น ในการทำงานจริงผู้อ่านสามารถใช้วิธีการประกาศตัวแปรและเรียกใช้แบบอื่น หรือดึงข้อมูลจากส่วนอื่นได้ ขึ้นอยู่กับความเหมาะสมและความถนัดค่ะ

    2. เตรียม GridView ที่จะใช้ในการแสดงผล โดยจะขอยกตัวอย่างให้ดูการแสดงผล GridView แบบทั่วไปก่อนมีการจัดกลุ่ม เพื่อให้เห็นความแตกต่าง ดังนี้ค่ะ

        <asp:GridView ID="GroupGv" runat="server" AutoGenerateColumns="False"
            DataKeyNames="ID"  CssClass="table table-sm table-hover Blue" Width="100%">
            <AlternatingRowStyle CssClass="Blue" />
            <EmptyDataRowStyle CssClass="Blue" />
            <EmptyDataTemplate>
                <br />
                <div style="text-align: center">
                    <i class="fas fa-exclamation-circle"></i>&nbsp;&nbsp; ไม่พบข้อมูล<br />
                    &nbsp;
                </div>
    
            </EmptyDataTemplate>
            <EmptyDataRowStyle HorizontalAlign="Center" />
            <HeaderStyle CssClass="Blue" />
    
            <Columns>
                <asp:TemplateField HeaderText="Name">
                    <ItemTemplate>
                        <asp:HiddenField ID="hdCatID" runat="server" Value='<%# Eval("CategoryID") %>' />
                        <asp:HiddenField ID="hdID" runat="server" Value='<%# Eval("ID") %>' />
                        <asp:HiddenField ID="HidCatName" runat="server" Value='<%# Eval("CategoryName") %>' />
                        <asp:Label ID="lblName" runat="server" Text='<%# Eval("Name") %>'></asp:Label>
                    </ItemTemplate>
                     <ItemStyle  Width="50%"  />
                </asp:TemplateField> <asp:BoundField HeaderText="Category Name" DataField="CategoryName">
                <ItemStyle  Width="30%"/>
                 </asp:BoundField>
                  <asp:BoundField HeaderText="Amount" DataField="Amt"  DataFormatString="{0:#,##0}" >
                <ItemStyle HorizontalAlign="Right"  Width="20%"/>
                 </asp:BoundField>
               
            </Columns>
        </asp:GridView>
    

    เพิ่มเติม : ข้อมูลจำนวนเป็นข้อมูลที่เป็นตัวเลข จึงได้ทำการจัด Format รูปแบบของข้อมูลให้แสดงผลแบบตัวเลข ด้วยการระบุ DataFormatString=”{0:#,##0}” เช่น หากข้อมูล 2500 จะแสดง 2,500 ให้อัตโนมัติ

    ผลลัพธ์(ก่อนทำการจัดกลุ่ม)

    3. เพิ่ม Event ที่ชื่อว่า OnDataBound=”GroupGv_DataBound” ให้กับ GridView เพื่อแสดงผลข้อมูลแบบกลุ่ม และตัดคอลัมน์ประเภท(Category Name)ออกไป เนื่องจากเราจะนำไปใช้แสดงผลในการจัดกลุ่ม

        <asp:GridView ID="GroupGv" runat="server" AutoGenerateColumns="False"
            DataKeyNames="ID"  CssClass="table table-sm table-hover Blue" Width="100%" OnDataBound="GroupGv_DataBound">
            <AlternatingRowStyle CssClass="Blue"  />
            <EmptyDataRowStyle CssClass="Blue" />
            <EmptyDataTemplate>
                <br />
                <div style="text-align: center">
                    <i class="fas fa-exclamation-circle"></i>&nbsp;&nbsp; ไม่พบข้อมูล<br />
                    &nbsp;
                </div>
    
            </EmptyDataTemplate>
            <EmptyDataRowStyle HorizontalAlign="Center" />
            <HeaderStyle CssClass="Blue" />
    
            <Columns>
                <asp:TemplateField HeaderText="Name">
                    <ItemTemplate>
                         <%--นำค่าไปใช้ตอนแทรกแถวหมวดหมู่ที่ต้องการจัดกลุ่ม--%>
                        <asp:HiddenField ID="hdCatID" runat="server" Value='<%# Eval("CategoryID") %>' />
                        <asp:HiddenField ID="hdID" runat="server" Value='<%# Eval("ID") %>' />
                        <asp:HiddenField ID="HidCatName" runat="server" Value='<%# Eval("CategoryName") %>' />
                        <asp:Label ID="lblName" runat="server" Text='<%# Eval("Name") %>'></asp:Label>
                    </ItemTemplate>
                     <ItemStyle  Width="70%"  />
                </asp:TemplateField> 
                  <asp:BoundField HeaderText="Amount" DataField="Amt"  DataFormatString="{0:#,##0}">
                <ItemStyle HorizontalAlign="Right"  Width="30%"/>
                 </asp:BoundField>
               
            </Columns>
        </asp:GridView>
    

    4. เพิ่มโค้ดในส่วนของฝั่งเซิร์ฟเวอร์(C#) ให้กับ Event ของ GridView ที่เราเพิ่มในข้อ 3. เพื่อจัดกลุ่ม ดังนี้ค่ะ

            string lastCatName = "";
            string AllName = "";
    
            protected void GroupGv_DataBound(object sender, EventArgs e)
            {
                lastCatName = "";
                Table table = (Table)GroupGv.Controls[0];
    
               ////////วนเพื่อแทรกแถวชื่อแต่ละประเภทของข้อมูล เช่น ดอกไม้ ต้นไม้ หรือผลไม้ 
                foreach (GridViewRow row in GroupGv.Rows)
                {
                  
                  
                    HiddenField HidCatName = (HiddenField)row.FindControl("HidCatName");
                    HiddenField HidCatID = (HiddenField)row.FindControl("hdCatID");
                    HiddenField HidID = (HiddenField)row.FindControl("hdID");
                    
            
               ////////หากพบว่าเป็นประเภทใหม่จะทำการสร้างแถวและเพิ่มแทรกเข้าไป โดยมีการกำหนดค่าต่างๆ เช่น ข้อความที่จะแสดง สีพื้นหลัง สีตัวอักษร และค่า ColumnSpan เป็นต้น
    
     
                    if (HidCatName.Value != lastCatName)
                    {
                       
                        int realIndex = table.Rows.GetRowIndex(row);
                        string text = HidCatName.Value;
                        GridViewRow newHeaderRow = new GridViewRow(realIndex, 0, DataControlRowType.Header, DataControlRowState.Normal);
    
    
                        /////สร้าง TableCell และระบุค่าต่างๆ ก่อนนำไปเพิ่มในแถว newHeaderRow ที่เพิ่งสร้าง
                       TableCell newCell = new TableCell();
                        newHeaderRow.Cells.AddAt(0, newCell);
    
                        ///กำหนด ColumnSpan เท่ากับจำนวนคอลัมน์ทั้งหมดใน GridView (GroupGv.Columns.Count)เพื่อให้คอลัมน์ที่ต้องการเพิ่มยาวครอบคลุมทั้งแถว
                        newCell.ColumnSpan = GroupGv.Columns.Count;
                        newCell.BackColor = System.Drawing.Color.FromName("#399ea9"); ;
                        newCell.ForeColor = System.Drawing.Color.White;
                        newCell.Font.Bold = true;
                        newCell.Text = string.Format(HidCatName.Value, "&nbsp;{0}", text);
    
                        ////เพิ่มแถวที่ต้องการแทรกเข้าไปในตารางหรือ GridView ที่เรากำลังจัดการอยู่นั่นเอง
                        table.Controls.AddAt(realIndex, newHeaderRow);
                       
                    }
                    lastCatName = HidCatName.Value;
                }
            }         
    

    ผลลัพธ์ (หลังมีการจัดกลุ่ม)

    เพิ่มเติม : จากตัวอย่างข้างต้น เนื่องด้วยข้อมูลเป็นการจัดกลุ่มและมีข้อมูลเชิงตัวเลข ผู้เขียนจึงขอแนะนำเพิ่มเติมในส่วนของการแสดงจำนวนรวมแยกในแต่ละกลุ่มไว้ด้วย โดยเพิ่มเติมโค้ดในส่วนของ GroupGv_DataBound ดังนี้ค่ะ

     protected void GroupGv_DataBound(object sender, EventArgs e)
            {
                lastCatName = "";
                Table table = (Table)GroupGv.Controls[0];
    
                foreach (GridViewRow row in GroupGv.Rows)
                {
                  
                    HiddenField HidCatName = (HiddenField)row.FindControl("HidCatName");
                    HiddenField HidCatID = (HiddenField)row.FindControl("hdCatID");
                    HiddenField HidID = (HiddenField)row.FindControl("hdID");
    
    
                    if (HidCatName.Value != lastCatName)
                    {
    
                       //// เป็นการหาผลรวมค่าของฟิลด์ Amt ซึ่งหมายถึงจำนวน โดยเป็นการรวมค่าฟิลด์แยกตามแต่ละ CategoryID นั่นเอง
    
                        var sumOfValuesInCategory = GroupData.AsEnumerable().Where(x => x.Field<string>("CategoryID") == HidCatID.Value).Sum(x => x.Field<int>("Amt")).ToString();
                       
    
                        int realIndex = table.Rows.GetRowIndex(row);
                        string text = HidCatName.Value;
                        GridViewRow newHeaderRow = new GridViewRow(realIndex, 0, DataControlRowType.Header, DataControlRowState.Normal);
                        TableCell newCell = new TableCell();
                        newHeaderRow.Cells.AddAt(0, newCell);
    
                       /////ปรับแก้การระบุค่า ColumnSpan จากเดิมที่รวมกันทุกคอลัมน์(GroupGv.Columns.Count) 
                       //// แต่กรณีนี้ต้องเว้นคอลัมน์ไว้แสดงผลจำนวนรวมแต่ละประเภทด้วย
    
                        newCell.ColumnSpan =1;
                        newCell.BackColor = System.Drawing.Color.FromName("#399ea9"); ;
                        newCell.ForeColor = System.Drawing.Color.White;
                        newCell.Font.Bold = true;
                        newCell.Text = string.Format(HidCatName.Value, "&nbsp;{0}", text);
    
                        ///สร้าง TableCell หรือคอลัมน์ใหม่ เพื่อแสดงผลข้อมูลจำนวนรวมของแต่ละประเภท 
    
                        TableCell newCellTotal = new TableCell();
    
    
                        /////เพิ่ม TableCell ในแถวที่กำลังสร้างและระบุค่าต่างๆ
                        newHeaderRow.Cells.AddAt(1, newCellTotal);
                        newCellTotal.ColumnSpan = 1;
                        newCellTotal.BackColor = System.Drawing.Color.FromName("#399ea9"); ;
                        newCellTotal.ForeColor = System.Drawing.Color.White;
                        newCellTotal.Font.Bold = true;
                        newCellTotal.HorizontalAlign = HorizontalAlign.Right;
    
    
                        ////นำค่าผลรวมในตัวแปร sumOfValuesInCategory ที่คำนวณได้ข้างต้นมาจัดรูปแบบก่อนแสดงผลใน TableCell สร้าง
    
                        newCellTotal.Text = string.Format("{0:#,##0}",int.Parse(sumOfValuesInCategory));
                       
                        table.Controls.AddAt(realIndex, newHeaderRow);
                    
                    }
                    lastCatName = HidCatName.Value;
                }
            }

    ผลลัพธ์

              เพียงเท่านี้ท่านก็จะสามารถจัดกลุ่มข้อมูล GridView ของท่าน เพื่อลดปัญหาการดูข้อมูลแบบตารางที่มีแถวข้อมูลจำนวนมากได้บ้างแล้ว ซึ่งผู้เขียนหวังเป็นอย่างยิ่งว่าผู้อ่านจะสามารถนำแนวทาง เกร็ดเล็กๆน้อยๆนี้ไปประยุกต์ใช้กับงานของท่านได้ไม่มากก็น้อยนะคะ หากมีส่วนใดผิดพลาดทางผู้เขียนก็ขออภัยมา ณ ที่นี้ด้วยค่ะ ขอบคุณที่ติดตามนะคะ ^^

    แหล่งอ้างอิง
    https://stackoverflow.com/questions/61773421/sum-column-where-condition-with-datatable

  • Bootstrap Modal Full Screen

    หลายๆ ท่านที่เคยใช้งาน Bootstrap เป็น Frontend Framework น่าจะเคยใช้ modal กันมาบ้าง ซึ่ง modal เป็นจาวาสคลิปต์ปลั๊กอิน มีไว้สำหรับการแสดงผลข้อมูล ทั้งรูปภาพ ข้อความ หรือแบบฟอร์มรับข้อมูล ( html input form ) ในลักษณะป๊อปอัพ ซึ่ง modal ของ bootstrap สามารถแสดงผลได้หลายขนาด ทั้งแบบปกติ แบบเล็ก และแบบใหญ่ ขึ้นอยู่กับ class ที่เราสามารถระบุเพิ่มเข้าไปว่าต้องการให้แสดงผลเป็นแบบไหน

    ตัวอย่างโค้ด modal dialog และปุ่มสำหรับเปิด modal

    <!-- Button trigger modal -->
    <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#myModal">Modal</button>
    
    <!-- Modal -->
    <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
    	<div class="modal-dialog" role="document">
    		<div class="modal-content">
    			<div class="modal-header">
    				<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
    				<h4 class="modal-title" id="myModalLabel">Modal title</h4>
    			</div>
    			<div class="modal-body">
    				...
    			</div>
    
    		</div>
    	</div>
    </div>
    

    จะได้ผลลัพธ์ดังรูป

    ถ้าต้องการให้ modal แสดงผลใหญ่ขึ้นหรือเล็กลง ให้ระบุ modal-lg modal-sm หลัง modal-dialog ดังตัวอย่าง

    <div class="modal-dialog modal-lg" role="document">
    <div class="modal-dialog modal-sm" role="document">
    

    ถ้าเราต้องการให้ modal สามารถแสดงผลแบบเต็มจอ (full screen) จะไม่สามารถทำได้ (อ้างอิงจากเวอร์ชัน 3.3 ที่ผู้เขียนใช้งาน แต่จากการตรวจสอบล่าสุดพบว่าเวอร์ชัน 5.0 สามารถทำได้แล้ว)

    แต่เราจะใช้ความรู้ในเรื่อง css มาปรับแต่งเพิ่มเติมเพื่อให้สามารถแสดงผลได้ตามความต้องการ โดยเราจะต้องเพิ่ม css class ที่ระบุคุณสมบัติที่จำเป็น ดังตัวอย่าง

    <style>
    .fullscreen {
    	margin: 0;
    	top: 0;
    	left: 0;
    	width: 100% !important;
    	height: 100% !important;
    }
    </style>
    

    ระบุ class fullscreen ไว้ด้านหลัง modal-dialog

    <div class="modal-dialog fullscreen" role="document">

    modal จะได้แสดงผลเต็มจอดังรูป

    และเพื่อให้การแสดงผล modal มีความยืดหยุ่นและมีการทำงานใกล้เคียงกับ dialog ของ Windows ทั่วไป คือสามารถขยายให้เป็น full screen และย่อให้กลับมาขนาดเท่าเดิม โดยสิ่งที่เราต้องทำเพิ่มคือ

    1. เพิ่มปุ่ม fullscreen
      • เมื่อ modal แสดงผลอยู่ในสภาพปกติ
      • เมื่อคลิกปุ่ม fullscreen จะเปลี่ยนการแสดงผล modal ให้เป็นแบบ fullscreen
      • ซ่อนปุ่ม fullscreen และแสดงผลปุ่ม restore แทน
    2. เพิ่มปุ่ม restore
      • เมื่อ modal แสดงผลในรูปแบบ fullscreen
      • เมื่อคลิกปุ่ม restore จะเปลี่ยนการแสดงผล modal กลับไปเป็นสภาพปกติ
      • ซ่อนปุ่ม restore และแสดงผลปุ่ม fullscreen แทน

    โดยมีขั้นตอนดังต่อไปนี้

    1. เพิ่มปุ่ม fullscreen และปุ่ม restore ใน div class=”modal header”

    <div class="modal-header">
            //ปุ่ม Close
    	<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
    
            //ปุ่ม restore
    	<button type="button" class="close restore-diloag" title="restore" style="display: none; vertical-align: middle" tabindex="-1"><i class='fa fa-clone' style="font-size: 16px; font-weight: bolder"></i>&nbsp;</button>
    
            //ปุ่ม fullscreen
    	<button type="button" class="close fullscreen-dialog" title="maximize" style="vertical-align: middle" tabindex="-1"><i class='fa fa-window-maximize' style="font-size: 16px"></i>&nbsp;</button>
    	<h4 class="modal-title" id="myModalLabel">Modal title</h4>
    </div>
    

    จะเห็นว่าปุ่มที่เพิ่มขึ้นมาจะมีการระบุชื่อ class “restore-dialog” และ “fullscreen-dialog” ซึ่งจะใช้ในการอ้างถึงในโค้ด jQuery ที่ใช้ควบคุมการทำงานเมื่อมีการคลิกปุ่มนั้นๆ

    2. เนื่องจากผู้เขียนใช้ icon ที่อยู่ใน font awesome ดังนั้นจะต้องเพิ่ม link style sheet เพื่ออ้างไปถึงไฟล์ css ของ font awesome ภายนอกไซต์ที่กำลังพัฒนา (CDN) โดยไปเพิ่มไว้ใน tag <head></head>

    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" />

    3. เพิ่มชุดคำสั่ง jQuery

    <script>
    	$(function () {
    
    		//เมื่อคลิกปุ่ม fullscreen
    		$(document).on('click', '.fullscreen-dialog', function (e) {
    
    			//เพิ่ม class fullscreen เพื่อให้แสดงผลแบบเต็มจอ
    			$('div.modal-dialog').addClass("fullscreen");
    
                            //แสดงปุ่ม restore
    			$('div.modal-dialog').find('.restore-diloag').show();
    
                            //ซ่อนปุ่ม fullscreen
    			$('div.modal-dialog').find('.fullscreen-dialog').hide();
    		});
    
    		//เมื่อคลิกปุ่ม restore
    		$(document).on('click', '.restore-diloag', function (e) {
    
                            //ลบ class fullscreen เพื่อให้กลับไปแสดงผลเหมือนเดิม
    			$('div.modal-dialog').removeClass("fullscreen");
    
                            //ซ่อนปุ่ม restore
    			$('div.modal-dialog').find('.restore-diloag').hide();
    
                            //แสดงปุ่ม fullscreen
    			$('div.modal-dialog').find('.fullscreen-dialog').show();
    		});
    	});
    </script>

    4. ทดสอบ ก็จะได้ผลลัพธ์ดังภาพ

    จะเห็นว่ามีปุ่ม fullscreen แสดงอยู่ เมื่อกด modal จะแสดงผลเป็น fullscreen ดังภาพ

    และเมื่อกดปุ่ม restore หน้าจอ modal ก็จะแสดงผลกลับไปเป็นเหมือนเดิม

    หวังว่าบทความนี้จะเป็นประโยชน์ไม่มากก็น้อย น่าจะพอเป็นแนวทางให้ผู้อ่านนำไปประยุกต์ใช้กับงานของตัวเองนะครับ

    สวัสดีครับ


    แหล่งข้อมูลอ้างอิง

  • จะทำอย่างไรให้สามารถดึงข้อมูลมาแสดงผลด้วย Progress bar โดยใช้ .Net (C#)

              จากบทความก่อนหน้านี้ ผู้เขียนได้แนะนำเกี่ยวกับ Progress bar และวิธีการใช้งานเบื้องต้นกันไปแล้ว ซึ่งใครที่ยังไม่ได้อ่าน สามารถอ่านเพิ่มเติมได้ ที่นี่ มาถึงในบทความนี้ ผู้เขียนจึงขอนำความรู้ดังกล่าวมาต่อยอดประยุกต์ใช้กับการทำงานจริงที่มีการดึงข้อมูลตอน Runtime มาแสดงผลด้วย Progress bar โดยผู้เขียนจะไม่ขอลงรายละเอียดในส่วนของการสร้างสไตล์ชีทและการกำหนดค่าต่างๆแล้ว แต่จะเน้นในส่วนของการดึงข้อมูลมาแสดงแทน โดยผู้เขียนจะพยายามยกตัวอย่างให้เห็นหลายแนว เพื่อให้ผู้อ่านเห็นภาพในการนำไปใช้มากขึ้น ซึ่งแต่ละแบบจะต้องทำอย่างไรบ้าง มาดูกันเลยค่ะ

    แบบแถบละสี

    1. ดึงข้อมูลจากฐานข้อมูล และจัดเตรียม Tag Html ที่จะใช้ในการแสดงผล
        private void getData()
        {
            ////////////////////เป็นการสมมุติการดึงข้อมูลมาใส่ Datatable ที่ชื่อว่า dtProgress ซึ่งเป็นจำนวนของผลไม้แต่ละชนิด
    
            StringBuilder strProgress = new StringBuilder();
            DataTable dtProgress = new DataTable();
            dtProgress.Columns.AddRange(new DataColumn[2] { 
                             new DataColumn("Percent", typeof(int)),
                            new DataColumn("Name",typeof(string))});
    
            dtProgress.Rows.Add(25, "Orange");
            dtProgress.Rows.Add(56, "Grape");
            dtProgress.Rows.Add(45, "Mango");
            dtProgress.Rows.Add(100, "Banana");
          
          ////////////////////เป็นการวนลูปค่าเพื่อสร้างแท็ก html ในการแสดงผลแถบ Progress bar
             int i = 0;
             for ( i = 0; i <= dtProgress.Rows.Count -1; i++) 
             {
             
         ////////////////////เป็นการแสดงชื่อผลไม้แต่ละชนิดบนแถบ Progress bar
                strProgress.Append("<h3 class=\"progress-title\">" + dtProgress.Rows[i]["Name"] + "</h3>");
                strProgress.Append("<div class=\"progress-outer\">");
                strProgress.Append("<div class=\"progress\">");
    
         ////////////////////เป็นการแสดงกำหนดขนาดให้กับแถบสี Progress bar ตามข้อมูล % ในแถวที่วน และมีการ ดึงค่าสไตล์ชีทจากการเรียกใช้ฟังก์ชั่น getCss()
    
    ตามเงื่อนไขของจำนวน % ด้วย
                strProgress.Append("<div class=\"progress-bar progress-bar-striped " + getCss(int.Parse(dtProgress.Rows[i]["Percent"].ToString())) + " \" style=\"width:" + dtProgress.Rows[i]["Percent"] + "%;\"></div>");
    
    
         ////////////////////แสดงจำนวน % ของแต่ละแถบ Progress bar
    
                strProgress.Append("<div class=\"progress-value\"><span>" + dtProgress.Rows[i]["Percent"] + "</span>%</div>");
                strProgress.Append("</div></div>");
             }
    
         ////////////////////นำค่า Tag Html ที่เตรียมไว้ มาแสดงผลด้วย Literal
    
             ltrProgressBar.Text = strProgress.ToString();
    
        }

    2. เมธอดในการแปลงค่าสไตล์ชีทเพื่อปรับสีตามจำนวนที่ส่งมาเป็นพารามิเตอร์

    private string getCss(int Percent)
        {
            string ReturnResult = "";
    
            if (Percent >= 0 && Percent <= 25) 
            {
                ReturnResult = "progress-bar-danger";
            }
            else if (Percent > 25&& Percent <= 50) 
            {
                ReturnResult = "progress-bar-warning"; 
            }
            else if (Percent > 50 && Percent <= 75)
            {
                ReturnResult = "progress-bar-info";
            }
            else if (Percent > 75 && Percent <= 100)
            {
                ReturnResult = "progress-bar-success";
            }
            return ReturnResult;
        }

    จากโค้ดข้างต้นจะเป็นการกำหนดสไตล์ชีทที่จะใช้ ซึ่งจะขึ้นอยู่กับจำนวนค่า % ที่ส่งมาเป็นพารามิเตอร์ โดยจะแบ่งออกเป็น 4 ช่วง

    • สีแดง ช่วงตั้งแต่ 0 – 25 %
    • สีส้ม ช่วงตั้งแต่ 26 – 50 %
    • สีฟ้า ช่วงตั้งแต่ 51 – 75 %
    • สีเขียว ช่วงตั้งแต่ 76 – 100 %

    ตัวอย่างการเรียกใช้งาน

        protected void Page_Load(object sender, EventArgs e)
        {
            getData();
        }

    ผลลัพธ์

    แบบหลายสีในแถบเดียวกัน(แบบที่ 1)

              โดยในตัวอย่างนี้ จะเป็นการแสดงข้อมูลจำนวนผลไม้เป็น % รวมในแถบ Progress bar เดียวกัน ซึ่งจะแยกตามสี และมีบอกจำนวนรวมถึงชื่อผลไม้ให้ทราบ อีกทั้งยังแสดงจำนวนรวมของผลไม้ทุกชนิดด้วย โดยมีวิธีการทำ ดังนี้ค่ะ

    1. ดึงข้อมูลจากฐานข้อมูล และจัดเตรียม Tag Html ที่จะใช้ในการแสดงผล
        private void getMultiFruitColorData()
        {
            StringBuilder strProgress = new StringBuilder();
            DataTable dtProgress = new DataTable();
            dtProgress.Columns.AddRange(new DataColumn[2] { 
                             new DataColumn("Percent", typeof(int)),
                            new DataColumn("Name",typeof(string))});
            dtProgress.Rows.Add(25, "Orange");
            dtProgress.Rows.Add(12, "Grape");
            dtProgress.Rows.Add(7, "Mango");
            dtProgress.Rows.Add(25, "Banana");
    
            int i = 0;
            int percent = 0;
            string CssStr = "";
            int Total = 0;
            
                strProgress.Append("<h3 class=\"progress-title\">Multiple-fruits</h3>");
                strProgress.Append("<div class=\"progress-outer\">");
                strProgress.Append("<div class=\"progress\">");
            for (i = 0; i <= dtProgress.Rows.Count - 1; i++)
            {
                percent = int.Parse(dtProgress.Rows[i]["Percent"].ToString());
    
    
    ////////////////////คำนวณผลรวม % ของผลไม้ทุกชนิดตามการวนรอบที่จะแสดงในแถบ Progress bar
    
                Total += percent;
                switch (i) 
                {
                    case 0: CssStr = "progress-bar-danger"; break;
                    case 1: CssStr = "progress-bar-warning"; break;
                    case 2: CssStr = "progress-bar-info"; break;
                    case 3: CssStr = "progress-bar-success"; break;
                }
    
    ////////////////////เป็นการแสงค่า % ของผลไม้แต่ละชนิดในแถบ Progress bar เดียวกัน โดยแสดงชื่อผลไม้ และจำนวน % ของผลไม้แต่ละชนิดด้วย
    
                strProgress.Append("<div class=\"progress-bar progress-bar-striped " + CssStr + "\" style=\"width:" + percent + "%;\">" + dtProgress.Rows[i]["Name"] + "(" + percent + "%)</div>");
               
            }  
    
     ////////////////////เป็นการแสงผลรวม % ของผลไม้ทุกชนิดในแถบ Progress bar
    
            strProgress.Append("<div class=\"progress-value\"><span>" + Total + "</span>%</div></div>");
            strProgress.Append("</div></div>");
    
            ltrProgressBar.Text = strProgress.ToString();
    
        }
    

              จากโค้ดตัวอย่างข้างต้น จะเห็นว่าการสร้างแท็ก Html จะแตกต่างจากแบบแรก คือจะมีการสร้างใน <div class=\”progress\”> เดียวกัน ซึ่งมีหลักการคล้ายกับการสร้าง Progress bar อย่างง่ายหลายสีในแถบเดียวกันที่เคยกล่าวไว้แล้วในบทความก่อนหน้านั่นเอง

    ผลลัพธ์

    แบบหลายสีในแถบเดียวกันและแสดงหลายแถบ Progress Bar(แบบที่ 2)

              ในตัวอย่างนี้ เป็นการแสดงผลแถบสีแยกตามช่วงของข้อมูลบน Progress bar แต่ละแถบ โดยการแสดงผลจะแบ่งสีตามปริมาณข้อมูลในแต่ละช่วง ดังนี้

    สีแดง ช่วงตั้งแต่ 0 – 25 % สีส้ม ช่วงตั้งแต่ 26 – 50 % สีฟ้า ช่วงตั้งแต่ 51 – 75 % สีเขียว ช่วงตั้งแต่ 76 – 100 %

    หากข้อมูลที่ต้องการแสดงผลตกอยู่ในช่วงใดก็จะมีการแสดงแถบสีนั้นขึ้นมาให้เห็นตามลำดับ โดยมีวิธีการทำต่อไปนี้ค่ะ

    1. ดึงข้อมูลจากฐานข้อมูล และจัดเตรียม Tag Html ที่จะใช้ในการแสดงผล
        private void getMultiColorData()
        {
            StringBuilder strProgress = new StringBuilder();
            DataTable dtProgress = new DataTable();
            dtProgress.Columns.AddRange(new DataColumn[2] { 
                             new DataColumn("Percent", typeof(int)),
                            new DataColumn("Name",typeof(string))});
    
            dtProgress.Rows.Add(25, "Orange");
            dtProgress.Rows.Add(56, "Grape");
            dtProgress.Rows.Add(45, "Mango");
            dtProgress.Rows.Add(100, "Banana");
    
            int i = 0;
            int j = 0;
            int maxRange = 0;
            int percent = 0;
            for (i = 0; i <= dtProgress.Rows.Count - 1; i++)
            {
                percent= int.Parse(dtProgress.Rows[i]["Percent"].ToString());
    
    
    ////////////////////วนเพื่อสร้าง Progress bar ใหม่ในผลไม้แต่ละชนิด
    
                strProgress.Append("<h3 class=\"progress-title\">" + dtProgress.Rows[i]["Name"] + "</h3>");
                strProgress.Append("<div class=\"progress-outer\">");
                strProgress.Append("<div class=\"progress\">");
                maxRange = 0;
    
    ////////////////////คำนวณหาว่าค่าของ % ตกอยู่ในช่วงใด 1-4(เนื่องจากแบ่งออกเป็นช่วงละ 25 % และรวมเป็น 100%)
    
    
                if (percent >= 0 && percent <= 25)
                {
                    maxRange = 1;
                }
                else if (percent > 25 && percent <= 50)
                {
                    maxRange = 2;
                }
                else if (percent > 50 && percent <= 75)
                {
                    maxRange = 3;
                }
                else if (percent > 75 && percent <= 100)
                {
                    maxRange = 4;
                }
    
    ////////////////////วนลูปเพื่อแสดงผลสีในแต่ละช่วงบน Progress bar โดยมีการเรียกใช้งานเมธอด getCssRange()
    
                for (j = 1; j <= maxRange; j++)
                {
                    strProgress.Append(getCssRange(percent, j, maxRange));
                 }
                strProgress.Append("<div class=\"progress-value\"><span>" + dtProgress.Rows[i]["Percent"] + "</span>%</div></div>");
                strProgress.Append("</div></div>");
            }
    
            ltrProgressBar.Text = strProgress.ToString();
    
        }

    2. สร้างเมธอดที่ใช้ในการสร้างแท็ก Html ในการแสดงผลแถบสี ซึ่งในส่วนของกระบวนการคำนวณแถบสีจะไม่ขอลงในรายละเอียด แต่แสดงไว้ให้เห็นภาพการทำงานหลักๆเท่านั้น ดังนี้ค่ะ

        private string getCssRange(int Percent,int Range,int MaxRange)
        {
            string ReturnResult = "";
    
            switch (Range)
            {
                case 1:
                    if (MaxRange > Range)
                        ReturnResult = "<div class=\"progress-bar progress-bar-striped progress-bar-danger\" style=\"width:25%;\"></div>";
    
                    else 
                    {
                        Percent = Percent >= 25 ? 25 : Percent - (25 * (Range - 1));
                    ReturnResult = "<div class=\"progress-bar progress-bar-striped progress-bar-danger\" style=\"width:" + Percent + "%;\"></div>";
                    }
                    break;
                case 2:
                    if (MaxRange > Range)
                        ReturnResult = "<div class=\"progress-bar progress-bar-striped progress-bar-warning\" style=\"width:25%;\"></div>";
    
                    else
                    {
                        Percent = Percent >= 50 ? 50 : Percent - (25 * (Range - 1));
                        ReturnResult = "<div class=\"progress-bar progress-bar-striped progress-bar-warning\" style=\"width:" + Percent + "%;\"></div>";
                    }
                   
                    break;
    
                case 3:
                    if (MaxRange > Range)
                        ReturnResult = "<div class=\"progress-bar progress-bar-striped progress-bar-info\" style=\"width:25%;\"></div>";
    
                    else
                    {
                        Percent = Percent >= 75 ? 75 : Percent- (25* (Range-1));
                        ReturnResult = "<div class=\"progress-bar progress-bar-striped progress-bar-info\" style=\"width:" + Percent + "%;\"></div>";
                    }
                     break;
    
                case 4:
    
                     if (Percent==100)
                         ReturnResult = "<div class=\"progress-bar progress-bar-striped progress-bar-success\" style=\"width:25%;\"></div>";
                     else
                     {
                         Percent =   Percent - (25 * (Range - 1));
                         ReturnResult = "<div class=\"progress-bar progress-bar-striped progress-bar-success \" style=\"width:" + Percent + "%;\"></div>";
                     }
                      break;
    
            }
             return ReturnResult;
        }
    

    ผลลัพธ์

    หมายเหตุ : จากตัวอย่างข้างต้น เป็นเพียงการยกตัวอย่างเพื่อให้ผู้อ่านเข้าใจการประยุกต์ใช้งาน และการทำงานร่วมกับ Progress bar เพิ่มมากขึ้นเท่านั้น ซึ่งผู้อ่านสามารถดัดแปลง และมีวิธีการรวมถึงเทคนิคที่แตกต่างออกไปที่จะนำมาใช้ในการพัฒนาเพื่อแสดงผลข้อมูล ขึ้นกับอัลกอริธึมของแต่ละท่าน

              จากบทความนี้จะเห็นได้ว่า วิธีการดึงข้อมูลมาแสดงผลด้วย Progress bar แบบนี้ จะมีความยืดหยุ่นกว่าแบบที่มีการกำหนดค่าตายตัวในบทความก่อนหน้า อีกทั้งยังสามารถแสดงจำนวนแถบ Progress bar และสีได้ไม่จำกัด ขึ้นอยู่กับข้อมูล การกำหนดเงื่อนไขในการแสดงผล และความต้องการของผู้พัฒนาที่จะดึงข้อมูลมาแสดงผลนั่นเอง และผู้เขียนหวังเป็นอย่างยิ่งว่า บทความนี้ จะพอเป็นแนวทางในการนำไปใช้งานจริงให้กับทุกท่านได้นะคะ ขอบคุณค่ะ ^^

    แหล่งข้อมูลอ้างอิง :

    https://bestjquery.com/tutorial/progress-bar/demo78/

    https://www.jquery-az.com/boots/demo.php?ex=51.0_5

  • วิธีสร้างตาราง HTML Table จาก CSV File

    โจทย์คือ ต้องสร้างระบบ เพื่อให้ผู้ใช้ สร้าง โครงสร้างแบบฟอร์ม ด้วย Excel แล้วต้องแปลงให้เป็น Web Page ซึ่ง จะต้องการตกแต่งด้วย Bootstrap

    หน้าตาต้นแบบเป็นแบบนี้ (ในที่นี้ใช้ Google Sheets แต่ก็ทำวิธีการเดียวกันกับ MS Excel)

    ถ้า ใช้วิธี Save As เป็น HTML ตรง ๆ จะได้หน้าตาประมาณนี้

    ซึ่งจะยุ่งยากมาก ในการจัดการ และการควบคุมการแสดงผล

    วิธีการคือ !!!

    1 Save As เป็น CSV

    2 เปิดเว็บ
    http://www.convertcsv.com/csv-to-html.htm

    3 แล้ว Upload ไฟล์ หรือจะ Copy Paste ก็ได้

    4 ก็จะได้ plain HTML

    5 เอาไปประกอบกับ Bootstrap ได้สบาย (ในภาพ ใช้ django template tag ด้วย เลยได้ widget ตามที่กำหนด)

    หวังว่าจะเป็นประโยชน์ครับ

  • ddready – แพ็ครวม django + bootstrap4 + crispy form + docker พร้อมใช้งาน

    สำหรับใครที่อยากจะลองพัฒนา Web Application ด้วย django web framework ผมได้รวบรวมเป็นชุดเริ่มต้น ซึ่งจะสามารถสร้าง Responsive Web และ มีแบบฟอร์มที่สวยงามด้วย crispy form มาเรียบร้อย ใช้งานได้ทั้ง แบบ Python บนเครื่อง และ แบบ Docker ลองทำตามดูได้ครับ

    ต้นแบบ มี Bootstrap 4 พร้อมใช้งาน
    มี Login Form มาให้เลย
    เข้ามาในส่วนของ Profile และ Logout ได้

    Repository

    สามารถเปิด URL ต่อไปนี้ เพื่อไป Download หรือ จะใช้ git clone ก็ได้

    https://github.com/nagarindkx/ddready.git

    https://gitlab.psu.ac.th/kanakorn.h/ddready.git

    จากนั้น ให้เปิด cmd ไปยังตำแหน่งที่ clone ลงมา

    สำหรับผู้ที่ติดตั้ง python อยู่แล้ว

    แล้วใช้คำสั่งต่อไปนี้

    pip install -r requirements.txt
    cd code
    cd main
    waitress-serve --listen *:8080 main.wsgi:application

    สำหรับผู้ที่จะใช้ Docker

    บน Windows ใช้คำสั่งต่อไปนี้

    set PROJECTNAME="projectname"
    set GCP-PROJECT-ID="gcp-project-id"
    set SERVICE="service"
    docker build --rm -f "Dockerfile" -t %PROJECTNAME%:dev .
    docker run -d -v %CD%\code:/code -p 8080:8080 --name %PROJECTNAME% %PROJECTNAME%:dev
    docker exec -it %PROJECTNAME% /bin/sh -c "[ -e /bin/bash ] && /bin/bash || /bin/sh"

    ทดสอบใช้งาน

    http://localhost:8080

    ในตอนต่อไป จะแนะนำวิธีการสร้าง แบบสอบถาม ทดแทนการใช้ Google Forms ครับ

  • Download multiple files as Zip Archive (Compressed) file in ASP.Net MVC

    ในบทความนี้จะขอกล่าวถึงการ  download file ครั้งละหลายๆไฟล์ โดยมีการระบุว่าต้องการ   download file ใดบ้าง โดย  ผู้ใช้สามารถกดเลือกได้ทั้งละหลายๆไฟล์ หรือไฟล์เดียว แล้วแต่ความต้องการของผู้ใช้งาน โดยหลังจากที่มีการกดปุ่มแล้วระบบจะทำการ zip ไฟล์รวมเป็น 1 ไฟล์ โดยในบทความนี้จะขอเสนอวิธีการพัฒนาโดยใช้ ASP.NET  ในรูปแบบ MVC ค่ะ

     

     

    ในส่วนของ java script

    function DownloadFiles() {

    var items = [];

    $(“input:checkbox[name=chkThis]:checked”).each(function () {

    items.push($(this).val());

     

    });

     

    if (items.length <= 0) {

    alert(‘กรุณาเลือกข้อมูลที่ต้องการ Download ด้วยค่ะ/ครับ!!’);

    }

    else {

    $.ajax({

    type: ‘POST’,

    contentType: ‘application/json; charset=utf-8’,

    url: ‘@Url.Action(“DownloadAndZipFile”, “NoteUpload”)’,

    data: JSON.stringify({ fileItemsAll: items }),

    success: function (resultDownload) {

    //window.location = ‘/NoteUpload/Download?fileGuid=’ + resultDownload.FileGuid

    //                   + ‘&filename=’ + resultDownload.FileName;

    window.location = ‘/NoteUpload/Download?fileGuid=’ + resultDownload.FileGuid;

    },

    error: function (data) {

    alert(data);

    }

    });

    }

    //   return items;

    }

    ในส่วนของ controller

    public ActionResult DownloadAndZipFile(IEnumerable<int> fileItemsAll)

    {

    if (!(ViewBag.IsAuthorized = (azResult = azService.Authorize(AccountingOperation.NoteUploadReader, this).Result).IsAuthorize))

    {

    Danger(string.Format(“ไม่มีสิทธิ์ใช้งานในส่วนนี้: {0} ({1})”, azResult.Operation.OP_NAME, Convert.ToString(azResult.OperationEnum)));

    return View();

    }

     

    string handle = Guid.NewGuid().ToString();

    MemoryStream output = new MemoryStream();

     

    var zipMs = new MemoryStream();

    string strZipName = “Accounting” + DateTime.Now.ToString(“yyyyMMdd”);

     

    TempData[handle] = fileItemsAll;

     

    var resultDownload = new { FileGuid = handle, FileName = strZipName + “.zip” };

    return this.Json(resultDownload, JsonRequestBehavior.AllowGet);

    }

     

    public FileResult Download(string fileGuid)

    {

    if (!(ViewBag.IsAuthorized = (azResult = azService.Authorize(AccountingOperation.NoteUploadReader, this).Result).IsAuthorize))

    {

    Danger(string.Format(“ไม่มีสิทธิ์ใช้งานในส่วนนี้: {0} ({1})”, azResult.Operation.OP_NAME, Convert.ToString(azResult.OperationEnum)));

    return null;

    }

     

    var zipMs = new MemoryStream();

    string zipFisYear, zipPeriod, fileC, FileF, FileFinance, fileID, fileNameZip = null;

     

    if (TempData[fileGuid] != null)

    {

    using (ZipFile zip = new ZipFile())

    {

    foreach (var item in TempData[fileGuid] as IEnumerable<int>)

    {

    var dataItemSelect = db.NoteUploadViews.Where(g => g.ID == item).FirstOrDefault();

    var financeID = db.FinanceBudgets.Where(g => g.FINANCE_BUDGET_ID == dataItemSelect.FINANCE_BUDGET_ID).FirstOrDefault();

    zipFisYear = dataItemSelect.FISCAL_YEAR_ID.ToString();

    zipPeriod = dataItemSelect.QUARTER.ToString();

     

    var dataORG = db.Organizations.Where(g => g.ORG_ID == dataItemSelect.ORG_ID && g.FISCAL_YEAR_ID == dataItemSelect.FISCAL_YEAR_ID).FirstOrDefault();

    fileC = Right(“000” + dataORG.ORG_ID, 3);

    FileF = Right(“000” + dataItemSelect.ORG_ID, 3);

    FileFinance = Right(“000” + financeID.FINANCE_ID.ToString(), 3);

    zipFisYear = Right(“0000” + dataItemSelect.FISCAL_YEAR_ID.ToString(), 4);

    zipPeriod = dataItemSelect.QUARTER.ToString();

    fileNameZip = zipFisYear + zipPeriod + “.zip”; //JAR+ Format for Zip name = FiscalYear|PeriodID >> 25601

     

    var fileType = dataItemSelect.FILE_TYPE;

    var fileNameInzip = fileC + FileF + FileFinance + dataItemSelect.ID.ToString() + “.” + fileType;  //JAR+  Format for  File Name = Campus|Fac|Finance|ID >> C01F010031

     

    byte[] fileDatas = (byte[])dataItemSelect.FILE_DATA;

     

    zip.AddEntry(fileNameInzip, fileDatas);

    }

     

    zip.Save(zipMs);

     

    byte[] fileData = zipMs.GetBuffer();

    zipMs.Seek(0, SeekOrigin.Begin);

     

    zipMs.Flush();

    Response.Clear();

    Response.AddHeader(“content-disposition”, “attachment;filename=” + fileNameZip);

    Response.BinaryWrite(fileData);

    Response.End();

    }

    }

     

    return File(zipMs, “application/zip”);

    }

    จากตัวอย่างข้างต้นจะเห็นว่าในปัจจุบัน มีความต้องการไม่น้อยที่จะ download ไฟล์หลายๆไฟล์ แล้วรวมเป็นไฟล์เดียว เพื่อความสะดวกในการจัดเก็บข้อมูล และวิธีการพัฒนาก็ไม่ได้ยุ่งยาก ผู้เขียนจึงหวังเป็นอย่างยิ่งว่าจะเป็นประโยชน์ไม่มากกว่าน้อยสำหรับผู้พัฒนาในรูปแบบ MVC นะคะ

     

     

     แหล่งอ้างอิง

    https://www.aspsnippets.com/Articles/Download-multiple-files-as-Zip-Archive-Compressed-file-in-ASPNet-MVC.aspx

    https://www.carlrippon.com/zipping-up-files-from-a-memorystream/