Author: wunwisa.c

  • Create Excel 2007 file using Interop services.

    Microsoft Office Interop (Excel Automation)คือ ตัวเลือกอีกทางหนึ่งที่ใช้สำหรับ สร้าง อ่าน เอกสาร Excel (XLS, XLSX, CSV) จากการพัฒนาโดยใช้   C# หรือ VB.NET แต่บทความนี้จะขอยกตัวอย่างการพัฒนาเพื่อสร้างเอกสารโดยใช้ C# ค่ะ

    Step 1 

    • เปิด Visual Studio
    • File>New>Project
    • เลือก Visual C#> Windows > Select Console Application

    Step

    • เลือก >> Project Menu >> เลือก Add References >> เลือกที่ Tab COM >> กด “Microsoft Excel 12.0 Object Library”  เพื่อเพิ่ม References
    รูปตัวอย่างการ Add References

    Step

    ตัวอย่าง Code C# เพื่อใช้ในการสร้าง Excel File ดังนี้

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    using Microsoft.Office.Interop;
    using Microsoft.Office.Interop.Excel;
     
    namespace ConsoleApplication1
    {
        class Class1
        {
            public static void Main(string[] ar)
            {
                Application ExcelApp = new Application();
                Workbook ExcelWorkBook = null;
                Worksheet ExcelWorkSheet = null;
     
                ExcelApp.Visible = true;
                ExcelWorkBook = ExcelApp.Workbooks.Add(XlWBATemplate.xlWBATWorksheet);
                // ExcelWorkBook.Worksheets.Add(); //Adding New Sheet in Excel Workbook
                try
                {
                    ExcelWorkSheet = ExcelWorkBook.Worksheets[1]; // Compulsory Line in which sheet you want to write data
                    //Writing data into excel of 100 rows with 10 column 
                    for (int r = 1; r < 101; r++) //r stands for ExcelRow and c for ExcelColumn
                    {
                        // Excel row and column start positions for writing Row=1 and Col=1
                        for (int c = 1; c < 11; c++)
                            ExcelWorkSheet.Cells[r, c] = "R" + r + "C" + c;
                    }
                    ExcelWorkBook.Worksheets[1].Name = "MySheet";//Renaming the Sheet1 to MySheet
                    ExcelWorkBook.SaveAs("d:\\Testing.xlsx");
                    ExcelWorkBook.Close();
                    ExcelApp.Quit();
                    Marshal.ReleaseComObject(ExcelWorkSheet);
                    Marshal.ReleaseComObject(ExcelWorkBook);
                    Marshal.ReleaseComObject(ExcelApp);
                }
                catch (Exception exHandle)
                {
                    Console.WriteLine("Exception: " + exHandle.Message);
                    Console.ReadLine();
                }
                finally
                {
     
                    foreach (Process process in Process.GetProcessesByName("Excel"))
                        process.Kill();
                }
             }
        }
    }
    

    เมื่อมีการ Run โปรแกรมแล้ว Excel file ก็จะถูกสร้างตาม path ที่ระบุ ค่ะ

     แหล่งอ้างอิง https://www.c-sharpcorner.com/blogs/creating-excel-file-using-interop-services1

  • WPF Layout (Part III)

    The WPF Viewbox

    WPF viewbox จะสามารถปรับขนาด/ปรับมิติของ control ของ Windows Presentation Foundation ได้โดยอัตโนมัติ  viewbox และสามารถปรับขนาดของ control เพื่อเติมเต็มพื้นที่ที่มีอยู่ คือจะมีการปรับขนาดของ child ซึ่งเป็น control ที่บรรจุอยู่ใน ViewBox ให้อัตโนมัติ ซึ่งจะมีการปรับพิกัดโดยอัตโนมัติเมื่อมีการปรับเปลี่ยนขนาดเพื่อให้ชุดของพิกัดที่กำหนดเป็นพิกเซลในขนาดดั้งเดิม

    อะไรคือจุดประสงค์ของ WPF Viewbox? เมื่อไหร่ที่คุณจะใช้มัน?

    เมื่อคุณพัฒนาโปรแกรม โดยทั่วไปแล้วคุณจะไม่สามารถรู้ถึงความแน่นอนของ hardware ของระบบที่จะเป็นไป โดยเฉพาะอย่างยิ่งหากแอปพลิเคชันมีไว้เพื่อการใช้งานในระยะยาว คุณไม่สามารถมั่นใจได้เลย   ไม่ว่าจะเป็นขนาดหรือความละเอียดของจอภาพที่แสดงผลการใช้งานของระบบต่างๆ เพราะปีที่แล้วหน้าจออาจจะเป็น ultra-high-resolution ปีต่อมาอาจจะเป็น  low-resolution  monitor  และถ้าคุณได้พัฒนาโปรแกรมโดยคำนึงถึงความละเอียดของหน้าจอของปีที่แล้วไว้ เมื่อมาดูในปีถัดมาในส่วนของหน้าต่างโปรแกรม ปุ่ม และ elements อื่นๆของ GUI ที่ได้พัฒนาไว้ อาจจะมีขนาดที่เล็กมาก จนทำให้เกือบจะใช้ไม่ได้เลย

    Resize!

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

    จากตัวอย่าง XAML code จะสร้าง Viewbox กับ button  ที่เมื่อมีการปรับขนาดของ button  ให้อัตโนมัติเมื่อมีการปรับขนาดของ Viewbox window

     <Window x:Class="ViewboxTest.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="Viewbox Test"
            SizeToContent="WidthAndHeight" Height="75" Width="250">
        <Viewbox>
            <Button Content="I'm a Button" Width="150"/>
        </Viewbox>
    </Window>

    What About the Margins?

    จะน่าสนใจขนาดไหนถ้าcontrol ที่อยู่ใน Viewbox  window ก็จะถูกปรับขนาดโดยอัตโนมัติ และจะเกิดอะไรขึ้นเมื่อ window ไม่ได้ถูกปรับขนาดแค่เพียงอย่างเดียวเท่านั้นแต่จะปรับในส่วนของมิติให้กับ control ที่ไม่พอดีกับ window ไปด้วย ?  Viewbox มี 2 properties  คือ Stretch และ StretchDirection ที่จะมาจัดการในส่วนนี้ให้คุณได้

    Stretching it a Bit

    จากโค้ดตัวอย่างข้างต้น เมื่อ complied เป็น   EXE  เราจะเห็นโปรแกรม “Viewbox Test” ที่มีข้อความ “I’m a Button” อยู่บนปุ่ม และเมื่อคุณปรับขนาดของ window ปุ่มก็จะถูกปรับขนาดและอัตราส่วนให้พอดีโดยอัตโนมัติ ทำให้ปุ่มมีขนาดของสัดส่วนที่พอดีสวยงามไม่ว่าจะมีการปรับให้มีขนาดใดก็ตาม ในกรณีที่อัตราส่วนของภาพใน view box แสดงขนาดไม่พอดีกับปุ่ม อาจจะเกิดจากการมีที่ช่องว่างทั้งด้านบนและด้านล่างปุ่มหรือทางซ้ายและขวา

    The Default

    กรณีที่ไม่ได้กำหนดค่าของ Stretch property  จะมีค่า default ของ Viewbox  ที่กำหนดในส่วนของ Stretch property คือ “Uniform” ดังตัวอย่าง

        <Viewbox>
            <Button Content="I'm a Button" Width="150"/>
        </Viewbox>

    จากตัวอย่างข้างต้นจะมีความหมายเหมือนกับตัวอย่างข้างล่าง

        <Viewbox Stretch="Uniform">
            <Button Content="I'm a Button" Width="150"/>
        </Viewbox>

    Stretch property จะประกอบด้วย 4 ตัวเลือก  ดังนี้

    1. No Stretch At All

    กรณีที่ไม่ต้องการให้มีการปรับขนาดของ child  control จำเป็นที่จะต้องกำหนดค่า  Stretch property เป็น None ดังตัวอย่าง

       <Viewbox Stretch="None">
            <Button Content="I'm a Button" Width="150"/>
        </Viewbox>

    เมื่อมีการกำหนดกำหนดค่า  Stretch property เป็น None ปุ่มจะคงขนาดเดิมไว้ โดยจะไม่เกิดอะไรขึ้นไม่ว่าคุณจะปรับขนาดหรือมิติของ Viewbox window  กรณีถ้ามีการเพิ่มขนาดของ window ปุ่มจะคงขนาดเดิมแต่มีช่องว่างรอบๆปุ่มแทน ส่วนถ้าย่อขนาดของ window ปุ่มก็จะคงขนาดเดิมเช่นกันแต่อาจจะถูก window บังปุ่มจนมองไม่เห็นทั้งปุ่ม หรือสุดท้ายเมื่อย่อขนาดลงเรื่อยๆจนทำให้เรามองไม่เห็นปุ่มเลย

    2.Free-Form

    เมื่อกำหนดค่า Stretch=”Fill”  จะทำให้ child control ถูกปรับขนาดให้พอดีกับ  Viewbox  โดยไม่มีการตัดของเนื้อหาใดๆออกเลย คือจะแสดงผลอย่างสมบูรณ์ ในส่วนของการปรับขนาดในแนวตั้งและแนวนอนอาจจะแตกต่างกัน ซึ่งอาจจะทำให้เนื้อหาใน Viewbox ผิดเพี้ยนไป เนื่องจากจะถูกปรับเปลี่ยนอัตราส่วนเป็นขนาดตาม window ดังตัวอย่าง

        <Viewbox Stretch="Fill">
            <Button Content="I'm a Button" Width="150"/>
        </Viewbox>
    3.Uniform Adjustment

    Uniform option จะเหมือนกับ Free-Form ที่อธิบายข้างบน และเป็นค่า default ของ Stretch  เนื่องจากตัวลูกของ control จะรักษาขนาดและอัตราส่วนเดิมไว้ มันจะไม่ถูกบิดเบือนขนาดเมื่อมีการปรับขนาด ข้อเสียคือเมื่ออัตราส่วนของ View box มีขนาดไม่ตรงกันกับ control อาจจะทำให้มีช่องว่างรอบๆ control ได้ ดังตัวอย่าง

      <Viewbox Stretch="Uniform">
            <Button Content="I'm a Button" Width="150"/>
      </Viewbox>
    4.Uniform and Fill

    กรณีที่ต้องการใช้ทั้งแบบ Uniform และ Fill เราสามารถกำหนดให้  Stretch=”UniformToFill” ดังตัวอย่าง

        <Viewbox Stretch="UniformToFill">
            <Button Content="I'm a Button" Width="150"/>
        </Viewbox>

    ในส่วน UniformToFill  ที่คล้ายกับ Uniform คือ จะรักษาสัดส่วนต้นแบบของ control ไว้  แต่ในส่วนที่เหมือนกับ Fill คือ UniformToFill จะปรับให้เท่ากับขนาดของ view box  อย่างสมบูรณ์ แต่เนื่องจากจำเป็นต้องรักษาสัดส่วนเดิมไว้และมีการปรับขนาดและมิติตามขนาดของ Viewbox  ไว้ด้วย จะทำให้บางส่วนของ control ถูกตัดออกเพื่อให้มีขนาดพอดีกับสัดส่วนของปลายทาง

    StretchDirection Property ประกอบด้วย 3 ตัวเลือก ดังนี้

    1. UpOnly

    กรณีที่มีการกำหนด StretchDirection=”UpOnly” จะทำให้ ถ้า Viewbox มีขนาดใหญ่กว่า size ของ child control จะส่งผลให้ child control มีขนาดใหญ่ไปด้วย แต่ถ้ามีการลดขนาด child control จะไม่ลดขนาดตาม จึงทำให้การแสดงผลบางส่วนถูกตัดออกไป ดังตัวอย่าง

     <Viewbox Stretch="Uniform" StretchDirection="UpOnly">
            <Button Content="I'm a Button" Width="150"/>
     </Viewbox>
    2. DownOnly

    Viewbox  จะอนุญาตให้ย่อขนาดลงให้น้อยกว่า default ของขนาดเดิม โดยการย่อขนาดลงจะส่งผลให้มีพื้นที่ว่างรอบๆ child control ดังตัวอย่าง

     <Viewbox Stretch="Uniform" StretchDirection="DownOnly">
            <Button Content="I'm a Button" Width="150"/>
     </Viewbox>

    3. Both

    จะเป็นค่า default ของ StretchDirection Property โดย Viewbox จะอนุญาตให้สามารถย่อและขยายขนาดของ child control ได้

     แหล่งอ้างอิง
    https://www.wpftutorial.net

  • WPF Layout (Part II)

    Popular layout panels of WPF

    1. StackPanel

    ภาพตัวอย่าง StackPanel

    StackPanel ของ WPF เป็น panel ที่เรียบง่ายและมีประโยชน์ในการนำมาใช้งาน โดยรูปแบบการจัดวางของ element ที่เป็น child จะวางอยู่ในรูปแบบซ้อนจากบนลงล่างหรือเรียงไปทางด้านซ้ายไปขวา ขึ้นกับการกำหนดค่าของ orientation StackPanel เหมาะสำหรับการจัดวางในรูปแบบที่มีลักษณะเป็น List โดย ItemsControls ของ WPF ทั้งหมด ที่เป็นลักษณะของ ComboBox, ListBox หรือ Menu จะใช้ StackPanel เพื่อจัดรูปแบบภายในของ Control ตัวมันเอง ดังตัวอย่าง

    <StackPanel>
      <TextBlock Margin="10" FontSize="20">How do you like your coffee?</TextBlock>
      <Button Margin="10">Black</Button>
      <Button Margin="10">With milk</Button>
      <Button Margin="10">Latte machiato</Button>
      <Button Margin="10">Chappuchino</Button>
    </StackPanel>

    Stack Items horizontally

    จากภาพตัวอย่างจะแสดงในส่วนของการใช้ horizontal stack panel คือจะประกอบด้วย ปุ่ม “OK” และ ปุ่ม “Cancel” ใน window โดยเมื่อมีการเปลี่ยนขนาดของตัวอักษรหรือเปลี่ยนภาษา โดยที่เราไม่จำเป็นต้องกำหนดขนาดของปุ่ม แต่ stack panel จะทำหน้าที่ในการจัดเรียงปุ่มตามขนาดของปุ่มทั้งสองให้โดยอัตโนมัติ ดังตัวอย่าง

    ภาพตัวอย่าง Stack Items horizontally
    <StackPanel Margin="8" Orientation="Horizontal">            
       <Button MinWidth="93">OK</Button>
       <Button MinWidth="93" Margin="10,0,0,0">Cancel</Button>
    </StackPanel>
    

    2. Wrap Panel

    ภาพตัวอย่าง Wrap Panel

    Wrap panel เหมือนกับ Stack Panel แต่ต่างกันที่ Wrap panel คือจะไม่บรรจุ child ทั้งหมดใน 1 แถว แต่ตัดส่วนที่เกินและขึ้นบรรทัดใหม่ กรณีที่ไม่มีพื้นที่ว่างเหลือพอบรรจุอยู่ โดยทิศทางการจัดวางสามารถกำหนดเป็น Horizontal หรือ Vertical
    Wrap panel เคยใช้จัดวาง tabs ของ tabs control, itemของเมนูใน toolbar หรือ item ใน Windows Explorer ในรูปแบบ list โดย Wrap panel จะถูกนำมาใช้บ่อยกับitem ที่มีขนาดเท่ากัน แต่ในส่วนนี้ไม่ใช่ข้อกำหนดของ Wrap panel

    <WrapPanel Orientation="Horizontal">
        <Button Content="Button" />
        <Button Content="Button" />
        <Button Content="Button" />
        <Button Content="Button" />
        <Button Content="Button" />
    </WrapPanel>
    

    3.  Dock Panel

    Dock panel คือรูปแบบในการจัดวางเรียง elements ของ panel นั้นๆ ได้ 4 ด้าน คือ  ซ้าย ขวา บน และ ล่าง หรือจะจัดเรียงไว้ตรงกลางก็สามารถทำได้ โดยแต่ละทิศทางการจัดเรียงจะสามารถกำหนดได้นั้นต้องระบุที่ DockPanel.Dock  กรณีที่ต้องการจัดวางเรียงให้อยู่ตรงกลางของ panel จำเป็นต้องให้ child ที่ต้องการจัดเรียงไว้ตรงกลางนั้นจัดวางอยู่ในตำแหน่งสุดท้าย แล้วต้องกำหนด panel  นั้น ให้ มี LastChildFill property = true ดังตัวอย่าง

    ภาพตัวอย่าง Dock Panel
    <DockPanel LastChildFill="True">
        <Button Content="Dock=Top" DockPanel.Dock="Top"/>
        <Button Content="Dock=Bottom" DockPanel.Dock="Bottom"/>
        <Button Content="Dock=Left"/>
        <Button Content="Dock=Right" DockPanel.Dock="Right"/>
        <Button Content="LastChildFill=True"/>
    </DockPanel>
    

    Multiple elements on one side

    Dock panel รองรับการมี elements ได้หลายตัวในแต่ละด้าน โดยแต่ละ element ต้องระบุ DockPanel.Dock ให้ตรงกับทิศทางที่ต้องการจัดเรียง ดังตัวอย่าง

    ภาพตัวอย่าง Multiple elements on one side
    <DockPanel LastChildFill="True">
        <Button Content="Dock=Left"/>
        <Button Content="Dock=Left"/>
        <Button Content="Dock=Top" DockPanel.Dock="Top"/>
        <Button Content="Dock=Bottom" DockPanel.Dock="Bottom"/>
        <Button Content="Dock=Right" DockPanel.Dock="Right"/>
        <Button Content="LastChildFill=True"/>
    </DockPanel>
    

    Change the stacking order

    ลำดับการจัดเรียงพื้นที่ให้กับ element คือ มันจะกำหนดพื้นที่การจัดเรียงคือ elements ตัวแรก จะได้พื้นที่ทั้งหมดตามความยาวหรือความสูงของตัวมันเอง ในส่วนของ elements อื่นๆจะได้พื้นที่เหลือจาก elements ตัวแรก และ elements ตัวอื่นๆที่จัดวางเรียงต่อๆกัน

    4. Canvas Panel

    Canvas คือ panel ที่มีรูปแบบพื้นฐานที่ธรรมดาที่สุดใน WPF  โดย elementภายใน panel   จะมีพิกัดที่ชัดเจน โดยสามารถระบุพิกัดที่ต้องการจัดเรียงด้านต่างๆโดยใช้  Canvas.Left, Canvas.Top, Canvas.Bottom และ Canvas.Right โดยระบุใน properties ของ element นั้นๆ

    Canvas เป็น Panel ที่ใช้สำหรับจัดกลุ่ม elements ที่เป็น 2D แต่จะไม่ใช้สำหรับจัดรูปแบบของ UI โดยส่วนนี้จะสำคัญมากเนื่องจากการกำหนดพิกัดที่แน่นอนจะทำให้ยุ่งยากในกรณีที่เราต้องการเปลี่ยนขนาด ขยายขนาด หรือจำกัด application ผู้พัฒนาที่เคยพัฒนาในส่วนของ Winforms จะคุ้นเคยกับการจัดวางรูปแบบแบบนี้ แต่มันไม่ใช่วิธีปฏิบัติที่ดีใน WPF โดยมีการกำหนดการจัดวางตำแหน่งตามตัวอย่าง

    <Canvas>
        <Rectangle Canvas.Left="40" Canvas.Top="31" Width="63" Height="41" Fill="Blue"  />
        <Ellipse Canvas.Left="130" Canvas.Top="79" Width="58" Height="58" Fill="Blue"  />
        <Path Canvas.Left="61" Canvas.Top="28" Width="133" Height="98" Fill="Blue" 
              Stretch="Fill" Data="M61,125 L193,28"/>
    </Canvas>
    

    Override the Z-Order of Elements

    โดยปกติ Z-Order ของ elements ใน canvas จะถูกระบุลำดับใน XAML แต่เราสามารถ override ของ  Z-Order ได้โดยการกำหนด Canvas.ZIndex  ของ  element ดังตัวอย่าง

    ภาพตัวอย่าง Override the Z-Order of Elements
    <Canvas>
        <Ellipse Fill="Green" Width="60" Height="60" Canvas.Left="30" Canvas.Top="20"    Canvas.ZIndex="1"/>
        <Ellipse Fill="Blue"  Width="60" Height="60" Canvas.Left="60" Canvas.Top="40"/>
    </Canvas>
    
    

     แหล่งอ้างอิง
    https://www.wpftutorial.net

  • WPF Layout (Part I)

    Introduction to WPF Layout

    การอออกแบบ controls  คือส่วนสำคัญของการออกแบบโปรแกรมที่นำมาใช้งาน การจัดวาง controls ที่มีการกำหนดค่าขนาดของ pixel ของ controls นั้นๆ อาจจะใช้งานได้ดีในกรณีสภาพแวดล้อมที่ถูกจำกัด แต่ในขณะเดียวกันจะได้ผลลัพธ์ที่ไม่พึงประสงค์นักหากไปแสดงบนหน้าจอที่มีความละเอียดของหน้าจอหรือขนาดของตัวอักษรที่ต่างกันออกไป WPF จึงมีการนำ panel มาแก้ไขปัญหาในส่วนนี้  โดย panel ที่ใช้ในการจัดวาง Layout มีอยู่ 5 ชนิดที่นิยมใช้การการออกแบบดังนี้

    • Grid Panel
    • Stack Panel
    • Dock Panel
    • Wrap Panel
    • Canvas Panel

    Best Practices

    1. หลีกเลี่ยงการกำหนดค่าตำแหน่ง : กรณีที่ต้องการกำหนดตำแหน่งการวาง controls ใน panel ควรใช้ Alignment properties ร่วมกับ Margin เพื่อกำหนดค่าตำแหน่งใน panel แทนการกำหนดเป็นค่าคงที่ในตำแหน่งนั้นๆ
    2. หลีกเลี่ยงการกำหนดขนาด : ควรกำหนด Width และ Height ของ element ให้เป็น Auto
    3. ไม่ใช้ canvas panel ในการจัดวาง layout ของ element ใช้ canvas panel กรณีที่เป็น vector graphic เท่านั้น
    4. ควรใช้ Stack panel เพื่อจัดเรียงปุ่ม
    5. ใช้ grid panel ในกับ form ที่เป็น form สำหรับการรับข้อมูลเข้า โดย control ที่อยู่ใน grid panel ที่เป็น label ควรกำหนดขนาดเป็น Auto และ controlที่เป็น Textbox ควรกำหนดขนาดเป็น *
    6. ใช้ itemControl ใน DataTemplate ของ grid panel เพื่อใช้ในการจัดวาง control ที่แสดงข้อมูลที่มีการเปลี่ยนแปลงของ keyตลอดเวลา และควรใช้ SharedSize เพื่อให้ label มีขนาดความกว้างเท่ากัน

    Vertical and Horizontal Alignment

    ใช้ VerticalAlignment และ HorizontalAlignmant properties เพื่อกำหนด  Control ให้อยู่ในส่วนเดียวกันหรือหลายๆส่วนใน panel   นั้นๆ  โดยมีตัวอย่างดังภาพ

    https://www.wpftutorial.net/images/v2_alignment.png

    Margin and Padding

    Margin และ Padding properties จะจองพื้นที่ช่องว่างรอบๆของcontrol ดังนี้
    • The Margin คือ ส่วนของช่องว่างรอบๆของ control.
    • The Padding คือส่วนของช่องว่างที่อยู่ภายใน control.
    • The Padding ของ control ที่อยู่ภายนอก คือ Margin ของ control ที่อยู่ภายใน Control นั้นๆ

    Height and Width

    ไม่แนะนำให้กำหนดค่า Height และ Width เนื่องจากจะทำให้ยุ่งยากในการจัดการ  การจัดการที่ดีควรจะกำหนดค่า  MinHeight, MaxHeight, MinWidth และ MaxWidth ตามต้องการ  หรือหากต้องการกำหนด width หรือ height เป็น Auto ขนาดของ controls ก็จะมีขนาดเท่ากับของข้อมูลโดยอัตโนมัติ

    Overflow Handling

    Clipping

    ในกรณีที่ elementsซ้อนทับ border ของ panel เราจะกำหนดการแสดงการจัดวางของ control โดยใช้ ClipToBounds property โดยมีกำหนดค่าเป็น  true หรือ false  ดังรูป

    https://www.wpftutorial.net/images/cliptobounds.PNG

    Scrolling

    กรณีที่เนื้อหาของข้อมูลมีจำนวนมากเกินกว่าขนาดของ panel ที่จะสามารถแสดงให้เห็นได้ทั้งหมดใน panel นั้นๆ คุณสามารถนำความสามารถของ ScrollViewer มาใช้แก้ปัญหาตรงจุดนี้ได้ โดยประกอบด้วยสองส่วนคือ  vertical ScrollbarVisibility  และ horizontal ScrollbarVisibility properties ดังตัวอย่าง

    <ScrollViewer>    
    <StackPanel>
    <Button Content="First Item" />
    <Button Content="Second Item" />
    <Button Content="Third Item" />
    </StackPanel>
    </ScrollViewer>

     แหล่งอ้างอิง
    https://www.wpftutorial.net/LayoutProperties.html

  • 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/

  • เขียน CSS Selectors อย่างไรให้มีประสิทธิภาพ ?

    ในการเขียน css selectors นั้น เรารู้ดีว่ามี selectors แบบไหนให้เราเลือกใช้ได้บ้าง และแต่ละแบบนั้นมีวิธีการใช้งานอย่างไร แต่หลายๆ คน อาจไม่รู้ว่าการเขียน selectors ในแต่ละแบบนั้น มันมีผลต่อ performance ด้วย

     Web Browsers แบ่ง CSS Rules ออกเป็นกลุ่มๆ

    เรามาทำความเข้าใจถึงหลักการทำงานของ web browsers กันก่อน ไม่ว่าเราจะเขียน css rules อย่างไร web browsers จะแบ่ง rules ของเราออกเป็น 4 กลุ่ม ด้วยกัน ดังนี้

    • IDใช้เวลาในการหาน้อยที่สุด
    • Classใช้เวลาในการหามากกว่า ID เล็กน้อย
    • Tagใช้เวลาในการหามากกว่า Class
    • Universalใช้เวลาในการหามาก

    ซึ่งการจะดูว่า rule นี้ถูกจัดให้อยู่ในกลุ่มไหนนั้นจะดูจาก “key selector” หรือ selector ที่อยู่ขวาสุดนั่นเอง

     กลุ่ม ID

    rule ที่ถูกจัดให้อยู่ในกลุ่มนี้จะต้องมี id selector เป็น key selector

    #latest-news { }
    ul#latest-news { }
    #latest-news[title=”Latest News”] { }
    section > div > ul#latest-news:first-child { }

    จากโค้ดด้านบน จะได้ว่า rules ทั้ง 4 แบบ ถูกจัดให้อยู่ในกลุ่ม id ทั้งหมด

     กลุ่ม Class

    rule ที่ถูกจัดให้อยู่ในกลุ่มนี้จะต้องมี class selector เป็น key selector

    .list-item { }
    #latest-news .list-item { }
    div ul li.list-item { }
    ul > .list-item:hover { }

    จากโค้ดด้านบน จะได้ว่า rules ทั้ง 4 แบบ ถูกจัดให้อยู่ในกลุ่ม class ทั้งหมด

     กลุ่ม Tag

    ถ้า key selector ของ rule นั้นไม่ใช่ทั้ง id selector และ class selector ให้ดูว่ามีการระบุ tag เอาไว้หรือไม่ ถ้ามี จะถูกจัดให้อยู่ในกลุ่ม tag

    a { }
    li > a { }
    #latest-news div[class*=”post”] { }
    ul#latest-news li.list-item a:hover { }

    จากโค้ดด้านบน จะได้ว่า rules ทั้ง 4 แบบ ถูกจัดให้อยู่ในกลุ่ม tag ทั้งหมด

     กลุ่ม Universal

    หาก rule นั้นไม่ตรงกับ 3 กลุ่มข้างต้นเลย จะถูกจัดให้อยู่ในกลุ่ม universal

    * { }
    [id*=”post”] { }
    .home [class*=”entry”] { }
    form > [type=”text”] { }

    จากโค้ดด้านบน จะได้ว่า rules ทั้ง 4 แบบ ถูกจัดให้อยู่ในกลุ่ม universal ทั้งหมด

     วิธีการอ่าน CSS Rules ของ Web Browsers

    ในการดูว่า CSS Rule นี้ จะถูก apply ให้กับ html elements ใดบ้าง web browsers จะต้องทำการ “match” โดยมันจะอ่าน selectors จากขวาไปซ้าย คือจะเริ่มจาก key selector ซึ่งอยู่ทางด้านขวาสุดก่อน web browsers จะดูว่ามี elements ใดที่ match กับ selector ตัวนี้บ้าง ถ้าไม่มีเลย ก็จะหยุดไป แต่ถ้ามี มันก็จะทำการเช็คต่อโดยการเลื่อนไปยัง selector ทางซ้ายมือทีละอันๆ แล้วดูว่า elements ที่ match มาก่อนหน้านี้นั้น ยังคง match กับ selector ตัวใหม่นี้อีกหรือไม่ ถ้ายังมีอีกก็จะเลื่อนไปยัง selector ตัวต่อไปทางซ้ายอีก มันจะทำเช่นนี้ต่อไปเรื่อยๆ จนกว่าจะครบทุกๆ selectors ใน rule นั้นๆ หรือจนกว่าจะไม่เจอ elements ใดๆ ที่ตรงกับ rule นั้นๆ เลย

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

    การเขียน css rules ก็เหมือนกับการกำหนดวิธีการจำแนกเด็กนักเรียนให้กับครู การใส่ id ให้กับ element เปรียบเหมือนการเขียนป้ายชื่อของเด็กนักเรียนคนนั้นแล้วเอามาห้อยคอ ทีนี้เวลาครูจะหา ด.ช. เอ สิ่งที่ครูต้องทำก็แค่มองหาเด็กที่มีป้ายชื่อห้อยคอก่อนว่ามีใครบ้าง พอเจอแล้วก็ดูต่อว่าป้ายนั้นเขียนไว้ว่า ด.ช. เอ ใช่หรือไม่ การหา element จาก id ของ web browsers นั้นจะใช้เวลาน้อยมาก เนื่องจาก id มีได้แค่ที่เดียวเท่านั้น

    การกำหนด class ให้กับ elements ต่างๆ จะเหมือนกับการแบ่งเด็กนักเรียนออกเป็นกลุ่มๆ หากเรากำหนด class ให้กับ elements แล้วล่ะก็ web browsers จะสามารถหาตัว elements เจอได้ง่ายเหมือนกับการที่ครูมองหาเฉพาะเด็กที่แต่งชุดลูกเสือที่มีอยู่ในห้องเรียน แค่มองปราดเดียวก็ดูออกแล้ว

    ส่วนการเขียน css rule โดยใช้ tag เป็น key selector นั้นจะเหมือนกับการมองสิ่งที่เด็กนักเรียน “เป็น” วิธีนี้ web browsers จะมองหาตัว elements เหมือนกับการที่ครูมองหาเฉพาะเด็กนักเรียนที่เป็นผู้ชาย มันอาจใช้เวลาเพิ่มขึ้นนิดหน่อย แต่ก็ไม่นานมากนัก

    และสุดท้ายคือการเขียน css rule ที่อยู่ในกลุ่ม universal วิธีนี้ก็เหมือนกับการที่ครูต้องการจะหาเด็กมาเข้าร่วมการประกวดร้องเพลงในโรงเรียน เขาไม่สามารถดูภายนอกแล้วรู้ได้ทันทีว่าใครร้องเพลงเพราะบ้าง เขาจำเป็นต้องไล่ดูไปทีละคนๆ เราจะเห็นว่าวิธีนี้นั้นมี candidates เยอะมาก

    ขั้นตอนการหา elements ของ web browsers นี้เองที่จะมีเรื่องของ performance เข้ามาเกี่ยวข้อง ยิ่ง rule นั้นซับซ้อนมากเท่าไร ยิ่งใช้เวลาในการ match มากขึ้นเท่านั้น จากการทดลองของ Steve Souders เขาได้เรียงลำดับประสิทธิภาพของ selectors ในแบบต่างๆ เอาไว้ดังนี้

    1. ID – #header
    2. Class – .post
    3. Tag – div
    4. Sibling – h1 + ph1 ~ p
    5. Child – ul > li
    6. Descendant – ul li
    7. Universal – *
    8. Attribute – [type="text"]
    9. Pseudo-classes, Pseudo-elements – a:hoverp::first-letter

    จากการจัดอันดับประสิทธิภาพของ selectors ด้านบน เราจะเห็นว่า selectors ในแต่ละแบบนั้น มีประสิทธิภาพที่ต่างกัน โดย id นั้นจะใช้เวลาในการ match น้อยที่สุด ส่วน pseudo-classes และ pseudo-elements จะใช้เวลาในการ match มากที่สุด และอย่าลืมว่า web browsers นั้นอ่าน selectors จากขวาไปซ้ายทีละตัวๆ ยิ่งเขียน selectors ยาวมากเท่าไร ประสิทธิภาพก็ยิ่งลดลงมากเท่านั้น

     เขียน Selectors อย่างไรให้มีประสิทธิภาพ ?

    หลังจากที่เราเข้าใจวิธีการอ่าน css rules ของ web browsers เราจึงได้รู้ว่าการเลือกใช้ selectors นั้นมีความสำคัญ ในการเขียน selectors ให้เรายึดหลักต่อไปนี้

      อย่าเขียน Selectors เกินความจำเป็น

    อย่างที่ทุกคนทราบกันดีว่า id นั้นมีได้เพียงที่เดียวในหนึ่งหน้า ฉะนั้นหากเราจะใช้ id selector แล้ว เราไม่ควรใส่อะไรเพิ่มเข้าไปอีก

    BAD

    div#latest-news { }
    .box#latest-news { }
    .sidebar #latest-news { }

    นี่เป็นตัวอย่างการใช้ id selector ที่แย่ ในตัวอย่างแรก web browsers ทั้งหลายจะมองหา elements ที่มี id ชื่อว่า “latest-news” ก่อน เมื่อเจอแล้วก็จะดูต่อว่า element นั้นเป็น div หรือไม่ ซึ่งจริงๆ แล้ว มันไม่จำเป็นต้องเช็คอีก เพราะ id นั้นมีได้เพียงที่เดียวเท่านั้น เช่นเดียวกับตัวอย่างที่ 2 ที่จะต้องเช็คต่อว่า element นั้นมี class “box” อยู่ด้วยหรือไม่ และในตัวอย่างที่ 3 ที่แย่สุดเลยก็ว่าได้ เนื่องจากมันต้องไปไล่เช็ค ancestors ทุกตัวของ element นี้ ว่ามีตัวที่มี class “sidebar” บ้างหรือไม่

    GOOD

    #latest-news{ }

    ในทำนองเดียวกัน เราก็ไม่จำเป็นต้องใส่อะไรหน้า class อีก เพราะมันจะทำให้เสียเวลาในการ match เพิ่มโดยไม่จำเป็น

    BAD

    p.red { }

    GOOD

    .red { }

    อย่างไรก็ตาม การใส่อะไรข้างหน้า class บางทีก็มีประโยชน์เหมือนกันในเรื่องของ semantic การใช้ p.red มีข้อดีตรงมันช่วยให้เรารู้ว่า css rule นี้มีไว้เพื่อทำให้ตัวอักษรใน paragraph เป็นสีแดง หรือบางทีเราอาจจะต้องการกำหนดสไตล์สำหรับ class “red” เฉพาะที่ p element เพียงอย่างเดียวเท่านั้น

     ลดการใช้ Descendant Selector

    การใช้ descendant selector นั้นทำให้ประสิทธิภาพลดลงอย่างมาก เราจึงควรหลีกเลี่ยง

    BAD

    ul#latest-news li a {  }      /* แย่มาก */
    ul#latest-news > li > a {  }  /* ดีขึ้น แต่ก็ยังแย่อยู่ดี */

    จากตัวอย่างนี้ web browsers จะมองหา key selector ซึ่งก็คือ a ก่อน แล้วค่อยเลื่อนต่อไปทางซ้ายมือ โดยดูว่า a นั้นเป็น descendant ของ li หรือไม่ ถ้าเป็นให้ดูต่อว่า li นั้นเป็น descendant ของ #latest-news หรือไม่ และถ้ายังเป็นอีกให้ดูว่า #latest-news นั้นเป็น ul หรือไม่ การเขียน selector แบบนี้ส่งผลเสียต่อ performance เป็นอย่างมาก

    GOOD

    .latest-news-link {  }

    วิธีแก้ง่ายๆ คือการเพิ่ม class “latest-news-link” ให้กับ a ทีนี้ web browsers ก็จะสามารถหา elements นั้นๆ เจอได้อย่างรวดเร็ว แต่การเขียนแบบนี้ก็มีข้อเสียตรงที่มันจะทำให้การ maintenance ลำบากขึ้น เพราะเราอาจดูไม่ออกว่า .latest-news-link นี้เป็น link ที่อยู่ใน ul#latest-news

     ลดการใช้ CSS Rules ที่อยู่ในกลุ่ม Universal

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

    BAD

    [type=”text”] { }           /* แย่มาก */
    [class~=”input-text”] { }   /* แย่มากๆ เพราะไม่จำเป็น */

    GOOD

    input[type=”text”] { }      /* ดีขึ้นมาก */
    .input-text { }             /* ดีที่สุด */

    จะเห็นว่า css rules 2 แบบแรกนั้น ถูกจัดให้อยู่ในกลุ่ม universal เพราะใน key selector ไม่มี id, class หรือ tag เลย ทำให้ web browsers จะต้องไปไล่เช็คกับ elements ทุกตัวในหน้านั้นๆ การเขียน css rules 2 แบบหลัง เป็นวิธีที่ดีกว่า

     มันจะช้าลงสักแค่ไหนกันเชียว ?

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

    เรื่อง performance ของ css selectors นี้อาจจะเห็นผลไม่ค่อยชัดนัก เนื่องจากความเร็วของการประมวลผลในสมัยนี้นั้นเรียกได้ว่าเร็วเอามากๆ สมมติว่าเราเลือกใช้ selectors โดยเน้นไปที่ performance เป็นหลัก เมื่อลอง render ดู เราพบว่ามันใช้เวลาเพียงแค่ 400ms แต่พอเราเลือกใช้ selectors แบบตามใจตัวเอง เมื่อลอง render ดูใหม่เราอาจจะพบว่ามันใช้เวลาเพิ่มเป็น 600ms จะเห็นว่าส่วนต่างขนาด 150% นั้น ฟังดูเยอะก็จริง แต่ผู้ใช้งานนั้น อาจจะสัมผัสความต่างเพียงแค่ 200ms นี้ไม่ได้

    อย่างไรก็ตาม การเลือกใช้ selectors โดยคำนึงถึง performance นี้ จะเห็นผลได้ชัดมากขึ้นหากในหน้านั้นๆ มี html elements และ css rules อยู่เป็นจำนวนมาก นอกจากนี้ หากเว็บของเราเป็นเว็บที่เน้นในเรื่องของ performance เป็นหลักแล้วล่ะก็ การคำนึงถึง performance ของ selectors คงจะเป็นเรื่องที่มองข้ามไม่ได้

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

     ความพอดี

    การคำนึงถึง performance นั้นไม่ได้แปลว่าห้ามใช้ selectors นอกเหนือจาก id, class และ tag แต่อย่างใด แต่มันหมายถึงการเขียน selectors แบบคุ้มค่า ไม่ฟุ่มเฟือย selectors แบบอื่นๆ นั้นใช้ได้ แต่ควรใช้เมื่อจำเป็นจริงๆ เท่านั้น

    บางคนอาจจะสงสัยว่า หากเราต้องการเขียน css rules ให้ render หน้าเว็บได้เร็วสุดๆ เราก็ต้องกำหนด class ให้กับ html elements ทั้งหมดในหน้านั้นเลยงั้นหรือ ? คำตอบคือ “ไม่ใช่”

    จริงอยู่ที่การทำอย่างนั้น ดูเหมือนจะทำให้ web browsers หา elements ที่ต้องการได้เร็วขึ้น แต่การใส่ class มาก “เกินไป” นั้นก็มีข้อเสียอยู่เหมือนกัน

    หากใส่ class ให้ทุกๆ elements เลย ขนาดของโค้ด html จะต้องใหญ่ขึ้นอย่างแน่นอน ซึ่งส่งผลให้ปริมาณไฟล์ที่เราต้องดาวน์โหลดมานั้นมากขึ้นตามไปด้วย การดาวน์โหลดไฟล์ส่วนต่างที่เพิ่มขึ้นมานี้ อาจใช้เวลามากกว่าเวลาที่ลดลงไปจากผลของ performance ที่ดีขึ้นเสียอีก

    นอกจากนี้ การใส่ class ให้กับทุกๆ elements อาจไม่ได้ทำให้หน้าเว็บ render ได้เร็วอย่างที่เราคิด สมมติในห้องเรียนห้องหนึ่ง มีเด็กนักเรียนนั่งอยู่เต็มไปหมด ผู้ปกครองต้องการพบลูกของเขา จึงบอกลักษณะกับครูไปว่าเด็กคนนั้นใช้กระเป๋าหนังยี่ห้อ Jacob สิ่งที่ครูต้องทำคือมองหาเด็กที่ 1. มีกระเป๋าหนัง 2. กระเป๋าหนังนั้นมียี่ห้อ Jacob หากในห้องนั้นมีเด็กเพียงไม่กี่คนที่มีกระเป๋าหนัง ครูก็จะสามารถหาเด็กที่มีกระเป๋าหนังยี่ห้อ Jacob ได้อย่างง่ายดาย ในทางกลับกัน หากเด็กในห้องนั้นมีกระเป๋าหนังกันทุกคน เท่ากับว่าครูต้องไล่ดูยี่ห้อของกระเป๋าหนังไปทีละคนๆ จนครบทั้งห้อง บางคนอาจสงสัยว่าทำไมครูไม่ตะโกนถามเด็กๆ ล่ะว่า ใครมีกระเป๋าหนังยี่ห้อ Jacob บ้าง จะได้ไม่ต้องมาเสียเวลาหา คำตอบคือ html นั้นพูดไม่ได้

     บทสรุปการเลือกใช้ CSS Selectors

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

     

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

    http://www.siamhtml.com/css-selectors-performance/

  • Uploading Files into Database with ASP.NET MVC

    การ    upload  file มีหลายรูปแบบ ไม่ว่าจะเป็นการ upload file  แบบ copy ไว้บน server หรือจะเป็นการบันทึกลงในฐานข้อมูลเลยโดยตรง วันนี้จะขอนำเสนอในส่วนของการ upload   file  และบันทึกลงฐานข้อมูล     โดยใช้ ASP.NET MVC  ดังนี้

     

    กำหนดไฟล์ที่ต้องการ upload ให้มีรูปแบบดังนี้

          public class UploadModel

    {

    [Required]

    public HttpPostedFileBase File { get; set; }

    }

     

    ในส่วนของ    View

     

    <form id=”uploader” enctype=”multipart/form-data” method=”POST”>

    <a type=”submit” href=”#” onclick=”uploadConfirm();” class=”btn btn-info”><span class=”glyphicon glyphicon-save”></span>&nbsp;Upload</a>

    </form>

    ในส่วนของ java script (สำหรับเรียก Controller)

      function uploadConfirm() {

    $.ajax({

    type: ‘POST’,

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

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

    data: “{ ‘periodID’:’” + $(‘#selectedlistPeriods’).val() +

    “‘ ,’financeID’:’” + $(‘#selectedlistFinance’).val() +

    “‘ }”,

    success: function (resultSave) {

     

    },

    error: function (data) {

    alert(data);

    }

    });

    }

     

    ในส่วนของ Controller

    public async Task<ActionResult> UploadFile(UploadModel model, FormCollection form)

    {

     

    string fileName = Path.GetFileName(model.File.FileName); //แสดงชื่อไฟล์

    string strFileName = Path.GetFileNameWithoutExtension(fileName); //แสดงชื่อไฟล์

    string contentType = model.File.ContentType;  //แสดงนามสกุลไฟล์

     

    string FileExtension = fileName.Substring(fileName.LastIndexOf(‘.’) + 1).ToLower();

    using (Stream fs = model.File.InputStream) //

    {

    using (BinaryReader br = new BinaryReader(fs))

    {

    byte[] bytes = br.ReadBytes((Int32)fs.Length);

     

    //TO DO:  Code ในส่วนที่ต้องการ insert ข้อมูลลง Database

    }

    }

     

    return RedirectToAction(“Index”);

    }

     

    จากตัวอย่างข้างต้น จะเป็นการ upload file ลงฐานข้อมูลโดยตรงในส่วนของรูปแบบการพัฒนาแบบ MVC ผู้เขียนหวังว่าอาจจะเป็นอีกทางเลือกหนึ่งของผู้พัฒนา ในการนำไปพัฒนาโปรแกรมต่อไปนะคะ ^_^

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

    https://stackoverflow.com/questions/15106190/uploading-files-into-database-with-asp-net-mvc

    https://stackoverflow.com/questions/21677038/mvc-upload-file-with-model-second-parameter-posted-file-is-null/21677156

  • GET STRING FROM ENUM

    เพื่อนๆนักพัฒนาหลายคนก็คงจะรู้จัก enum กันพอสมควรแล้วนะคะ วันนี้เรามาทำความรู้จักเจ้า Enum กันให้มากขึ้นกว่าเดิมกันดีกว่านะคะ ว่านอกจากเราจะดึงค่า Integer ที่เก็บค่าในตัวแปร หรือ Tostring() เป็นค่าstring ตามชื่อของตัวแปร Enum แล้ว เรายังสามารถ ดึงค่า  String  เป็นประโยคยาวๆได้โดยที่ไม่จำเป็นต้องเหมือนกับชื่อตัวแปรแล้วนะคะ เอาล่ะค่ะ เรามาเริ่มจากการทำความรู้จักเจ้าตัว Enum กันตั้งแต่เริ่มต้นเพื่อระลึกความจำกันก่อนละกันนะคะ ^^

    Enumeration จะมีชนิดเป็น integer type ซึ่ง เราสามารถกำหนดกลุ่มของข้อมูลได้ (User-defined)

    ในการสร้าง enumeration เราจะใช้ keyword คำว่า  enum  ดังนี้

    public enum Colors
    {
      White,
      Black,
      Red,
      Green,
      Blue
    }

    ในตัวอย่างนี้สร้าง enum ที่ชื่อว่า Colors โดยมีสมาชิกอยู่ 5 สมาชิกคือ White,Black,Red,Green,Blue

    โดยสมาชิกต่างๆของ enum ถ้าเราไม่ได้กำหนดค่าเริ่มต้นจะมีค่าเริ่มต้นจาก 0 ดังนี้

    white = 0

    Black =1

    Red =2

    Green=3

    Blue=4

    ในการเรียกใช้งาน enum

    Colors  c = Colors.Green; //สร้างตัวแปร c ขึ้นมาเพื่อเรียกใช้สมาชิกที่ชื่อ Green

    ตัวอย่าง enum

     

    using System;
    public enum Colors
    {
        White,
        Black,
        Red,
        Green,
        Blue
    }
    public class TestEnum
    {
        public static void Main()
        {
            Colors c = Colors.Blue;
            Console.WriteLine(c);
            Console.ReadLine();
        }
    }

    ผลลัพธ์จะแสดง Blue

    จากตัวอย่างนี้ถ้าต้องการให้แสดงเป็น integer type   เราก็เขียนได้ดังนี้

    Console.WriteLine((int)c);

    ผลลัพธ์จะได้ 4

    เราสามารถกำหนดค่าให้กับสมาชิกของ  enum ได้ดังนี้

     

    public enum Colors
    {
      Red = 10,
      Green =20,
      Blue=30
    }

    ตัวอย่าง

     

    using System;
    public enum Colors
    {
      Red = 10,
      Green =20,
      Blue=30
    }
    public class TestEnum
    {
        public static void Main()
        {
            Colors c = Colors.Green;
            Console.WriteLine((int)c);
            Console.ReadLine();
        }
    }

    ผลลัพธ์จะได้ 20
    นอกจากนี้สามารถเขียนรับค่า value มาจาก enum ได้อีกแบบคือ
    Colors c = (Colors)Enum.Parse(typeof(Colors), “Blue”, false);
    ตัวอย่าง

    using System;
    public enum Colors
    {
      Red = 10,
      Green =20,
      Blue=30
    }
    public class TestEnum
    {
        public static void Main()
        {
            Colors c = (Colors)Enum.Parse(typeof(Colors), “Blue”, false);
            Console.WriteLine((int)c);
            Console.ReadLine();
        }
    }

    ผลลัพธ์จะได้ 30

    กรณีที่เราจะใช้ tostring เข้ามาใช้ก็สามารถทำได้
    ตัวอย่าง

     

    using System;

    public enum Colors
    {
      Red = 10,
      Green =20,
      Blue=30
    }
    public class TestEnum
    {
        public static void Main()
        {
            string  c = Colors.Green.tostring();
            Console.WriteLine(c);
            Console.ReadLine();
        }
    }

    ผลลัพธ์จะได้ Green
    นอกจากเราจะสามารถ tostring ให้ได้ค่า string เหมือนกับชื่อ enum แล้วนั้น ผู้พัฒนาทราบหรือไม่คะ ว่า enum เราสามารถกำหนดคำอธิบายได้เป็นประโยค และสามารถดึงค่านั้นมาใช้ได้ หรือพูดง่ายๆว่าเราสามารถดึงค่า enum มาใช้ในรูปแบบประโยคได้ง่ายๆ ดังตัวอย่างค่ะ

    public enum Colors
    {[Description(“This is Red”)]  //เราต้องกำหนด Description attributes ว่าเราต้องการจะให้แสดงข้อความอย่างไร
      Red = 10,   Green =20,
      Blue=30
    }

    ส่วนนี้จะเป็น code ส่วนที่จะดึงข้อมูล Description ตามที่กำหนด ที enum

    public static string GetEnumDescription(Enum value)

    {   

    FieldInfo fi = value.GetType().GetField(value.ToString());    

    DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(        typeof(DescriptionAttribute),        false);    

    if (attributes != null &&        attributes.Length > 0)       

    return attributes[0].Description;   

    else        return value.ToString();

    }

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

    var myEnumDescriptions = Colors.Red.GetDescription();

    ผลลัพธ์จะได้ This is Red

    ผู้เขียนหวังว่านี่คงจะเป็นอีกทางเลือกนึงของผู้พัฒนา ในการนำไปพัฒนาโปรแกรมต่อไปนะคะ ^_^

    แหล่งอ้างอิง
    https://stackoverflow.com/questions/2650080/how-to-get-c-sharp-enum-description-from-value
    http://yaritc.blogspot.com/2012/01/enum-struct-by-paedotnet.html

  • Unlimited multi level menu in mvc

    สวัสดีค่ะวันนี้เรามาว่ากันเรื่องเมนูกันดีกว่านะคะ คำว่าเมนู ผู้เขียนคิดว่าทุกคนต้องรู้จักแน่นอน เพราะในการพัฒนาแต่ละระบบนั้นส่วนใหญ่จะมีส่วนของงานหลายๆส่วน ทำให้มีการออกแบบหน้าจอการใช้งานหลายหน้าจอเพื่อรองรับการทำงานของระบบนั้น เมื่อส่งมอบระบบ แน่นอน!! ค่ะ ความต้องการของลูกค้าไม่หยุดแค่นั้นแน่นอน เมื่อความต้องการเพิ่ม การทำงานของหน้าจอก็เพิ่ม เมนูก็ต้องเพิ่มตามมาเช่นกัน ทำให้ผู้พัฒนาต้องไปแก้โค้ดในส่วนของเมนูทุกครั้งที่มีการเพิ่มเมนู การจัดหมวดหมูของเมนู หรือต้องการปรับเปลี่ยน path ที่ไปเรียกหน้าจอนั้นๆ แค่คิดก็ดูยุ่งยากต่อการจัดการแล้ว วันนี้ผู้เขียนจึงนำวิธีการออกแบบและพัฒนาในส่วนของเมนูที่ผู้เขียนได้ใช้พัฒนาใน MVC มาเป็นตัวอย่างให้ดูกันนะคะ สำหรับผู้อ่านที่ไม่ได้ใช้ MVC ก็สามารถนำไปปรับเปลี่ยนได้ค่ะ หลังจากพัฒนาแล้วระบบก็จะสามารถ เพิ่มเมนู หรือเปลี่ยน path ของเมนู หรือ จัดการกลุ่มของเมนู ได้โดยไม่ต้อง publish ระบบ ทุกครั้งที่มีการแก้ไข ทำให้ง่ายและไม่เสียเวลาในการจัดการเลยค่ะ เรามาเริ่มกันเลยค่ะ ผู้เขียนขอแบ่งการพัฒนาเป็นสองส่วนนะคะ คือ
    1.การพัฒนาในฐานข้อมูล : ในส่วนของการออกแบบฐานข้อมูลผู้อ่านต้องออกแบบให้มีการเก็บ id ของ parent เพื่อระบุให้รู้ว่าเมนูตัวนี้เป็นลูกของเมนูตัวไหน

    ตัวอย่างการออกแบบฐานข้อมูลของเมนู

    คอลัมภ์ คำอธิบาย
     ParentID Id ของ parent
    MenuID Id ของเมนู
    MenuName ชื่อของเมนู

    2.การพัฒนาระบบ : เป็นการพัฒนาระบบผู้เขียนจะใช้วิธีการพัฒนาแบบใช้ Recursive เข้ามาจัดการในส่วนของการแสดงเมนู โดยข้อดีของ Recursive คือเราสามารถจัดการได้ทั้งส่วนที่มีเมนูย่อย และไม่มีเมนูย่อย

    ตัวอย่างในส่วนของ Recursive ที่ใช้ใน View เพื่อการแสดงเมนู ( MVC)

    @helper GetSubMenus(IEnumerable<menutable> siteMenu, Nullable<int> parentID)

    {

        foreach (var i in Model.Where(a => a.ParentID.Equals(parentID)))

        {

            var submenu = Model.Where(a => a.ParentID.Equals(i.MenuID)).Count();

     

            <li class=”@(submenu > 0 ? “dropdown-submenu” : “dropdown”)”>

                <a href=”@(!string.IsNullOrEmpty(i.MenuLink) ? Url.Content(i.MenuLink) : “~/default)” style=”font-size:16px;”>@i.MenuName</a>

                @if (submenu > 0)

                {

                    <ul class=”dropdown-menu”>

                        @GetSubMenus(siteMenu, i.MenuID)

                        @* Recursive  Call for Populate Sub items here*@

                    </ul>

                }

            </li>

        }

    }

     

    @{

        var mymenu = @Model;

        var menuParentID = mymenu.First().ParentID;

    @if (mymenu != null && mymenu.Count() > 0)

    {

        <nav class=”navbar navbar-default”>

            <div class=”container-fluid”>

                <div class=”collapse navbar-collapse” id=”bs-example-navbar-collapse-1″>

                    <ul class=” nav navbar-nav”>

                        @GetSubMenus(mymenu, menuParentID)

                    </ul>

                </div>

            </div>

        </nav>

    }

    ตัวอย่างหน้าจอของเมนูที่เรียกใช้ Recursive เพื่อจัดการเมนู

    จากรูปจะเป็นตัวอย่างของเมนูที่มีเมนูย่อย โดยมีเมนูหลักเป็น Parent ในรูป คือ testsub2 โดยมีลูก(Child) เป็น jar2 และ jar และ jar ก็เป็น Parent โดยมี testSub เป็นลูก(Child) อีกครั้ง จะเห็นว่าไม่ว่าจะต้องการให้มีเมนูย่อยแค่ไหน Recursive ก็สามารถทำเราง่ายในการจัดการมากขึ้น โดยไม่ต้องมา copy code วางหลายๆครั้งให้ยุ่งยากอีกต่อไปค่ะ และที่สำคัญในตัวอย่างเราจะดึงข้อมูลเมนูมาจากฐานข้อมูล ไม่ว่าเราจะเพิ่มเมนู หรือเปลี่ยน path หรือเพิ่มเมนูย่อยในเมนูหลักลงลึกแค่ไหน เราก็ไม่ต้องมาเสียเวลานั่งแก้โค้ดในหน้าของเมนูอีกต่อไปแล้วนะคะ ผู้อ่านท่านใดมีการจัดเมนูหลายเมนู ลองนำไปปรับใช้กันดูนะคะ ขอบคุณค่ะ^^

     

     

     

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

    https://stackoverflow.com/questions/35326302/unlimited-multi-level-menu-in-mvc