Month: July 2015

  • ASP.NET MVC Part2: เริ่มต้นสร้างเว็บด้วย MVC with Bootstrap

    สวัสดีค่ะ จากบทความที่แล้ว ASP.NET MVC Part 1 : ทำความรู้จักกับ ASP.NET MVC ได้กล่าวถึง ASP.NET MVC ไปบ้างแล้วว่าคืออะไร ในส่วนของบทความนี้จะแนะนำการเริ่มต้นสร้าง Project เพื่อพัฒนา Web Application ด้วย Microsoft Visual Studio 2013 โดยใน MVC5 ซึ่งเป็นเวอร์ชันล่าสุด ได้มีการติดตั้ง Bootstrap มาให้ในตัว ช่วยให้การพัฒนาในส่วนของ View สามารถเรียกใช้งาน Bootstrap class ร่วมกับ Razor syntax(จะกล่าวถึงในบทความถัดไป) ในการแสดงผลหน้า View ได้ง่ายขึ้นและทำให้สามารถรองรับหลากหลายอุปกรณ์ที่มีหน้าจอที่มีขนาดแตกต่างกันออกไป โดยที่เราไม่ต้องเสียเวลาติดตั้งเพิ่มเอง

    Tools & Environment 

    • Microsoft Visual Studio 2013
    • Microsoft .Net Framework 4.5
    • ภาษาที่ใช้ในการพัฒนา Visual C#

    Step 1: เปิด Visual Studio ขึ้นมา และคลืกที่ New Project

    รูปที่ 1


    Step 2: ที่เมนูด้านซ้ายให้เลือก Visual C# > Web >ASP.NET Web Application และตั้งชื่อ Project ดังรูปที่ 2 หลังจากนั้นให้กดปุ่ม OK

    รูปที่ 2
    Step 3: เลือก Template Web โดยเลือกเป็น MVC หลังจากนั้นกด OK
    รูปที่ 3
    Step 4: Check และ Update เวอร์ชัน Bootstrap
    สังเกตที่ solution explorer ฝั่งด้านขวา ที่โฟลเดอร์ Content และโฟลเดอร์ Scripts จะพบไฟล์ Css และ JavaScript ของ Bootstrap ติดตั้งมาให้เรียบร้อยแล้ว
    รูปที่ 4
    ตรวจสอบเวอร์ชันของ Bootstrap ว่าเป็นเวอร์ชันล่าสุดแล้วหรือยัง โดย คลิกขวา ที่ Project แล้วเลือก Manage Nuget Packages ดังรูปที่ 5
    รูปที่ 5
    จะปรากฎหน้าต่าง Manage Nuget Package ซึ่งจะแสดง Package หรือ Library  ทั้งหมดที่ตอนนี้ Project เราติดตั้งอยู่ ให้เลือกดูที่ Boostrap และสังเกตคำอธิบายที่ช่องด้านขวา จะแสดงเวอร์ชั่นอยู่ ซึ่งจากรูปที่ 6 เป็น Bootstrap 3.0 ซึ่งเป็นเวอร์ชันปัจจุบันแล้ว หากยังไม่เป็นปัจจุบันจะขึ้นปุ่มให้สามารถ Update เวอร์ชันได้
    รูปที่ 6
     หลังจากนั้นให้ไปดูที่โฟลเดอร์ App_Start > BudleConfig.cs  ดังรูปที่ 7 โดยไฟล์ ฺBundleConfig.cs จะทำหน้าที่ลงทะเบียนเก็บ Path ของไฟล์ Java script และ CSS ต่างๆที่มีการเรียกใช้ในระบบไว้ที่นี่ เพื่อไว้เวลาเราอ้าง Path เราสามารถอ้าง Path ตามชื่อ Bundle ที่เราสร้างโดยไม่ต้องเรียกไปยัง Path File ตรงๆได้
    รูปที่ 7
    Step 5: ทดลอง Run preview ดูหน้าเว็บ
    ก่อนจะรัน ให้ไปที่ โฟลเดอร์ View > Shared >_layout.cshtml ซึ่งเป็น Layout ของระบบ(เทียบกับ Web Form ก็คือ Master Page) จากรูปที่ 8 จะเห็นว่า มีการเรียกใช้ Bootstrap Class ในการจัดวาง Component และ Layout ต่างๆไว้ให้
    รูปที่ 8

    กดปุ่ม Run  เพื่อดูผลลัพธ์ ออกมาดังรูป 9

    รูปที่ 9
    เมื่อทำการย่อปรับเป็นขนาด Mobile หน้าเว็บก็จะปรับตามขนาดของหน้าจอของอุปกรณ์ที่แสดงผลดังรูป  เป็นอันเสร็จเรียบร้อย ^^
    ——–End——-
    บทความถัดไป : เป็นการแนะนำการเริ่มต้นสร้างส่วนของ Model ในการติดต่อกับฐานข้อมูล  >> ASP.NET MVC Part3: สร้าง Model ด้วย Entity Framework
  • ASP.NET MVC Part 1 : ทำความรู้จักกับ ASP.NET MVC

    สวัสดีค่ะ วันนี้ผู้เขียนจะมาแนะนำให้รู้จักกับแนวทางการพัฒนา Web Application ด้วย ASP.NET MVC ก่อนหน้านี้ผู้เขียนจะพัฒนา Web Application ด้วย ASP.NET Web Forms มาตลอด และช่วงที่ผ่านมา ผู้เขียนเองมีโอกาสได้ศึกษาและทำความเข้าใจกับ ASP.NET MVC มาซักระยะหนึ่ง จึงมาเขียนบทความเล่าสู่กันฟังเกี่ยวกับ การพัฒนา Web Application ด้วย ASP.NET MVC ซึ่งในบทความนี้ อ้างอิงและแปลมาจากบทความ WebForms vs MVC ซึ่งจะประกอบไปด้วย

    • ASP.NET คืออะไร?
    • ASP.NET Web Forms คืออะไร?
    • MVC คืออะไร?
    • ASP.NET MVC คืออะไร?
    • เปรียบเทียบระหว่าง Web Forms และ MVC
    • เลือกใช้อะไรดี ระหว่าง Web Forms และ MVC ?

    ASP.NET คืออะไร?

       คือ Web application framework ที่ถูกพัฒนาโดย Microsoft สำหรับนักพัฒนาในการพัฒนา web application ขึ้นมา โดยโปรแกรมด้วยภาษา C#,VB.NET และอื่นๆ ณ ปัจจุบัน มีอยู่ 2 รูปแบบในการเลือกใช้พัฒนา ได้แก่ Web Forms และ MVC
    ASP.NET Web Forms คืออะไร?
    คือ framework ในการพัฒนา web application ที่ถูกออกแบบมาในลักษณะ RAD(Rapid Application Development) คือ สามารถพัฒนา Web application ได้อย่างรวดเร็ว โดยใช้วิธีการลาก control มาวางบนพื้นที่ design หน้า page และเขียน code ภายใต้ control เหล่านั้น โดยอาศัย concept การทำงานของ Postback(การส่งข้อมูลไปมาระหว่าง server และ client) และ ViewState(การเก็บค่าให้คงไว้ระหว่างการทำ postback)
    วิธีการสร้างหน้า page ของ Web Forms
    การทำงานของ Web Forms

    MVC คืออะไร?
    คือ design pattern ที่ใช้ในการสร้าง Web Application แนวความคิดของ MVC design pattern จะจัดการแยกหน้าที่ขององค์ประกอบใน application ออกเป็นส่วนๆ(separation) เพื่อให้สะดวก รวดเร็ว และง่ายขึ้น ในการสร้าง พัฒนา และขยายระบบเพิ่มเติม รวมถึงมันจะทำให้เราทดสอบ application นี้เป็นส่วนๆได้โดยไม่กระทบ หรือกระทบน้อยที่สุดกับส่วนอื่น โดย MVC ย่อมาจาก Model, View และ Controller

    Model คือ คือส่วน Business Model หรือส่วนที่ติดต่อกับฐานข้อมูล
    Controller คือ ส่วนควบคุมและรับ request จาก user มาและไปดึงข้อมูลจาก Model มาเพื่อแสดงผลข้อมูลกลับไปยัง user ที่ส่วน View
    View คือ ส่วนที่แสดงผลข้อมูล

    ASP.NET MVC คืออะไร?
    คือ framework ในการพัฒนา web application ที่ถูกออกแบบให้รองรับ MVC pattern โดยจัดการแยกหน้าที่ขององค์ประกอบใน application ออกเป็นส่วนๆ(separation of concerns) ด้วยการจัดการที่แยกออกเป็นส่วนๆ ทำให้การทดสอบระบบ(Unit Testing)ที่ซํบซ้อน ทำได้ง่ายขึ้น

    วิวัฒนาการ ASP.NET Framework

    การทำงานของ ASP.NET MVC

    เปรียบเทียบระหว่าง Web Forms และ MVC
    1. Web Forms
    ข้อดี

    •      Web Form support Rich server control คือ มี control ฝั่ง server มาให้ใช้งานมากมายและหลากหลาย
    Tool box Control ที่ ASP.Net Web Forms เตรียมไว้ให้
    • เป็นการเขียนโปรแกรมแบบ Event Driven Programming คือ การเขียนโปรแกรมในลักษณะว่า “ถ้าเหตุการณ์นี้เกิดขึ้น เราจะให้โปรแกรมของเราจัดการกับเหตุการณ์นั้นๆ อย่างไร”  สำหรับ ASP.NET Web Forms จะมีตัวช่วยในการเขียนลำดับเหตการณ์ ประกอบด้วย Code behind,กลไก postback ตัวเอง และ ViewState ซึ่งด้วยวิธีการนี้ นักพัฒนาไม่จำเป็นต้องพึ่งพา Get/Post method ในการติดต่อกับ Server
    ตัวอย่าง Code Behind ภายใต้เหตุการณ์หลังจากการกดปุ่ม
    • Rapid Application Development จากการที่ Web forms มีการเตรียม Server control มาให้ใช้มากมาย และการเขียนโปรแกรมในลักษณะ Event driven จึงช่วยให้การพัฒนา Application เป็นไปได้รวดเร็วขึ้น ตัวอย่างเช่น นักพัฒนาสามารถลาก control Button(ปุ่ม) มาวาง และ double click เพื่อเขียน code logic การทำงานเข้าไปได้เลย โดยไม่จำเป็นต้องรู้การทำงานเบื้องหลังของ event นั้นๆ ก็สามารถสร้าง Application ให้ทำงานได้
    • Less Learning Effort จากการที่ Web forms มีการเตรียม Server control มาให้ใช้มากมาย และประกอบกับมีการทำงานด้วย ViewState ทำให้ผู้พัฒนาที่มี Skill ทางด้าน HTML และ Java script เล็กน้อย ก็สามารถสร้าง Application ขึ้นได้

    ข้อเสีย

    • Project Architecture 
                 code ทุกอย่างจะอยู่ภายใต้ code behind ซึ่งจะผูกติดกันอย่าแน่นหนากับ หน้า UI
    • Unit Testing  เนื่องจากภายใต้ code behind ที่มีจำนวน event ที่เยอะมาก ทำให้การทำ automatic unit testing ทำได้ยาก
    • Performance ด้วยการที่ Web forms ใช้ ViewState ในการเก็บค่าต่างๆของหน้า page ส่งผลให้ ขนาดของ ViewState ใหญ่ขึ้น ทำให้ performance ของระบบลดลงไปด้วย
    • Reusability  เนื่องจากการผูก code behind เข้ากับ UI ทำให้การ reuse code ทำได้ยาก
         จากรูป หากเราต้องการ binding ข้อมูลในลักษณะเดียวกับกับ Customer ทุกอย่างให้กับ UI อื่นๆ เราก็จะต้องสร้าง method binding ที่เหมือนกันกับในรูปขึ้นมาอีก เนื่องจาก code ถูกผูกเข้ากับ control gridview คนละตัวกัน
    • Less control over HTML control ที่มี่อยู่อาจไม่ support การทำงาน จำเป็นต้องนำเอา java script framework เข้ามาใช้ร่วมด้วย เช่น Jquery, AJAX เป็นต้น

    2. MVC

    ข้อดี
    Project Structure แบ่งแยกส่วนต่างๆ ของ code ออกจากกันชัดเจน
    • Test Driven Development and Reusability
      • ด้วยการแบ่งส่วนการทำงานของ code ออกจากกันชัดเจน ทำให้สามารถทำ Unit Testing ได้ง่ายขึ้น
      • Controller ไม่จำเป็นต้องผูกกับ View ใด View หนึ่ง สามารถ reuse ใช้ได้กับหลายๆ View ได้
    • Performance ใน MVC จะไม่มี ViewState และ event ทำให้ลดขนาด page size ลงไป ทำให้ได้ performance ที่ดีกว่า
    • Full control over HTML ใน MVC ไม่มี server control มาให้เหมือน Web Forms ทำให้เราสามารถใช้ control ของ HTML และ java script ได้อย่างอิสระ
    • SEO,URL Routing and REST มีการเรียก url แบบ REST service และการทำ SEO(Search Engine Optimization) ง่ายกว่า
    •  Support Parallel Development  ใน MVC มีการแบ่งส่วนการทำงานออกจากกันชัดเจน ทำให้ผู้พัฒนาสามารถพัฒนาคู่ขนานกันไปได้ เช่น คนที่รับผิดชอบส่วน View ก็ทำส่วน Design wxพร้อมกับที่ทำส่วน Controller ได้เลย ไม่จำเป็นต้องรอใครทำเสร็จก่อนแล้วจึงจะทำต่อได้

    ข้อเสีย

    • More Learning Effort จำเป็นต้องมีประสบการณ์ในการทำ web application มาพอสมควร เนื่องจากจะไม่มี ViewState, Event และ Server control ที่อำนวยความสะดวกเหมือนใน Web Forms
    • ไม่มี server control ให้ลากวาง เหมือนใน Web Forms ต้องเขียน HTML ขึ้นมาเอง

    เลือกใช้อะไรดีระหว่าง Web Forms และ MVC?

        ในการพัฒนา web application เราจำเป็นต้องพิจารณาจากหลายๆปัจจัย เช่น หากเราต้องการ web application ที่ต้องการให้เสร็จเร็วและง่าย ก็ควรเลือกใช้แบบ Web Forms เนื่องจาก Web Forms ถือว่าเป็น RAD(Rapid Application Development) คือ เตรียมทุกอย่างมาให้พร้อมที่จะพัฒนาได้เลยเช่น Server control,ViewState,Event เป็นต้น แต่หากเราต้องการพัฒนา web application ที่ซับซ้อนและต้องการ performance ที่ดี ประกอบกับทีมพัฒนามีความรู้ทางด้านการพัฒนา web application เป็นอย่างดี ก็ควรเลือกใช้ MVC มาเป็นตัวเลือกในการพัฒนา

    ที่มา: https://www.facebook.com/photo.php?fbid=685327044884788

    ในบทความถัดไปจะแนะนำการเริ่มต้นสร้าง web application ด้วย ASP.NET MVC ร่วมกับ Bootstrap >> ASP.NET MVC Part2: เริ่มต้นสร้างเว็บด้วย MVC with Bootstrap

    อ้างอิง :

    • http://www.codeproject.com/Articles/528117/WebForms-vs-MVC
    • http://www.codeproject.com/Articles/821275/Webforms-vs-MVC-and-Why-MVC-is-better
  • เกร็ดความรู้ประกอบการกู้หรือย้ายฐานข้อมูล SQL Server

    Count record แต่ละ tables ใน database

    SELECT T.name AS [TABLE NAME], I.row_count AS [ROWCOUNT]
    FROM sys.tables AS T
    INNER JOIN
    sys.dm_db_partition_stats AS I
    ON T.object_id = I.object_id AND I.index_id < 2
    ORDER BY I.row_count DESC

    หา Trigger ทั้งหมดในทุกๆ tables ใน Database

    SELECT [so].[name] AS [trigger_name], USER_NAME([so].[uid]) AS [trigger_owner],
    USER_NAME([so2].[uid]) AS [table_schema], OBJECT_NAME([so].[parent_obj]) AS [table_name],
    OBJECTPROPERTY( [so].[id], ‘ExecIsUpdateTrigger’) AS [isupdate],
    OBJECTPROPERTY( [so].[id], ‘ExecIsDeleteTrigger’) AS [isdelete],
    OBJECTPROPERTY( [so].[id], ‘ExecIsInsertTrigger’) AS [isinsert],
    OBJECTPROPERTY( [so].[id], ‘ExecIsAfterTrigger’) AS [isafter],
    OBJECTPROPERTY( [so].[id], ‘ExecIsInsteadOfTrigger’) AS [isinsteadof],
    OBJECTPROPERTY([so].[id], ‘ExecIsTriggerDisabled’) AS [disabled]
    FROM sysobjects AS [so]
    INNER JOIN sysobjects AS so2 ON so.parent_obj = so2.Id WHERE [so].[type] = ‘TR’

    ค้นหาข้อความในทุกๆ Stored Procedure ใน Database

    SELECT DISTINCT obj.name AS Object_Name,obj.type_desc
    FROM sys.sql_modules sm INNER JOIN sys.objects obj ON
    sm.object_id=obj.object_id
    WHERE sm.definition Like ‘%xxx%’

    วิธีการ Rebuild Full-text Catalogs of Database

    Use Management Studio
    1. In Object Explorer, expand the server, expand Databases, and then expand the
    database that contains the full-text catalogs that you want to rebuild.
    2. Expand Storage, and then right-click Full Text Catalogs.
    3. Select Rebuild All.
    4. To the question, Do you want to delete all full-text catalogs and rebuild them?,
    click OK.
    5. In the Rebuild All Full-Text Catalogs dialog box, click Close.

    rebuild all index in Database

    use DatabaseName;
    DECLARE @TableName varchar(255)
    DECLARE TableCursor CURSOR FOR
    SELECT table_name FROM information_schema.tables
    WHERE table_type = ‘base table’
    OPEN TableCursor
    FETCH NEXT FROM TableCursor INTO @TableName
    WHILE @@FETCH_STATUS = 0
    BEGIN
    DBCC DBREINDEX(@TableName,’ ‘,90)
    FETCH NEXT FROM TableCursor INTO @TableName
    END
    CLOSE TableCursor
    DEALLOCATE TableCursor

    ค้นหา tables ทั้งหมดใน Database ที่มี Identity Column

    select o.name,’set identity_insert [‘+s.name+’].[‘+o.name+’] ON;’ as ION,’set identity_insert
    [‘+s.name+’].[‘+o.name+’] OFF;’ as IOff
    from sys.objects o
    inner join sys.schemas s on s.schema_id=o.schema_id
    where o.[type]=’U‘ and
    exists(select 1 from sys.columns where object_id=o.object_id and is_identity=1)
    order by o.name

    ตัอย่างการ insert table ที่มี Identity Column

    set identity_insert [dbo].[AGroup] ON;
    insert into [xDB].[dbo].[AGroup] ([AGroupID],[AGroupName])
    select [AGroupID],[AGroupName]
    from [aDB].[dbo].[AGroup]
    order by AGroupID;
    set identity_insert [dbo].[AGroup] OFF;

    Compare two table data

    แบบที่ 1
    select * from zlog1
    except
    select * from zlog
    แบบที่ 2
    select * from
    ( select checksum(*) as chk, id as k from zlog1) as t1
    left join
    ( select checksum(*) as chk, id as k from zlog) as t2 on t1.k = t2.k
    where t1.chk <> t2.chk

    ## เสนอแนะ ถ้ามีการกู้หรือย้ายฐานข้อมูลโดยที่ตัวเดิมยังเปิดใช้งานอยู่ สิ่งที่ควรทำคือให้สร้าง User ขึ้นมาใหม่ที่มีสิทธิใช้งานเฉพาะ Database ตัวใหม่เท่านั้น เพื่อป้องกันการเรียกใช้งานฐานข้อมูลทั้งสองที่โดยที่เราอาจจะไม่รู้ซึ่งจะทำให้ข้อมูลในฐานข้อมูลมั่วมากจนอาจจะเกินเยียวยา โดยมากจะเกิดกับ การเขียน code Stored Procedure ที่มีการระบุชื่อ Database ไว้ใน Stored Procedure

  • การกู้ Suspect Database ของ SQL Server

    Recovery SQL Server Suspect Database

    สาเหตุของ Suspect Mode

    การที่ฐานข้อมูล SQL Server เข้าสู่ Mode Suspect นั้นมีได้จากหลายสาเหตุ ดังนี้

    • Hardware เกิดความเสียหาย
    • มีการปิด (Shutdown) ฐานข้อมูลที่ไม่เหมาะสม คือปิดโดยที่ยังมีกระบวนการทำงานยังไม่เสร็จสมบูรณ์ หรือมีบาง Transaction ค้างอยู่
    • เกิดความเสียหายกับ Database Files (*.mdf,*.log)
    • SQL Server ไม่พบ Device ที่เก็บ Files
    • SQL Server ไม่พบ Database Files
    • Database Resource ถูกใช้งานอยู่โดย Operation System
    • ไม่มีพื้นที่มากพอใน Page space ที่มีการเพิ่ม (Insert) ข้อมูลเข้าไป

    วิธีแก้ปัญหา

    แต่ต้องอยู่บนพื้นฐานที่ไม่มีอะไรผิดปกติที่เกิดจาก Devices หรือ ตัว Database Files จึงจะ สามารถ Recovery โดยวิธีดังนี้

    EXEC sp_resetstatus [YourDatabase];
    ALTER DATABASE [YourDatabase] SET EMERGENCY
    DBCC checkdb([YourDatabase])
    ALTER DATABASE [YourDatabase] SET SINGLE_USER WITH ROLLBACK IMMEDIATE
    DBCC CheckDB ([YourDatabase], REPAIR_ALLOW_DATA_LOSS)
    ALTER DATABASE [YourDatabase] SET MULTI_USER

    อธิบาย

    • EXEC sp_resetstatus [YourDatabase];

    เป็นคำสั่งทำการปิด suspect mode เมื่อทำแล้วก็ต้อง Stop และ Restart SQL Server ด้วย

    • ALTER DATABASE [YourDatabase] SET EMERGENCY

    เป็นคำสั่งให้ Database เข้าสู่ READ_ONLY MODE และจำกัดการเข้าถึงให้เข้าได้เฉพาะ SysAdmin Account เท่านั้น

    • DBCC checkdb([YourDatabase])

    CheckDB จะตรวจสอบการจัดสรรทรัพยากรสำหรับทุกๆ Object และตรวจสอบความสมบูรณ์ของโครงสร้างฐานข้อมูลทั้งหมด

    • ALTER DATABASE [YourDatabase] SET SINGLE_USER WITH ROLLBACK IMMEDIATE

    กำหนดให้สามารถเข้าใช้งานฐานข้อมูลได้เพียง User เดียวเท่านั้น

    • DBCC CheckDB ([YourDatabase], REPAIR_ALLOW_DATA_LOSS)

    จะมีการทำงานหลายขั้นตอนซึ่งแรกสุดจะเป็นการตรวจสอบทรัพยากรต่างๆและมีการตรวจสอบโครงสร้างของฐานข้อมูลจะมีการ Run คำสั่ง DBCC CheckAlloc, DBCC CheckTable, DBCC CheckCatalog ทั้งฐานข้อมูล มีการตรวจสอบข้อมูลของแต่ละ Indexed View ตรวจสอบ Link-Level Consistency ระหว่าง table metadata และ file system directories ตรวจสอบ Service Broker Data ในฐานข้อมูลทั้งหมด

    ส่วน Option REPAIR_ALLOW_DATA_LOSS นั้นจะมีการพยายามซ้อมแซ่มฐานข้อมูลในส่วนที่เสียไปตามรายการที่ได้จากการ CheckDB และเป็นการอนุญาตให้ข้อมูลสามารถศูนย์หายได้บาง

    • ALTERDATABASE [YourDatabase] SET MULTI_USER

    เป็นการ set database ให้กลับมาใช้งานตามปกติ

    หลังจากขั้นตอนเหล่านี้แล้ว ผมจะทำการ Shutdown Server แล้วเปิดขึ้นมาใหม่แล้วลองใช้คำสั่ง DBCC CheckDB() ตรวจสอบอีกครั้งเพื่อความแน่ใจ

  • จดหมายหลอกลวง 14/7/58

    หากท่านได้รับจดหมายลักษณะเช่นนี้ เป็นจดหมายหลอกลวง

    ห้ามคลิก Link หรือกรอกข้อมูลใดๆเด็ดขาด มิฉะนั้นบัญชีของท่านจะโดนปิดทันที

    Screenshot from 2015-07-14 09:42:15

    หากใครคลิกไปแล้ว จะเจอกับหน้านี้ ให้รู้ไว้เลยว่า โดนหลอกแล้ว Screenshot from 2015-07-14 09:51:42

    หากใคร พลาดกรอกข้อมูลไปแล้วให้ทำการ Reset Password ที่ https://webmail.psu.ac.th ทันทีครับ

     

     

  • Export ข้อมูลไฟล์ Excel ในแบบ Single และ Multiple sheet ด้วย ASP.NET(C#)

                ความเดิมตอนที่แล้ว ผู้เขียนได้พูดถึงความสำคัญของข้อมูล และวิธีการ Import ข้อมูลจากไฟล์ Excel กันไปพอสมควร สำหรับในบทความนี้ ผู้เขียนจะขอพูดถึงวิธีการส่งออกข้อมูล หรือที่เรามักเรียกกันติดปากว่า “Export ข้อมูล” กันบ้าง เพื่อให้ผู้พัฒนาที่มีความสนใจสามารถพัฒนาโปรแกรมได้ครบวงจรทั้งแบบนำเข้าและส่งออกข้อมูล โดยผู้เขียนจะไม่ขอพูดถึงในรายละเอียดที่ได้กล่าวไว้แล้วก่อนหน้า ผู้อ่านสามารถอ่านรายละเอียดเพิ่มเติมได้ในบทความ “เรียนรู้วิธีการ Import ข้อมูลในรูปแบบไฟล์ Excel ด้วย ASP.NET (C#)” สำหรับในบทความนี้ผู้เขียนจะเน้นในส่วนของการ Export ข้อมูลเท่านั้น ซึ่งจะมีการอธิบายใน 2 ลักษณะเช่นกัน คือ แบบ Single sheet และแบบ Multiple sheet เพื่อให้เห็นเป็นแนวทางและสามารถนำไปต่อยอดการพัฒนาเพิ่มเติมสำหรับแต่ละท่านได้

    กรณีส่งออกข้อมูล(Export) ซึ่งในบทความนี้จะแบ่งเป็น 2 ลักษณะ ดังนี้

    • การส่งออกข้อมูลแบบ Single-sheet ซึ่งมีขั้นตอนการทำงานไม่ซับซ้อนมากนัก ดังนี้
    protected void btnExport_Click(object sender, EventArgs e)
    
    {
    //// ตารางสมมติ สร้างขึ้นเพื่อให้ผู้พัฒนาเห็นภาพ หากเป็นกรณีใช้งานจริงจะเป็นข้อมูลที่ดึงจากฐานข้อมูลเพื่อ Export ในรูปแบบไฟล์ Excel
    DataTable table = new DataTable();
    table.Columns.Add("Name", typeof(string));
    table.Columns.Add("Latitude", typeof(decimal));
    table.Columns.Add("Longitude", typeof(decimal));
    table.Columns.Add("Description", typeof(string));
    table.Rows.Add("University1", 7.006923, 100.500238, "Desc1");
    table.Rows.Add("University2", 7.172661, 100.613726, "Desc2");
    
    
    
    StringBuilder sb = new StringBuilder();
    if (table.Rows.Count > 0)
    {
    string fileName = Path.Combine(Server.MapPath("~/ImportDocument"), DateTime.Now.ToString("ddMMyyyyhhmmss") + ".xls");
    
    //// ลักษณะการตั้งค่าเพื่อเชื่อมต่อด้วย OleDb ซึ่งในกรณีนี้ไฟล์ Excel จะต้องมีนามสกุลเป็น .xls แต่หากเป็นนามสกุลแบบ .xlsx ต้องเปลี่ยนการกำหนดค่าให้เป็น
    conString = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + 
    fileName + ";Extended Properties='Excel 12.0;HDR=YES;IMEX=1;';";  แทน
    
    string conString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + fileName + ";Extended Properties=\"Excel 8.0;HDR=Yes;IMEX=2\"";
    
    
     using (OleDbConnection con = new OleDbConnection(conString))
    {
    ////เขียนคำสั่งในการสร้างตาราง ซึ่งในที่นี้คือ WorkSheet ที่ต้องการ พร้อมทั้งกำหนดชื่อและชนิดของข้อมูลในแต่ละคอลัมน์
    string strCreateTab = "Create table University (" +
    " [Name] varchar(50), " +
    " [Latitude] double, " +
    " [Longitude] double, " +
    " [Description] varchar(200)) ";
    
    
    
    if (con.State == ConnectionState.Closed)
    {
    con.Open();
    }
    ////รันคำสั่งที่เขียนในการสร้างตาราง
    OleDbCommand cmd = new OleDbCommand(strCreateTab, con);
    cmd.ExecuteNonQuery();
    
    ////เขียนคำสั่งในการเพิ่มข้อมูล(insert) ข้อมูลในแต่ละฟิลด์ รวมทั้งประกาศพารามิเตอร์ที่ใช้ในการรับค่าข้อมูลที่อ่านได้
    string strInsert = "Insert into University([Name],[Latitude]," +
    " [Longitude], [Description]" +
    ") values(?,?,?,?)";
    
    OleDbCommand cmdIns = new OleDbCommand(strInsert, con);
    cmdIns.Parameters.Add("?", OleDbType.VarChar, 50);
    cmdIns.Parameters.Add("?", OleDbType.Double);
    cmdIns.Parameters.Add("?", OleDbType.Double);
    cmdIns.Parameters.Add("?", OleDbType.VarChar, 200);
    
    ////วนค่าที่ได้จากฐานข้อมูลและกำหนดค่าให้กับพารามิเตอร์และรันคำสั่งในการเพิ่มข้อมูลทีละรายการ
    foreach (DataRow  i in table.Rows)
    {
    cmdIns.Parameters[0].Value = i["Name"];
    cmdIns.Parameters[1].Value = i["Latitude"];
    cmdIns.Parameters[2].Value = i["Longitude"];
    cmdIns.Parameters[3].Value = i["Description"];
    cmdIns.ExecuteNonQuery();
    
    }
    }
    
    ////สร้างไฟล์ที่ต้องการดาวน์โหลดจากการอ่านที่ได้ทั้งหมด เพื่อบันทึกเป็นไฟล์ Excel ที่มีชื่อว่า University.xls
    
    byte[] content = File.ReadAllBytes(fileName);
    HttpContext context = HttpContext.Current;
    context.Response.BinaryWrite(content);
    context.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
    context.Response.AppendHeader("Content-Disposition", "attachment; filename=University.xls");
    Context.Response.End();
    }
    }
    • การส่งออกข้อมูลแบบ Multiple sheet ใช้ในกรณีที่มีข้อมูลที่ Export จำนวนมากและต้องการแบ่งข้อมูลที่มีอยู่ออกเป็น WorkSheet ย่อย ซึ่งหลักการทำงานจะคล้ายกับการส่งออกข้อมูลแบบ Single-sheet แต่จะซับซ้อนกว่าในส่วนของการวนค่าจากในฐานข้อมูลมาใส่ใน  WorkSheet โดยมีการกำหนดจำนวนแถวที่จะให้บันทึกในแต่ละ WorkSheet หากเกินค่าที่กำหนดจะสร้าง WorkSheet ใหม่และบันทึกข้อมูลลงไปใน WorkSheet นั้น
    static StringBuilder sqlInsert = new StringBuilder();
    static StringBuilder strScript = new StringBuilder();
    
    public static void ExportToMultipleXLSheets( System.String OutputFileName)
    {
    string connstr = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" +
    mOutputFileName + ";Extended Properties='Excel 8.0'";
    OleDbConnection xlConn = new OleDbConnection(connstr);
    
    try
    {
    xlConn.Open();
    ////ตารางสมมติเช่นเดียวกับที่ได้กล่าวไว้ในแบบการส่งออกข้อมูลแบบ Single sheet
    DataTable table = new DataTable();
    table.Columns.Add("EmployeeID", typeof(string));
    table.Columns.Add("Name", typeof(string));
    table.Columns.Add("LastName", typeof(string));
    table.Columns.Add("Position", typeof(string));
    
    table.Rows.Add("0001", "Jack", "Rayman", "Programmer");
    table.Rows.Add("0002", "John", "Wicky", "Programmer");
    table.Rows.Add("0003", "Jenifer", "Wincy", "Programmer");
    table.Rows.Add("0004", "Kate", "Wrapper", "Manager");
    table.Rows.Add("0005", "Bella", "Cole", "Programmer");
    table.Rows.Add("0006", "Sandy", "Stick", "Programmer");
    table.Rows.Add("0007", "Tom", "Runner", "Programmer");
    
    ////เป็นการเรียกใช้เมธอดที่ใช้ในการเตรียมคำสั่งที่จะใช้วนสร้างตาราง/Worksheet
    PrepareScript(table);
    
    ////เป็นเมธอดที่ใช้ในการเริ่มต้นทำงานคำสั่งในการ Export โดยจะมีการคำนวณจำนวนแถวของข้อมูลว่าเกินที่ระบุไว้หรือไม่ หากเกินกำหนดจะสร้าง WorkSheet ใหม่นั่นเอง
    StartExport(table, xlConn);
    
    if (xlConn != null)
    {
    if (xlConn.State == ConnectionState.Open) xlConn.Close();
    xlConn.Dispose();
    }
    
    ////อ่านค่าและ Export ไปยังไฟล์ที่มีชื่อว่า ExportMultiSheetData.xls
    byte[] content = File.ReadAllBytes(mOutputFileName);
    HttpContext context = HttpContext.Current;
    context.Response.BinaryWrite(content);
    context.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
    context.Response.AppendHeader("Content-Disposition", "attachment; filename=ExportMultiSheetData.xls"  );
    context.Response.End();
    
    }
    catch (Exception exp)
    {
    throw new Exception("ImportToMultipleXLSheets", exp.InnerException);
    }
    finally
    {
    if (xlConn != null)
    {
    if (xlConn.State == ConnectionState.Open) xlConn.Close();
    xlConn.Dispose();
    }
    }
    }
    o เมธอดในการเตรียมคำสั่งที่ใช้ในการสร้างตารางและเพิ่มข้อมูล
    private static string PrepareScript(DataTable DTable)
    {
    // เตรียมคำสั่งในการสร้าง WorkSheet
    sqlInsert.Length = 0;
    strScript.Length = 0;
    
    ////วนค่าเพื่ออ่านค่าคอลัมน์ที่มีในแต่ละตาราง
    for (int i = 0; i < DTable.Columns.Count; i++)
    {
    sqlInsert.Append( "[" + DTable.Columns[i].ColumnName + "],");
    strScript.Append("[" + DTable.Columns[i].ColumnName.Replace("'", "''") + "]");
    
    ////กำหนดชนิดของข้อมูลแต่ละคอลัมน์
    if (DTable.Columns[i].DataType.ToString().ToLower().Contains("int") || DTable.Columns[i].DataType.ToString().ToLower().Contains("decimal"))
    strScript.Append(" double");
    else
    strScript.Append(" text");
    strScript.Append(", ");
    }
    
    sqlInsert.Remove(sqlInsert.Length - 1, 1);
    strScript.Remove(strScript.Length - 2, 1);
    strScript.Append(") ");
    ////ผลที่ได้ : 
    sqlInsert >> [EmployeeID],[Name],[LastName],[Position]
    strScript >> [EmployeeID] text, [Name] text, [LastName] text, [Position] text ) 
    เพื่อนำไปใช้ในการ run คำสั่งการทำงานสร้าง (create table) และเพิ่มข้อมูล (insert)
    
    return strScript.ToString();
    }
    o เมธอดในการสร้างตาราง/WorkSheet ตามคำสั่งที่เตรียมไว้
    private static void CreateXLSheets(DataTable DTable,
    OleDbConnection xlConn, System.String XLSheetName)
    {
    // สร้าง WorkSheet ใหม่
    System.Text.StringBuilder SqlFinalScript = new System.Text.StringBuilder();
    
    OleDbCommand cmdXl = new OleDbCommand();
    try
    {
    SqlFinalScript.Length = 0;
    cmdXl.Connection = xlConn;
    
    ///สร้างคำสั่งในการสร้างตาราง/WorkSheet ขึ้นใหม่ โดยตั้งชื่อตามพารามิเตอร์ที่ได้รับมา
    SqlFinalScript.Append("CREATE TABLE " + XLSheetName + " (");
    SqlFinalScript.Append(strScript.ToString());
    cmdXl.CommandText = SqlFinalScript.ToString();
    cmdXl.ExecuteNonQuery();
    }
    catch (Exception xlSheetExp)
    {
    throw (new Exception("CreateXLSheetException", xlSheetExp.InnerException));
    }
    finally
    {
    cmdXl.Dispose();
    }
    }
    
    private static void StartExport(DataTable DTable, OleDbConnection xlConn)
    {
    Int64 rowNo = 0, xlSheetIndex = 1, TotalNoOfRecords = 0;
    System.String NewXLSheetName = "Sheet";
    System.Text.StringBuilder strInsert = new System.Text.StringBuilder();
    TotalNoOfRecords = DTable.Rows.Count;
    OleDbCommand cmdXl = new OleDbCommand();
    cmdXl.Connection = xlConn;
    for (int count = 0; count < DTable.Rows.Count; count++)
    {
    strInsert.Length = 0;
    ////กรณีที่เป็นแถวแรกจะทำการสร้าง WorkSheet ขึ้นใหม่ โดยเรียกใช้เมธอด CreateXLSheets
    if (rowNo == 0 )
    { CreateXLSheets(DTable, xlConn, NewXLSheetName + xlSheetIndex);
    }
    rowNo += 1;
    
    ////กรณีที่จำนวนแถวใน 1 WorkSheet เกินที่กำหนดไว้จะสร้างชีทใหม่ ในกรณีนี้คือจะสร้าง WorkSheet ใหม่ทุกๆ 3 แถว และเพิ่มลำดับของ index ของ WorkSheet คราวละ 1
    if (TotalNoOfRecords > 3 && rowNo > 3
    {
    xlSheetIndex += 1;
    CreateXLSheets(DTable, xlConn, NewXLSheetName + xlSheetIndex);
    rowNo = 1;
    }
    
    ////เขียนคำสั่งในการเพิ่มข้อมูลไปยัง WorkSheet ที่กำหนด โดยมีการแทนที่ค่าของคำสั่งที่เตรียมไว้แล้วก่อนหน้านี้ในตอนเรียกใช้เมธอด PrepareScript
    strInsert.Append("Insert Into [" + NewXLSheetName + xlSheetIndex.ToString() +  "$](" + sqlInsert.ToString() + ") Values (");
    
    
    foreach (DataColumn dCol in DTable.Columns)
    {
    if (dCol.DataType.ToString().ToLower().Contains("int"))
    {
    if (DTable.Rows[count][dCol.Ordinal].ToString() == "")
    strInsert.Append("NULL");
    else
    strInsert.Append(DTable.Rows[count][dCol.Ordinal]);
    }
    else if (dCol.DataType.ToString().ToLower().ToLower().Contains("decimal"))
    {
    if (DTable.Rows[count][dCol.Ordinal].ToString() == "")
    strInsert.Append("NULL");
    else
    strInsert.Append(DTable.Rows[count][dCol.Ordinal]);
    }
    else
    strInsert.Append("\"" +
    DTable.Rows[count][dCol.Ordinal].ToString().Replace("'",
    "''") + "\"");
    strInsert.Append(",");
    }
    strInsert.Remove(strInsert.Length - 1, 1);
    strInsert.Append(");");
    
    ////run คำสั่งที่สร้างขึ้นในการเพิ่มข้อมูลทีละรายการ
    cmdXl.CommandText = strInsert.ToString();
    cmdXl.ExecuteNonQuery();
    
    }
    }
    
    o เขียนการทำงานเมื่อกดปุ่ม “Export”
    protected void btnExport_Click(object sender, EventArgs e)
    { 
     string fileName = Path.Combine(Server.MapPath("~/ImportDocument"), Guid.NewGuid().ToString() + ".xls");
    //เรียกใช้เมธอดในการ Export ข้อมูลแบบ Multiple sheet
    ExportToMultipleXLSheets(fileName);
    
    }

    หลักการทำงานคร่าวๆในการ Export ข้อมูลใน 2 ลักษณะจะมีหลักการพื้นฐานคล้ายกัน ซึ่งจะสรุปได้ดังนี้

    • ดึงข้อมูลจากฐานข้อมูลมาเก็บไว้ใน Datadatable หรือ Dataset
    • กำหนดชื่อไฟล์ที่จะใช้ในการเชื่อมต่อกับ OleDb
    • เชื่อมต่อกับ OleDb โดยการกำหนดค่าต่างๆ เพื่อใช้ในการทำงานกับไฟล์ Excel นั้นๆ
    • เตรียมกำหนดคำสั่ง sql command ในการสร้างโครงสร้างตาราง(create table) และ เพิ่มข้อมูล (insert) ในตารางที่สร้างไว้ หากเป็นกรณีที่ต้องการสร้างแบบ Multiple sheet
      ก็จะทำการเตรียมชุดคำสั่งไว้ และนำไปวน run คำสั่งนั้นๆตามจำนวนรอบที่มีการสร้าง WorkSheet ใหม่นั่นเอง
    • สั่ง run คำสั่งดังกล่าว หากจำนวนแถวของข้อมูลเกินกว่าที่กำหนดต่อ 1 WorkSheet (ซึ่งในกรณีสมมติให้เป็น 3 แถว) จะทำการสร้าง WorkSheet ใหม่
    • อ่านค่าที่ได้จากไฟล์ที่เชื่อมต่อกับ OleDb และผ่านกระบวนการ Export ข้อมูล  มาเขียนเป็นไฟล์ Excel เพื่อให้ผู้ใช้สามารถบันทึกข้อมูลในรูปแบบไฟล์ที่ Export ได้
    • หากเป็นการ Export ข้อมูลแบบ Multiple sheet จำนวนของ WorkSheet จะขึ้นอยู่กับข้อมูลที่ดึงมาและจำนวนที่กำหนดไว้ต่อ 1 WorkSheet นั่นเอง
    หมายเหตุ : Namespace ที่ต้องอ้างอิงเพิ่มเติมในการใช้งานโค้ดที่กล่าวไว้ข้างต้น มีดังนี้
                –  System.IO
                –  System.Data.OleDb
                –  System.Data
                –  System.Text

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

    แหล่งข้อมูลอ้างอิง :
    http://www.codeproject.com/Articles/33271/Import-and-Export-to-Multiple-Worksheets
    http://dotnetawesome.blogspot.com/2013/11/how-to-import-export-database-data-from_18.html

  • เรียนรู้วิธีการ Import ข้อมูลในรูปแบบไฟล์ Excel ในแบบ Single และ Multiple sheet ด้วย ASP.NET (C#)

                “ข้อมูล” นับว่าเป็นส่วนหนึ่งที่มีความสำคัญในการทำงานหรือการแสดงผลข้อมูลของระบบหรือเว็บไซต์ในปัจจุบัน ซึ่งอาจมีที่มาจากการจัดการเองผ่านระบบจัดการข้อมูล (Back office) หรือมีการนำเข้าจากแหล่งอื่นเนื่องจากข้อมูลที่ต้องการบันทึกเข้าสู่ระบบดังกล่าวอาจมีจำนวนมาก ทำให้การป้อนข้อมูลผ่านระบบทีละรายการเป็นไปอย่างลำบากและเกิดความผิดพลาดได้โดยง่าย เช่น ข้อมูลการเงิน ข้อมูลการสั่งซื้อสินค้า เป็นต้น อีกทั้งยังพบว่ามีบางกรณีที่ผู้ใช้มีความต้องการดึงข้อมูลจากฐานข้อมูลและส่งออกมาในรูปแบบไฟล์อื่นๆเพื่อนำไปประมวลผลต่อไปได้โดยง่าย ซึ่งโดยทั่วไปผู้ใช้มักเก็บข้อมูลเหล่านี้ในรูปแบบไฟล์ต่างๆ เช่น ไฟล์ Excel หรือ ไฟล์ Access ดังนั้น นักพัฒนาของระบบที่มีความต้องการจัดการข้อมูลในลักษณะนี้จึงจำเป็นที่จะต้องมีความรู้เกี่ยวกับกระบวนการดังกล่าวเพื่อให้ได้ข้อมูลตามรูปแบบที่ผู้ใช้ต้องการ ซึ่งในบทความนี้ ผู้เขียนขอเสนอวิธีการ Import ข้อมูลให้ออกมาในรูปแบบไฟล์ Excel พัฒนาโดยใช้ ASP.NET ด้วย C# เนื่องจากเป็นกรณีความต้องการที่พบบ่อยและสามารถนำข้อมูลจากไฟล์ไปใช้งานต่อได้โดยง่าย ซึ่งจะพูดทั้งในลักษณะแบบ Single sheet และแบบ Multiple sheet เพื่อที่จะช่วยให้ผู้พัฒนาที่มีความสนใจสามารถเห็นภาพการทำงานโดยรวม และสามารถนำไปประยุกต์ใช้กับระบบที่ท่านกำลังพัฒนาอยู่ได้

    กรณีนำเข้าข้อมูล(Import) ซึ่งในบทความนี้จะแบ่งเป็น 2 ลักษณะ ดังนี้

    • การนำเข้าข้อมูลแบบ Single sheet ถือเป็นการนำเข้าในแบบทั่วไป ไม่ซับซ้อนมากนัก
    ฝั่ง Client
    <%@ Page Language="C#" AutoEventWireup="true" 
    CodeFile="Excel.aspx.cs" Inherits="ExcelTest" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
    <title></title>
    </head>
    <body>
    <form id="form1" runat="server">
    <asp:FileUpload ID="FileUpload1" runat="server" />
    <div>
    <asp:GridView ID="GridView" runat="server">
    </asp:GridView>
    <asp:Button ID="btnImport" runat="server" 
    onclick="btnImport_Click" Text="Import" />
    </div>
    </form>
    </body>
    </html>
    ฝั่งเซิร์ฟเวอร์
    protected void btnImport_Click(object sender, EventArgs e)
    {
    try
    {
    ////เป็นการกำหนดชื่อของไฟล์ที่ต้องการจะบันทึกลงเซิร์ฟเวอร์ ซึ่งมีการระบุพาธรวมทั้งนามสกุลของไฟล์ตามไฟล์ที่รับเข้ามา
    string fileName =  Path.Combine(Server.MapPath("~/ImportDocument"), Guid.NewGuid().ToString() + Path.GetExtension(FileUpload1.PostedFile.FileName));
    
    ////บันทึกไฟล์ดังกล่าวลงเซิร์ฟเวอร์
    FileUpload1.PostedFile.SaveAs(fileName);
    
    string conString = "";
    
    string ext = Path.GetExtension(FileUpload1.PostedFile.FileName);
    
    ////เป็นส่วนของเงื่อนไขในการตั้งค่า ConnectionString ในการอ่านไฟล์ Excel ด้วย OleDb ซึ่งจะแยกด้วยนามสกุลของไฟล์ Excel ที่รับมา
    if (Path.GetExtension(ext) == ".xls")
    {
    conString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" +
     fileName + ";Extended Properties=\"Excel 8.0;HDR=Yes;IMEX=2\"";
    }
    else if (Path.GetExtension(ext) == ".xlsx")
    {
    conString = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + 
    fileName + ";Extended Properties='Excel 12.0;HDR=YES;IMEX=1;';";
    }
    
    ////เป็นการเปิดการเชื่อมต่อผ่าน OleDb
    OleDbConnection con = new OleDbConnection(conString);
    
    if (con.State == System.Data.ConnectionState.Closed)
    {
    con.Open();
    }
    
    
    DataTable dtExcelSchema;
    
    dtExcelSchema = con.GetOleDbSchemaTable(OleDbSchemaGuid.Tables,
     null);
    ////ดึงค่าชื่อของ Worksheet ที่อ่านมาจากไฟล์ Excel ที่รับเข้ามา
    string SheetName = dtExcelSchema.Rows[0]["TABLE_NAME"].ToString();
    
    ////เขียนคำสั่งในการดึงข้อมูลจาก Worksheet ดังกล่าว ซึ่งลักษณะการทำงานจะคล้ายกับการเขียนคำสั่ง sql command ในการดึงข้อมูลตารางโดยทั่วไป และเปรียบ Worksheet นั้นเป็นตาราง
    string query = "Select *  from [" + SheetName + "]";
    
    OleDbCommand cmd = new OleDbCommand(query, con);
    OleDbDataAdapter da = new OleDbDataAdapter(cmd);
    DataSet ds = new DataSet();
    da.Fill(ds);
    da.Dispose();
    con.Close();
    con.Dispose();
    ////เป็นการสมมติโครงการสร้างตาราง หากเป็นการทำงานจริงส่วนนี้จะหมายถึงตารางในฐานข้อมูลของแต่ละระบบ
    DataTable table = new DataTable();
    table.Columns.Add("EmployeeID", typeof(string));
    table.Columns.Add("EmployeeName", typeof(string));
    ////เป็นการวนค่าเพื่อบันทึกลงฐานข้อมูล แต่ในกรณีนี้จะเป็นเพียงแค่การเพิ่มแถวข้อมูลลงใน datatable ที่ชื่อ table เท่านั้น
    foreach (DataRow dr in ds.Tables[0].Rows)
    {
    //// dr["EmployeeID"].ToString() ชื่อของค่าฟิลด์ต้องตรงกับชื่อของคอลัมน์ใน Worksheet ที่อ่านมาจากไฟล์ Excel
    table.Rows.Add(dr["EmployeeID"].ToString(), dr["EmployeeName"].ToString());
    }
    ////นำค่าที่ได้แสดงในกริดวิว
    GridView.DataSource = table;
    GridView.DataBind();
    
    }
    catch (Exception)
    {
    throw;
    }
    }
    • การนำเข้าข้อมูลแบบ Multiple Sheet จะใช้ในกรณีที่มีจำนวนของ Worksheet ไม่จำกัด ขึ้นอยู่กับข้อมูล ซึ่งจะมีความซับซ้อนกว่าแบบแรก โดยหลักการทำงานโดยสรุปจะเป็นในลักษณะของการดึงข้อมูล Worksheet ที่มีทั้งหมดในไฟล์ Excel ที่อ่านได้ และนำไปเพื่อวนอ่านค่าข้อมูลในแต่ละชีทและนำค่าเหล่านั้นลงฐานข้อมูล ซึ่งจะอธิบายเป็นส่วนๆดังนี้
    1. การดึงข้อมูลชื่อ Worksheet ที่มีทั้งหมดในไฟล์ที่รับเข้ามา
     public static string[] getExcelSheets(string mFile)
    {
    try
    {
    string strXlsConnString;
    strXlsConnString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + mFile + ";Extended Properties='Excel 8.0;HDR=Yes;IMEX=1'";
    OleDbConnection xlsConn = new OleDbConnection(strXlsConnString);
    xlsConn.Open();
    
    ////เป็นการดึงค่าชื่อ Worksheet ของไฟล์ excel ที่กำลังอ่าน โดยตารางหรือชีทใน Excel จะมีสัญลักษณ์ $ ต่อท้ายชื่อเสมอ
    DataTable xlTable = new DataTable();
    xlTable = xlsConn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
    System.String strExcelSheetNames = "";
    string sheetName;
    for (int lngStart = 0; lngStart < xlTable.Rows.Count; 
    lngStart++)
    {
    ////เป็นการเอา '' ออกจากชื่อตาราง/worksheet ที่ดึงมาได้
    sheetName = xlTable.Rows[lngStart][2].ToString().Replace("'", "");
    
    ////เป็นการคัดกรองเฉพาะตัวที่เป็นตารางหรือworksheet เนื่องจากจบด้วย $
    if (sheetName.EndsWith("$"))
    {
    ////เป็นการเชื่อมตัวสุดท้ายด้วย ~ เพื่อใช้ในการตัดคำในขั้นตอนถัดไป
    strExcelSheetNames += sheetName.Substring(0, sheetName.Length - 1) + "~";
    }
    }
    ////เป็นการตัด  ~ ตัวสุดท้ายออกจากการเชื่อมคำ
    if (strExcelSheetNames.EndsWith("~"))
    {
    strExcelSheetNames = strExcelSheetNames.Substring(0,
    strExcelSheetNames.Length - 1);
    }
    xlsConn.Close();
    xlsConn.Dispose();
    char[] chrDelimter = { '~' };
    ////เป็นการตัดคำด้วย ~ และส่งค่าตัวแปร array ของ string ที่เป็นชื่อ worksheet ทั้งหมดที่อ่านได้กลับไป
    return strExcelSheetNames.Split(chrDelimter);
    
    }
    catch (Exception exp)
    {
    throw new Exception("Error while listing the excel" +
    " sheets from upload file " + exp.Message, exp);
    }
    }
    2. การอ่านค่าข้อมูลใน Worksheet ที่มีทั้งหมดในไฟล์ที่รับเข้ามา โดยมีการส่งค่าของชื่อ Worksheet และชื่อของไฟล์ที่อ่าน รวมทั้งฟิลด์เพิ่มเติมที่ต้องการระบุในการอ่านค่า ซึ่งจะทำงานในลักษณะเดียวกับการ Import แบบ Single sheet นั่นเอง
    public static DataSet getXLData(string xlSheetName,
    string xlFileName, string AdditionalFields)
    
    {
    try
    {
    string connstr = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" 
    +  xlFileName + ";Extended Properties='Excel 8.0;HDR=Yes;IMEX=1'";
    OleDbConnection xlConn = new OleDbConnection(connstr);
    DataSet xlTDS = new DataSet("xlDataSet");
    xlConn.Open();
    OleDbDataAdapter xlDA = new OleDbDataAdapter("Select" + AdditionalFields +  " * from [" + xlSheetName + "$] ", xlConn);
    xlDA.Fill(xlTDS);
    xlConn.Close();
    xlConn.Dispose();
    
    ////เป็นการลบแถวที่มีค่าว่างออกจากการอ่านข้อมูลในไฟล์
    RemoveEmptyRows(xlTDS.Tables[0], (AdditionalFields.Length -
    
    AdditionalFields.ToLower().Replace(" as ", "").Length) / 4);
    
    return xlTDS;
    }
    catch (Exception e)
    {
    throw new Exception("Error while reading data from excel sheet", e);
    }
    }
    
    public static void RemoveEmptyRows(DataTable dtbl,
    
    System.Int32 intNumberOfFieldsToIgnore) ////เป็นการตรวจสอบค่าว่างในแต่ละแถว
    System.String strFilter = "";
    System.Int32 intAvgColsToCheck =
    Convert.ToInt32((dtbl.Columns.Count - intNumberOfFieldsToIgnore) * 0.75);
    if (intAvgColsToCheck < 3)
    {
    intAvgColsToCheck = dtbl.Columns.Count;
    }
    System.Int32 lngEnd = dtbl.Columns.Count;
    lngEnd = lngEnd - intAvgColsToCheck;
    
    ////เป็นการเชื่อมเงื่อนไขในการดึงข้อมูลว่าให้ฟิลด์ใดบ้างที่ห้ามเป็นค่าว่าง ในที่นี้จะทำการวนดูคอลัมน์ที่มีใน worksheet นั้นๆ และเชื่อมเป็นเงื่อนไข
    for (int lngStartColumn = dtbl.Columns.Count;
    lngStartColumn > lngEnd; lngStartColumn--)
    {
    strFilter += "[" + dtbl.Columns[lngStartColumn - 1].ColumnName +
    "] IS NULL AND ";
    
    }
    
    ////ทำในกรณีที่มีอย่างน้อย 1 คอลัมน์ถูกเพิ่มเป็นเงื่อนไขในการตรวจสอบค่าว่าง และลบคำว่า “AND” สุดท้ายออก เพื่อนำไปใช้งานในการกรองข้อมูลตามเงื่อนไขนี้
    if (strFilter.Length > 1)
    {
    strFilter = strFilter.Remove(strFilter.Length - 4);
    
    }
    DataRow[] drows = dtbl.Select(strFilter);
    ////ลบแถวเมื่อพบว่าค่าของฟิลด์นั้นๆ เป็นค่าว่าง
    foreach (DataRow drow in drows)
    {
    dtbl.Rows.Remove(drow);
    }
    }
    3. เขียนการทำงานเมื่อกดปุ่ม “Import”
    protected void btnImport_Click(object sender, EventArgs e)
    
    {
    ////เรียกใช้เมธอดในการบันทึกไฟล์ Excel ที่รับเข้ามาตามพาธของโฟลเดอร์ที่กำหนด
    string fileName = uploadXLFile(FileUpload, Server.MapPath("~/ImportDocument"));
    
    ////เรียกใช้เมธอดในการดึงค่าชื่อ WorkSheet ที่มีทั้งหมดในไฟล์ โดยมีตัวแปร array ชนิด string มารับข้อมูลดังกล่าว
    string[] listExcelSheet = getExcelSheets(fileName);
    
    DataSet DSTotal = new DataSet();
    ////วนลูปข้อมูล WorkSheet ตามชื่อในตัวแปร array และส่งค่าให้กับเมธอดที่ใช้ในการอ่านค่าข้อมูลในแต่ละ WorkSheet นั้น
    for (int i = 0; i < listExcelSheet.Count(); i++)
    {
    DataSet DS = getXLData(listExcelSheet[i], fileName, "");
    DSTotal.Merge(DS);
    
    }
    
    ////แสดงผลตัวอย่างข้อมูลที่อ่านได้ในกริดวิว ซึ่งในการใช้งานจริงในส่วนนี้ผู้พัฒนาจะต้องนำข้อมูลที่อ่านได้เหล่านี้วนบันทึกลงฐานข้อมูลเช่นเดียวกับที่กล่าวไว้ในการ Import ข้อมูลแบบ Single sheet นั่นเอง
    if (DSTotal.Tables[0].Rows.Count > 0)
    {
    GvData.DataSource = DSTotal.Tables[0];
    GvData.DataBind();
    
    }
    }
    }
    
     public static string uploadXLFile(FileUpload fileUpload, string mPath)
     {
     mPath = Path.Combine(mPath ,Guid.NewGuid().ToString() + Path.GetExtension(fileUpload.PostedFile.FileName));
     fileUpload.SaveAs(mPath);
     return mPath;
     }
    
    

     

    จะเห็นว่าจริงๆแล้วการทำงานใน 2 ลักษณะจะมีหลักการพื้นฐานคล้ายกัน ซึ่งจะสรุปได้ดังนี้

    • บันทึกไฟล์ Excel บนเซิร์ฟเวอร์ตามพาธที่กำหนดเพื่อให้สามารถเรียกอ่านค่าได้
    • เชื่อมต่อกับ OleDb โดยการกำหนดค่าต่างๆ เพื่อใช้ในการอ่านค่าจากไฟล์ Excel นั้นๆ
    • กำหนดคำสั่ง sql command ในการดึงข้อมูลจาก WorkSheet ซึ่งต้องมีการระบุชื่อของ WorkSheet นั้นๆ
    • สั่ง run คำสั่งดังกล่าวและนำค่าที่ได้ไปประมวลผลต่อไป
    • หากเป็นกรณีแบบ Multiple sheet เราจะไม่สามารถทราบจำนวนและชื่อของ WorkSheet ตายตัว จึงต้องเพิ่มการทำงานที่ทำการวนค่าเพื่อดึงข้อมูลชื่อ WorkSheet และทำตามกระบวนการต่อไป
    หมายเหตุ : Namespace ที่ต้องอ้างอิงเพิ่มเติมในการใช้งานโค้ดที่กล่าวไว้ข้างต้น มีดังนี้
                –  System.IO
                –  System.Data.OleDb
                –  System.Data
                –  System.Text

                สำหรับในบทความนี้ผู้เขียนจะขอเสนอวิธีการ Import ข้อมูลด้วยไฟล์ Excel ไว้เพียงเท่านี้ก่อน หากมีผู้รู้ท่านใดมีข้อเสนอแนะที่ต้องการแลกเปลี่ยนความรู้ร่วมกัน สามารถชี้แจงเพิ่มเติมได้เป็นกรณีศึกษาเพื่อการเรียนรู้ หากผิดพลาดประการใด ผู้เขียนขออภัยไว้ ณ ที่นี้ค่ะ และสำหรับท่านผู้พัฒนาที่มีความสนใจเกี่ยวกับการ Export ข้อมูลไฟล์ Excel ด้วย ASP.NET(C#) สามารถติดตามต่อได้ใน Part II นะคะ

    แหล่งข้อมูลอ้างอิง :
     http://www.codeproject.com/Articles/33271/Import-and-Export-to-Multiple-Worksheets
    http://dotnetawesome.blogspot.com/2013/11/how-to-import-export-database-data-from_18.html

     

  • รวมเทคนิคการออกแบบ UI ให้สวยงามสำหรับ Designer มือใหม่ (ตอนที่ 3 จบ)

    สำหรับผู้ที่ต้องการอ่านบทความก่อนหน้านี้ ตามลิงค์ด้านล่างได้เลยครับ:
    รวมเทคนิคการออกแบบ UI ให้สวยงามสำหรับ Designer มือใหม่ (ตอนที่ 1)
    รวมเทคนิคการออกแบบ UI ให้สวยงามสำหรับ Designer มือใหม่ (ตอนที่ 2)

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

    แนวคิดที่ 21 : Try Exposing Options instead of hiding them.
    _____แนวคิดนี้กล่าวถึงการนำเอา DropdownList มาใช้สำหรับให้ผู้ใช้เลือกตัวเลือกสำคัญๆในหน้าจอนั้น อาจทำให้ผู้ใช้เลือกตัวเลือกไม่ตรงตามที่ต้องการหรือเกิดข้อผิดพลาดจากการคลิก ทำให้ข้อมูลที่ได้ไม่ตรงตามต้องการ หรือส่งผลให้เป็นการโน้มน้าวใจผู้ใช้ให้เลือกตัวเลือกที่ถูกตั้งค่าเริ่มต้นกรณีที่ผู้ใช้ยังตัดสินใจไม่ได้ หรือในกรณีตัวเลือกที่มีให้เลือกนั้นมีจำนวนน้อย นอกจากการใช้ Dropdownlist ก็อาจเปลี่ยนมาใช้ Radio Button แทนได้ จะทำให้ผู้ใช้สามารถเห็นตัวเลือกได้อย่างชัดเจน ลดขั้นตอนในการทำงานของผู้ใช้ได้อีกด้วย DropdownList จึงเหมาะกับกรณีที่มีตัวเลือกที่หลากหลายมากกว่าidea014

    แนวคิดที่ 22 : Try Showing State instead of being state agnostic.
    _____ในหัวข้อที่ผ่านมาได้มีการกล่าวถึงการออกแบบให้รองรับสำหรับการแสดงผลแบบตารางกรณีที่ไม่มีค่าข้อมูลหรือเท่ากับ 0 นั้นเอง ซึ่งในหัวข้อนี้จะกล่าวถึงการออกแบบสำหรับกรณีที่มีข้อมูลที่นอกจากจะนำข้อมูลมาแสดงผลแล้ว การแจ้งสถานะของข้อมูลในแต่ละรายการ ก็เป็นส่วนหนึ่งที่จะช่วยให้ผู้ใช้รับทราบถึงผลการทำงานได้ เช่นการแสดงผลของรายการอีเมล์พร้อมทั้งแสดงสถานะว่าอ่านแล้วหรือยังไม่อ่าน รายการเรียกเก็บภาษีพร้อมแสดงสถานะว่ารายการนี้ชำระแล้ว เป็นต้น ส่งผลให้ผู้ใช้รู้ว่าควรจะดำเนินการส่วนไหนต่อ หรือสิ่งที่ได้กระทำลงไปนั้นได้ผลลัพธ์เป็นเช่นไรidea017

    แนวคิดที่ 23 : Try Direct Manipulation instead of contextless menus.
    _____ก่อนหน้านี้มีแนวคิดเกี่ยวกับการให้รวมเมนูต่างๆที่ซ้ำกัน มาไว้ในที่เดียวกันทั้งหมด เพื่อผู้ใช้จะสะดวกในการเรียกใช้เมนู แต่ในกรณีของแนวคิดนี้จะเป็นการให้เราแยกเมนูต่างๆลงไปยังรายการนั้นๆ เมื่อข้อมูลในแต่ละรายการ มีการเรียกใช้เมนูที่ไม่เหมือนกันหรือผู้ใช้สามารถจัดการกับข้อมูลแต่ละรายการได้มากน้อยไม่เท่ากัน การออกแบบให้เมนูอยู่คู่กับรายการจึงเป็นสิ่งที่ทำให้ผู้ใช้ไม่สับสนได้ว่าทำไมบางรายการถึงกดใช้เมนูในแถบเมนูไม่ได้ และลดขั้นตอนที่ผู้ใช้ต้องทำการเลือกรายการก่อนถึงจะไปเรียกใช้เมนูได้ ถามว่าแนวคิดไหนผิด ผู้เขียนขอตอบว่าไม่มีแบบไหนผิด แต่ขึ้นอยู่กับการนำไปใช้งานมากกว่าครับidea019

    แนวคิดที่ 24 : Try Opt-Out instead of opt-in.
    _____ในการออกแบบเงื่อนไขแบบ 2 ตัวเลือกนั้น ปัจจุบันส่วนใหญ่นักออกแบบจะใช้ Checkbox มาใช้ในการเลือกเงื่อนไขกันอย่างแพร่หลาย เช่น ให้ติ๊กยอมรับผล หรือให้ติ๊กถ้าต้องการอีเมล์ตอบรับจากระบบ เป็นต้น แต่ในความเป็นจริงแล้ว การที่เราออกแบบโดยใช้ Checkbox จะทำให้ผู้ใช้เสียโอกาสต่างๆพอสมควรและเป็นการแสดงตัวเลือกได้ไม่ชัดเจนนักเนื่องจากบางครั้งผู้ใช้อาจลืมไม่ได้เลือกติ๊กรายการต่างๆ การเปลี่ยนมาใช้ Radio Button เราจะสามารถทราบถึงผลลัพธ์ ที่แน่นอนกว่าและลดกรณีที่ผู้ใช้ลืมไม่ได้ติ๊กเลือกรายการที่ต้องการได้ และไม่เป็นการเอารัดเอาเปรียบผู้ใช้อีกด้วยidea026

    แนวคิดที่ 25 : Try Smart Defaults instead of asking to do extra work.
    _____มาตั้งค่าเริ่มต้นกันเถอะ เมื่อพูดถึงค่าเริ่มต้นในการกรอกข้อมูลแล้ว อาจเป็นสิ่งที่ทำได้ค่อนข้างยาก ยิ่งเป็นระบบที่กลุ่มผู้ใช้หลากหลาย การกำหนดค่าเริ่มต้นจึงทำได้ยากมาก โดยส่วนใหญ่จะยึดค่าเริ่มต้นตามกลุ่มผู้ใช้งานส่วนใหญ่ ซึ่งค่าเหล่านี้ต้องได้มาจากการรวบรวมข้อมูลของกลุ่มผู้ใช้และใช้ประสบการณ์ของผู้ออกแบบมากพอสมควร ซึ่งข้อนี้ผู้เขียนคิดว่าต้องระวังเป็นพิเศษ ถ้าออกแบบมาดีตรงตามกลุ่มผู้ใช้ส่วนใหญ่ ก็จะส่งผลให้ผู้ใช้สะดวกสบายในการกรอกข้อมูลยิ่งขึ้นไปด้วย แถมเป็นการลดเวลาในการใช้งานระบบไปในตัว แต่ถ้าออกแบบมาไม่ดี จะส่งผลให้ผู้ใช้เสียเวลามากขึ้นตามไปด้วยเช่นกันidea028

    แนวคิดที่ 26 : Try Inline Validation instead of delaying errors.
    _____การกรอกข้อมูลมักจะมาคู่กับการออกแบบเงื่อนไขเพื่อรองรับความผิดพลาดจากการกรอกข้อมูล ซึ่งผู้ออกแบบสามารถใช้เครื่องมือ Validater มาช่วยในการตรวจสอบข้อมูลตามเงื่อนไขที่เราต้องการได้ และเมื่อผู้ใช้งานกรอกข้อมูลไม่ตรงตามเงื่อนไข ก็ควรที่จะแจ้งเตือนความผิดพลาดทันทีเมื่อจบการทำงานในส่วนของการกรอกข้อมูลช่องนั้นๆ แทนการแจ้งเตือนความผิดพลาดทั้งหมดภายหลัง เพื่อผู้ใช้ไม่ต้องเสียเวลาในการย้อนกลับมาตรวจสอบข้อมูลว่าส่วนไหนของรายการที่ผิดพลาดไป และลดความกลัวหรือกังวลให้กับผู้ใช้ได้กรณีที่ผู้ใช้ทราบผลการแจ้งเตือนของข้อมูลที่ผิดพลาดหลายรายการพร้อมๆกันidea033

    แนวคิดที่ 27 : Try Forgiving Inputs instead of being strict with data.
    _____แนวคิดนี้ต่อเนื่องมาจากข้อก่อนหน้านี้ที่พูดถึงการตั้งค่าเงื่อนไขเพื่อตรวจสอบข้อมูลที่ผู้ใช้กรอกเข้าสู่ระบบ ซึ่งควรออกแบบให้มีความหลากหลายในการกรอกข้อมูลแทนที่การจำกัดเงื่อนไขในการกรอกแบบตายตัว ซึ่งจะทำให้ผู้ใช้รู้สึกไม่เป็นมิตรกับระบบได้ แต่การออกแบบให้รองรับการกรอกข้อมูลหลายรูปแบบนั้น ค่อยข้างจะทำได้ยากและเสียเวลาในส่วนของการพัฒนาโปรแกรม เช่นการกรอกข้อมูลเบอร์โทรศัพท์ รูปแบบในการกรอกค่อนข้างมีหลากหลาย ไม่ว่าจะใส่วงเล็บ ใส่เครื่องหมายขีดกลาง แม้กระทั้งเว้นวรรคระหว่างกลุ่มตัวเลข ในด้านการเขียนโปรแกรมอาจเขียนได้ยากพอสมควร แต่สำหรับด้านการใช้งานถือว่าเป็นการอำนวยความสะดวกและลดเวลาในการกรอกข้อมูลแก่ผู้ใช้ได้เช่นกัน idea034

    แนวคิดที่ 28 : Try Progressive Disclosure instead of overwhelming.
    _____ในการออกแบบแบบสอบถามผ่านระบบนั้น ผู้ออกแบบไม่ควรสร้างแบบสอบถามที่แสดงคำถามทั้งหมดให้ผู้ใช้ตอบถึงแม้บางเงื่อนไข ผู้ใช้ไม่จำเป็นที่จะตอบคำถามในข้อนั้นๆ หรือคำถามดังกล่าวไม่เกี่ยวข้องกับผู้ใช้เลย การออกแบบให้การตอบคำถามข้อนั้นๆเป็นการนำไปสู่คำถามข้อถัดไปจะช่วยลดคำถามที่ไม่จำเป็นกับผู้ใช้ได้ ส่งผลให้ผู้ใช้ไม่เกิดความสับสนหรือรู้สึกไม่ดีกับการตอบแบบสอบถามidea043

    แนวคิดที่ 29 : Try Useful Calculations instead of asking to do math.
    _____แนวคิดนี้เป็นตัวช่วยที่เอื้ออำนวยความสะดวกให้กับผู้ใช้ได้คล้ายๆกับกรณีที่ให้มีการแจ้งเตือนสถานะของรายการข้อมูล โดยจะกล่าวถึงการคำนวนค่าต่างๆที่สามารถทำได้ในระบบให้ผู้ใช้ทราบได้ เช่นการแจ้งเตือนหมดอายุการเป็นสมาชิกในรายการต่างๆ การบอกระยะเวลาของอีเมล์ที่ได้รับแทนการบอกเป็นวันเวลาแบบตรงๆซึ่งผู้ใช้ต้องเสียเวลาในการคิดคำนวนว่าอีเมล์ดังกล่าวมาถึงนานแล้วหรือไม่ เราอาจนำเอาแนวคิดก่อนหน้านี้มาใช้ร่วมกันได้ เพื่อเพิ่มความสะดวกสบายให้แก่ผู้ใช้งานมากยิ่งขึ้นidea053

    แนวคิดที่ 30 : Try Responsive Layouts instead of static ones.
    _____เนื่องด้วยปัจจุบันความก้าวหน้าทางเทคโนโลยีและการให้บริการอินเตอร์เน็ตความเร็วสูงมีการพัฒนาอย่างต่อเนื่อง การเข้าถึงข้อมูลข่าวสารเป็นไปได้อย่างรวดเร็วและทั่วถึง รวมถึงในปัจจุบันได้มีเครื่องมือสื่อสารหลายขนาดและรูปแบบ ไม่ว่าจะเป็นโทรศัพท์ Tablet หรือแม้แต่โทรทัศน์เองก็สามารถใช้งานอินเตอร์เน็ตได้เช่นกัน ซึ่งมีคุณสมบัติที่แตกต่างกันออกไป เช่น ความกว้าง ความสูง ความละเอียดหน้าจอ เป็นต้น ทำให้การออกแบบหน้าจอต้องคำนึงถึงการแสดงผลที่รองรับกับอุปกรณ์ต่างๆเช่นกันแทนการออกแบบให้หน้าจอแสดงผลได้เพียงบนคอมพิวเตอร์อย่างเดียว ส่งผลให้ผู้ใช้ได้รับความสะดวกสบายในการใช้งานไม่ว่าจากคอมพิวเตอร์หรือโทรศัพท์ก็ตามidea070

    สรุปส่งท้าย
    _____สุดท้ายนี้ต้องขอขอบคุณทุกท่านที่อ่านมาจนจบทั้ง 3 ตอน ซึ่งจริงๆแล้วยังมีอีกหลายแนวคิดที่ผู้เขียนไม่ได้นำเอามาเขียนในชุดบทความนี้ ผู้ที่สนใจสามารถเข้าไปอ่านได้ที่ GoodUI.com เลยครับ และขออภัยถ้าอ่านแล้วไม่เข้าใจหรือผู้เขียนแปลความหมายผิดไปจากต้นฉบับ และถ้าต้องการเสริมในหัวข้อไหน สามารถมา Comment บอกกันได้ครับ จะรู้สึกเป็นเกียรติอย่างยิ่ง สุดท้ายนี้หวังว่าชุดบทความนี้จะเป็นประโยชน์กับผู้อ่านไม่มากก็น้อยนะครับ

    ขอบคุณครับ

  • รวมเทคนิคการออกแบบ UI ให้สวยงามสำหรับ Designer มือใหม่ (ตอนที่ 2)

    ใครยังไม่ได้อ่านตอนที่ 1 แนะนำให้อ่านก่อนครับ ที่: รวมเทคนิคการออกแบบ UI ให้สวยงามสำหรับ Designer มือใหม่ (ตอนที่ 1)

    มาต่อกันกับบทความรวมเทคนิคการออกแบบ UI ให้สวยงามสำหรับ Designer มือใหม่ ตอนที่ 2 ซึ่งในชุดบทความนี้จะมีทั้งหมด 3 ตอน เพื่อไม่ให้เป็นการเสียเวลาเราไปเริ่มข้อต่อไปกันเลยดีกว่าครับ

    แนวคิดที่ 11 : Try Merging Similar Functions instead of fragmenting the UI.
    _____แนวคิดนี้จะพูดถึงการจัดกลุ่มเมนูหรือลิงค์ที่มีการทำงานไปยังส่วนเดียวกันหรือเปิดไปทำงานในอีกหน้าจอ การออกแบบให้สิ่งเหล่านี้กระจายไปอยู่ตามจุดต่างๆของหน้าจอ อาจทำให้ดูเหมือนเข้าถึงได้ง่าย แต่จริงๆแล้วกลับส่งผลให้หน้าจอดูรกขึ้นมาโดยทันที และอาจจะทำให้ผู้ใช้งานสับสนได้กับเมนูที่มีซ้ำกันในหน้าจอ การย้ายเมนูหรือลิงค์ดังกล่าวมาอยู่รวมกันเป็นเมนูเดียวหรือสร้างเป็นเมนูย่อยจะทำให้หน้าจอของเรามีพื้นที่ใช้งานมากขึ้นด้วยครับidea003

    แนวคิดที่ 12 : Try Repeating Your Primary Action instead of showing it just once.
    _____สำหรับนักออกแบบ UI หลายท่าน การออกแบบให้มีปุ่มหรือลิงค์หลักกับผู้ใช้เพียงจุดเดียวเป็นสิ่งที่ดูเหมาะสมแล้ว แต่เมื่อนำไปใช้งานจริงบางกรณีอาจทำให้ผู้ใช้งานหน้าจอไม่สะดวกในการใช้งาน มักจะเกิดกับหน้าจอที่มีข้อมูลมากส่งผลให้เกิดสกอร์บาร์ขึ้น เมื่อผู้ใช้งานเลื่อนหน้าจอขึ้นลง ปุ่มหรือลิงค์หลักอาจหายไปจากหน้าจอได้ ส่งผลให้ผู้ใช้งานต้องเลื่อนหน้าจอไปมาเพื่อที่จะกลับไปกดปุ่มหรือลิงค์ดังกล่าว กรณีนี้ควรเพิ่มปุ่มหรือลิงค์ขึ้นมาในส่วนล่างสุดและบนสุดของหน้าจอ โดยปุ่มหรือลิงค์ดังกล่าวควรมีหน้าตาที่เหมือนหรือคล้ายกันเพื่อไม่ให้ผู้ใช้เกิดความสับสนidea005

    แนวคิดที่ 13 : Try Suggesting Continuity instead of false bottoms.
    _____จากที่กล่าวไปแล้วก่อนหน้านี้ในการออกแบบรูปแบบการแสดงผลของบทความแบบคอลัมน์เดียวนั้น กรณีที่บทความนั้นยาวมาก หรือเป็นบทความหลายๆเรื่องต่อๆกันไปนั้น ผู้ออกแบบหน้าจอควรมีการเพิ่มช่องว่างหรือลำดับที่บอกถึงการเปลี่ยนแปลงของบทความเมื่อมีการขึ้นเรื่องใหม่หรือย่อหน้าใหม่ แทนการออกแบบให้บทความนั้นๆยาวต่อกันไปเรื่อยๆ เมื่อผู้ใช้เข้ามาอ่านบทความแล้วจะได้ไม่เกิดความสับสนหรือหาจุดที่อ่านต่อไม่เจอเมื่อมีการเลื่อนสอร์บาร์ แต่ข้อควรระวังคือ อย่าใส่ช่องว่างระหว่างบทความมากจนเกินไป จนทำให้ผู้ใช้รุ้สึกว่าบทความนั้นๆจบแล้วidea015

    แนวคิดที่ 14 : Try Visual Hierarchy instead of dullness.
    _____แนวคิดนี้เป็นอีกวิธีหนึ่งที่น่าสนใจในการออกแบบหน้าจอแสดงผลบทความแบบคอมลัมน์เดียว คือการจัดตัวนำสายตาหรือก็คือการจัดหน้าจอแบบการย่อหน้าเป็นลำดับชั้น ถ้านึกภาพไม่ออกให้นึกถึงการใส่เลขที่หัวข้อข้อย่อยในเอกสารจำพวก MS Word จะมีการเลื่อนระดับย่อหน้าเข้าไปเรื่อยๆตามลำดับชั้นที่เล็กลงไป วิธีนี้จะง่ายกับผู้ที่กำลังอ่านบทความได้ เพราะผู้อ่านจะรับรู้ได้ว่าบทความนี้จะจบลงที่ส่วนไหนของหน้าจอและผู้ใช้อาจไม่ต้องเรียนรู้เพิ่มเติมเลย เราสามารถนำวิธีการนี้ไปใช้ร่วมกับการออกแบบในข้อก่อนหน้านี้ได้idea031

    แนวคิดที่ 15 : Try Grouping Related Items instead of disordering.
    _____การจัดกลุ่มรายการเครื่องมือการใช้งานพื้นฐานก็เป็นอีกสิ่งหนึ่งที่จำเป็นสำหรับการออกแบบเพื่อผู้ใช้โดยเฉพาะ ซึ่งการจัดกลุ่มควรยึดตามหลักพื้นฐานทั่วไปในชีวิตประจำวัน เช่นส้อมต้องคู่กับช้อน เป็นต้น หรือจัดกลุ่มตามประสบการ์ณการทำงานของผู้ใช้ เพราะเป็นสิ่งที่ผู้ใช้เข้าใจได้โดยไม่ต้องเรียนรู้เพิ่มเติม การจัดกลุ่มและเรียงลำดับเครื่องมือจึงช่วยให้ผู้ใช้เข้าใจการทำงานของเครื่องมือๆนั้นได้มากขึ้นและสะดวกยิ่งขึ้นidea032

    แนวคิดที่ 16 : Try Thanking instead of simply confirming completion.
    _____การขอบคุณดูเป็นเรื่องปกติทั่วไป แต่เมื่อนำมาใช้ใน UI ของเราจะเป็นสิ่งหนึ่งที่ช่วยยกระดับ UI ของเราให้ดูดียิ่งขึ้น โดยให้มีการออกแบบการแสดงผลข้อความขอบคุณเมื่อผู้ใช้มีการกระทำบางอย่างที่เราต้องการในหน้าจอนั้นๆ นอกจากการแสดงผลรับหรือรายงานผลว่าเสร็จสิ้นแล้ว การเพิ่มข้อความขอบคุณผู้ใช้ จะทำให้ผู้ใช้เกิดความรู้สีกว่าตัวเองมีคุณค่าและได้รับความสนใจจากระบบของเรา ส่งผลให้ความรู้สึกของผู้ใช้ต่อระบบในแง่ดีเพิ่มมากขึ้นตามไปด้วย และเป็นการกระตุ้นให้ผู้ใช้กลับมาใช้งานระบบของเราอีกครั้งidea052

    แนวคิดที่ 17 : Try Softer Prompts instead of modal windows.
    _____Popup ถือเป็นส่วนหนึ่งในการตอบโต้กับผู้ใช้ ซึ่งผู้เขียนได้กล่าวถึงไปแล้วในข้อก่อนหน้านี้ ซึ่งในปัจจุบัน popup ที่นักออกแบบนิยมใช้งานกันอย่างแพร่หลายก็คงไม่พ้น modal popup ข้อดีของมันคือไม่มีการเรียกใช้จาวาสคริปแบบ popup รุ่นก่อนๆส่งผลให้รองรับการทำงานทุกเบราเซอร์และจุดเด่นอีกอย่างคือการล๊อคหน้าจอไม่ให้ผู้ใช้งานทำงานในส่วนของเบื้องหลังได้กรณีที่ popup ทำงานอยู่ แต่จุดเด่นข้อนี้จะกลายเป็นข้อเสียทันทีเมื่อเรานำไปใช้แบบผิดวิธี เช่นการ popup ให้กรอกข้อมูล ที่มีความเกี่ยวเนื่องกับข้อมูลที่อยู่ด้านหลัง popup ซึ่งส่งผลให้ผู้ใช้ย้อนไปดูข้อมูลหรือนำข้อมูลดังกล่าวมาอ้างอิงในการตัดสินใจได้ หรือกรณี popup ทำการแจ้งเตือนอัตโนมัติเมื่อผู้ใช้ไม่ทำการบันทึกข้อมูลเป็นเวลานานๆ หรือผู้ใช้ที่กำลังจดจ่อกับการกรอกข้อมูลหรือกำลังทำงานบางอย่างกับหน้าจออยู่ เมื่อมีการแสดผล popup ขึ้นมา จะเป็นการรบกวนสมาธิหรือขัดจังหวะผู้ใช้งานได้ ส่งผลให้ผู้ใช้งานไม่พอใจหรือตกใจได้ ดังนั้นการนำ popup ประเภทนี้ไปใช้งาน ผู้ออกแบบควรพิจารณาว่าเหมาะสมกับหน้าจอนั้นๆด้วยหรือไม่idea045

    แนวคิดที่ 18 : Try Expectation Setting instead of being ignorant.
    _____การออกแบบหน้าจอให้มีการแจ้งเตือนสถานะของผู้ใช้ในปัจจุบันหรือการแสดงให้ผู้ใช้ทราบว่ากำลังทำงานอยู่ในขั้นตอนไหน และยังเหลืออีกกี่ขั้นตอน หรืออาจออกแบบไปถึงขั้นตอนที่บอกว่าจะได้อะไรในการทำงานในหน้าจอนี้ ส่งผลให้ผู้ใช้งานระบบรู้ถึงจุดประสงค์ของการทำงานในแต่ละขั้นตอนหรือหน้าจอได้ และยังทำให้ผู้ใช้ไม่รู้สึกเป็นกังวลว่ายังเหลือสิ่งที่ต้องทำอีกมากน้อยเพียงใดหรือทำไปเพื่ออะไรidea059

    แนวคิดที่ 19 : Try Providing Feedback instead of silence.
    _____สำหรับแนวคิดนี้ อาจจะดูเป็นเรื่องที่ขาดไม่ได้ในการออกแบบหน้าจอ เพื่อใช้ในการตอบโต้กับผู้ใช้ ทำให้ผู้ใช้รู้สึกว่าระบบมีความเป็นมนุษย์มากขึ้น ซ้ำยังช่วยให้ผู้ใช้ทราบถึงผลลัพธ์จากการกระทำบางอย่างของผู้ใช้ด้วย เช่นเมื่อมีการบันทึกหรือลบข้อมูล หลังจากทำกระบวนการเสร็จสิ้นแล้ว ก็ควรมีข้อความแจ้งเตือนบอกผู้ใช้ให้ทราบถึงผลลัพธ์ว่าสำเร็จลุล่วงหรือไม่ เป็นต้น ส่วนรูปแบบการแสดงผลตอบสนองกับผู้ใช้ก็สามารถออกแบบได้หลายรูปแบบไม่ว่าจะเป็น ในส่วนของ popup หรือแถบข้อความแจกเตือนในหน้าจอidea061

    แนวคิดที่ 20 : Try Explaining instead of assuming the obvious.
    _____แนวคิดข้อนี้จะพูดถึงเกี่ยวกับการใช้คำแนะนำมาช่วยขยายความหัวข้อของการกรอกฟอร์มข้อมูล โดยปกติการออกแบบฟอร์มกรอกข้อมูลจะประกอบไปด้วยส่วนของหัวข้อและส่วนของกล่องข้อความหรือเครื่องมืออื่นๆเช่น dropdownlist checkbox เป็นต้น เพื่อทำให้ฟอร์มนั้นๆดูกระชับน่าใช้งาน แต่บางครั้งคำที่เรานำมาเขียนเป็นหัวข้อนั้นเราคิดว่าอธิบายความหมายหรือสิ่งที่เราต้องการได้หมดครบถ้วนแล้ว แต่สำหรับผู้ใช้บางคำอาจทำให้เกิดความสับสนหรือเข้าใจผิด ส่งผลให้ข้อมูลที่ได้มาจากฟอร์มอาจผิดพลาด การใช้คำมาช่วยอธิบายเพิ่มเติมหรือเขียนเป็นคำถามปลายเปิด จะช่วยให้ผู้ใช้เข้าใจสิ่งที่เราต้องการจากผู้ใช้มากขึ้นidea068


    สำหรับบทความชุด “รวมเทคนิคการออกแบบ UI ให้สวยงามสำหรับ Designer มือใหม่” ตอนที่ 2 ขอจบแต่เพียเท่านี้ สามารถอ่านตอนที่ 3 ได้ตามลิงคด้านล่างเลยครับ

    รวมเทคนิคการออกแบบ UI ให้สวยงามสำหรับ Designer มือใหม่ (ตอนที่ 3 จบ)

    ขอบคุณครับ