Month: May 2021

  • การนับจำนวนข้อมูลโดยใช้ Pivot Table

    ตามปกติหากเราต้องการนับจำนวนข้อมูลที่ซ้ำๆ กัน ว่ามีจำนวนเท่าไหร่ใน Excel หลาย ๆคนคงใช้ฟังก์ชัน COUNTIF ในการนับจำนวนกัน แต่วันนี้มีอีกวิธีที่จะมานำเสนอนั้นคือการใช้ Pivot Table ในการนับจำนวน ซึ่งใช้งานง่ายและไม่ได้ยุ่งยากอย่างที่คิดค่ะ ลองมาดูวิธีกันเลยนะคะ

    1. ใน excel ที่เรามีข้อมูลซ้ำ ๆ กันหน้าตาแบบนี้

    2. ไปที่เมนู Insert เลือก Pivot Table

    3. ปรากฏหน้าจอ Create PivotTable เพื่อให้เลือกกลุ่มของข้อมูลที่ต้องการนับจำนวน ให้เลือกคอลัมน์ที่ต้องการนับจำนวน  แล้วกดปุ่ม OK  ดังรูป

    4. ปรากฏหน้าจอการทำงานของ Pivot Table ให้เลือกลากข้อมูลชื่อ-สกุล มาไว้ในช่อง  Rows และ Values สังเกตุว่าในช่อง Values ใน Excel จะใช้ฟังก์ชัน Count ให้เราโดยอัตโนมัติ ดังรูป

    5. แสดงข้อมูลผลลัพธ์ของการนับจำนวนโดยใช้ Pivot Table ดังรูป

             

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

  • แปลภาษาผ่าน Google Sheets ด้วยสูตร(ไม่)ลับ

    ใน Google Sheets จะมีสูตรที่ชื่อว่า =GOOGLETRANSLATE ใช้เป็นคำสั่ง ให้ดึงคำหรือประโยคภาษาต่าง ๆ เข้าไปแปลให้ใน Google Translate แล้วส่งคำแปลนั้นมาแสดงใน Google Sheets ซึ่งสูตรนี้จะช่วยแปลคำหรือประโยคในภาษาต่าง ๆ ได้อย่างรวดเร็ว แต่มีข้อจำกัดก็คือ มันจะแสดงคำแปลตรงๆ มาให้ ไม่มีคำใกล้เคียง (Synonyms) แสดงมาให้ด้วย สามารถแปลได้ทุกภาษา แต่ต้องมีการปรับสูตรบ้างเล็กน้อย

    เริ่มต้นด้วย : เปิด Google Sheets เพื่อสร้างข้อมูลคำศัพท์ที่ต้องการแปล

    โครงสร้างสูตร
    ชื่อเซลล์ ภาษาที่จะแปล
    =GOOGLETRANSLATE(A2, “th”, “en”)
    หมายความว่า แปลคำศัพท์ที่อยู่ใน “ตำแหน่งของเซลล์ที่มีคำศัพท์หรือประโยค” ตามด้วย
    “ภาษาต้นฉบับ” และ “ภาษาที่จะแปล” เพื่อให้สูตรทำการแปลภาษาได้อย่างถูกต้องตรงตามที่เราต้องการ
    และสามารถ Copy สูตรด้านล่างได้


    หากต้องการแปลภาษาอื่น ๆ สามารถใช้อักษรย่อในแต่ละภาษาแทนได้ เช่น
    อักษรย่อแต่ละภาษา
    th ไทย , en อังกฤษ, zh จีน, k๐ เกาหลี, ja ญี่ปุ่น,ge เยอรมัน
    สำหรับภาษาอื่น ๆ เหนือจากนี้ก็เข้าไปหาได้ในที่ Link https://www.loc.gov/standards/iso639-2/php/code_list.php

    ยกตัวอย่าง เช่น
    แปลภาษาอังกฤษ เป็น ไทย
    = GOOGLETRANSLATE( ตำแหน่งของเซลล์ , “en”, “th”)


    แปลภาษาไทย เป็น อังกฤษ
    = GOOGLETRANSLATE ( ตำแหน่งของเซลล์ , “th”, “en”)


    แปลทุกภาษา เป็น ไทย
    = GOOGLETRANSLATE( ตำแหน่งของเซลล์ , “auto”, “th”)


    แปลทุกภาษา เป็น อังกฤษ
    = GOOGLETRANSLATE( ตำแหน่งของเซลล์ , “auto”, “en”)

    สูตร(ไม่)ลับ นี้มีประโยชน์สำหรับผู้ที่ต้องการแปลคำศัพท์ในภาษาต่าง ๆ มาเป็นภาษาไทย หรือภาษาอื่น ๆ อีกทั้งเพิ่มประสิทธิภาพในการเรียนรู้ และประหยัดเวลาในการเข้าไปหาใน Google Translate อีกด้วย ^^

    ขอบคุณเรื่องราวดี ๆ จาก : https://www.extremeit.com และ https://today.line.me/





  • อัพโหลดแอปพลิเคชันไปยัง App Store ด้วย Transporter

    สำหรับนักพัฒนาแอปพลิเคชัน เพื่อให้บริการบนระบบปฏิบัติการ iOS, iPadOS ฯลฯ ของ Apple นั้น ทราบกันดีอยู่แล้วว่าต้องอัพโหลดขึ้นไปยัง App Store เพื่อให้ผู้ใช้เข้าไปค้นหาและดาวส์โหลดไปใช้งาน ทั้งนี้ช่องทางการอัพโหลดแบบปกติ (Native App ที่พัฒนาด้วย Xcode อยู่แล้ว หรือ เฟรมเวิร์คที่ Export ออกมาเป็น Xcode โปรเจค) คือ การใช้ขั้นตอน Build Archive เมื่อสำเร็จก็จะสามารถเลือก Distribute App ไฟล์ Build ก็จะอัพโหลดขึ้นไปรอที่ appstoreconnect.apple.com ให้อัตโนมัติ เพื่อเข้ากระบวนการ Review App ต่อไป

    ทั้งนี้ในปัจจุบันเฟรมเวิร์คที่ใช้พัฒนาครั้งเดียวสามารถให้บริการได้หลายระบบปฏิบัติการ (Cross Platform) เป็นที่นิยมอย่างมาก เช่น Flutter, React Native, Xamarin ซึ่งมักจะมีเครื่องมือที่ช่วย Build เป็นไฟล์ .ipa ที่ใช้สำหรับอัพโหลดมาเลย ซึ่งในปัจจุบันไม่สามารถใช้ Xcode อัพโหลดไฟล์นี้ได้โดยตรง (ยังใช้ Terminal รันคำสั่งเพื่ออัพโหลดได้ และจำเป็นต้องติดตั้ง Xcode) จึงมีแอปพลิเคชันที่ทำหน้าที่ตรงนี้ และใช้งานง่ายมาแนะนำครับ นั้นคือ Transpoter

    ก่อนอัพโหลดไฟล์ด้วยแอปพลิเคชันนี้ ต้อง

    1. มี Apple ID ที่ลงทะเบียนเป็นนักพัฒนาเรียบร้อยแล้ว
    2. เข้าไปที่ developer.apple.com เลือก Account
    3. ทำการเพิ่ม Certificates, Identifiers & Profiles ให้เรียบร้อย
    4. ดาวส์โหลด และติดตั้ง Certificate และ Profile บนเครื่องที่จะใช้อัพโหลด (กรณีใช้ Expo จะต้องอัพโหลดตอนสั่ง Build)
    5. สร้าง App Record บน appstoreconnect.apple.com รอไว้ (แค่มีชื่อแอปไว้ก็เพียงพอแล้ว ยังไม่จำเป็นต้องมีรายละเอียดครบถ้วน)

    ขั้นตอนด้านบนจริงๆแล้วอัพโหลดด้วยวิธีไหนก็ต้องทำนะครับ และจะต้องมีการ Config ค่า Bundle ID, Team ID ให้ถูกต้องโดยขึ้นอยู่กับเครื่องมือที่ใช้พัฒนาว่าจะต้อง Config ค่าที่จุดไหน ไว้โอกาศหน้าผมจะมาลงรายละเอียดในส่วนนี้ครับ เมื่อทุกอย่างเรียบร้อยแล้ว ให้ทำการดาวส์โหลดแอปพลิเคชัน Transporter และติดตั้ง เมื่อเปิดแอปพลิเคชันขึ้นมาจะมีหน้าจอให้ Login ดังรูป

    จากนั้น ลากไฟล์ .ipa ของเรามายังหน้าจอของแอป กระบวนการ Validate ไฟล์ และอัพโหลดไปยัง App Store จะทำอัตโนมัติทั้งหมด

    หากเกิดข้อผิดพลาด จะมีหน้าต่างแจ้งเตือน หรือหากต้องการดู Log ก็มีให้เรียกดูได้ ดังภาพครับ

    เป็นอันเรียบร้อยครับ สามารถไปตรวจสอบว่า Build ของเราปรากฏอยู่บน appstoreconnect แล้วหรือยังโดยการกดปุ่ม … เลือก Open Appstoreconnect ได้เลย

    จริงๆแล้วบทความนี้จะเห็นได้ว่า เพียงแค่แนะนำให้รู้จักกับแอปพลิเคชันตัวนี้เท่านั้นครับ วิธีการใช้งานนั้นแค่ลากวางเป็นอันเสร็จ (ถ้าขั้นตอนอื่นๆถูกต้อง) โดยเฉพาะท่านใดที่ใช้ React Native แล้วใช้ Expo Server ในการ build เมื่อได้ไฟล์ .ipa มา ท่านไม่จำเป็นต้องโหลด Xcode ที่ขนาดมโหฬาร (ตัวติดตั้งอย่างเดียว 18GB ต้องมีพื้นที่ว่างในเครื่อง 40GB ถึงจะยอมให้ติดตั้งผ่าน Appstore) มาเพื่ออัพโหลดไฟล์เพียงอย่างเดียว หวังว่าบทความนี้จะเป็นประโยชน์ครับ

  • Query ที่ใช้งานบ่อยๆสำหรับทำ ETL , Data Warehouse และ Data Science ตอนที่ 1

    สายงานดึงข้อมูลเพื่อใช้สำหรับวิเคราะห์ข้อมูล แปลงข้อมูล จัดรูปแบบข้อมูลต่างๆไม่ว่าวัตถุประสงค์ที่จะทำ ETL, Data warehouse , Data Science, Data Lake สิ่งที่จะเกิดขึ้นบ่อยๆคือ

    • การจัดกลุ่มข้อมูล ROW_NUMBER(), RANK(), DENSE_RANK()
    • การแปลงข้อมูล CAST,CASE
    • การสร้าง View, Sub Table, temp table, Material View ,select ซ้อน select หลายชั้น (with)

    การทำงานด้านนี้จะแตกต่างจากการดึงข้อมูลในการทำงานแบบ CRUD (Create, Read, Update, Delete) เป็นงาน Transaction เน้นการทำงานที่เร็วอย่างมีประสิทธิภาพ ส่วนการวิเคราะห์ข้อมูลลืมเรื่อง Performance ไปได้เลยส่วนใหญ่คำสั่งที่ทาง Transaction Performance ต้องการให้เราหลีกเลี่ยงเราก็จะได้นำมาใช้งานอยากสนุกสนาน
    เนื่องจากตอนนี้ผมดึงข้อมูลจาก Oracle Database เป็นหลักก็เลยขอเขียนตัวอย่างของ Oracle ก่อนนะครับ ต่อไปค่อยเพิ่มเติม Database อื่นๆต่อไป

    การจัดกลุ่มข้อมูล

    พื้นฐานสุดๆที่รู้ๆกันคือการทำด้วยคำสั่ง Group By เช่น
    ถ้าเราต้องการค่าเดียวจากตารางเลย select sum(a) from table_a แบบนี้ก็จะเป็นการ Group ข้อมูลทั้งตาราง
    ถ้าต้องการมีตัวแยกข้อมูลก็จะเป็น select column_a,count(*) from table_a group by column_a แบบนี้ก็จะมีตัวช่วยแบ่งกลุ่มข้อมูลออกมาแล้ว
    แต่การทำงานจริงไม่ได้ง่ายดายขนาดนั้น เช่น โจทย์ต้องการเอาค่าที่มากสุด น้อยสุดหรือล่าสุดของข้อมูลในตารางโดยต้องแบ่งข้อมูลออกเป็นส่วนๆตามที่ต้องการก่อน อันนี้ก็จะพอไหว แต่ถ้าบอกว่าต้องการเอาข้อมูลลำดับที่ 1 และลำดับที่ 2 ของข้อมูลมาเปรียบเทียบกันซึ่งไม่สนใจลำดับอื่นๆแบบนี้การแบ่งกลุ่มก็ต้องมาการจัดลำดับ และสามารถดึงลำดับที่ต้องการออกมาได้ อันนี้ยากแล้วเราก็ต้องมาดูว่าฐานข้อมูลแต่ละแบบมีตัวช่วยอะไรให้เราใช้บ้างในส่วนของ oracle จะมีฟังก์ชันกลุ่มนึงที่เป็นการเรียงลำดับข้อมูลนั่นก็คือ ROW_NUMBER(),  RANK(), DENSE_RANK() 

    ROW_NUMBER() ใช้สำหรับแป๊ะเลขที่ ของชุดข้อมูลที่สนใจ ถ้าไม่ Partition ข้อมูลก็จะเป็นการ แป๊ะเลขที่ของข้อมูลทั้งหมด ซึ่งใน Oracle จะมี อีกตัวไว้ใช้งานอยู่แล้วคือ ROWNUM ซึ่งเป็น pseudocolumn ไม่ต้องใช้ ROW_NUMBER() แต่ถ้าต้องการแบ่งข้อมูลออกเป็นส่วนๆด้วย ก็ต้องใช้ ROW_NUMBER บวกกับ PARTITION BY ลองดูตัวอย่างการทำงานจริงๆครับกว่าจะได้คำตอบมาต้องทำกี่ขั้นดังนี้

    รูป Row_Number()

    จากรูปอธิบายได้ดังนี้

    ชั้นในสุดต้องการหาผลนับจำนวนข้อมูลที่สนใจ
    ชั้นที่ 2 เอาข้อมูลที่ได้มาแบ่ง Partition By ด้วย data_year และเอามาเรียงลำดับ Order By ด้วย Totals แบบเรียงจากมากไปน้อย DESC โดยจะใส่เลขกำกับไว้กับข้อมูลที่ data_year เดียวกันที่มีค่า totals มากที่สุดจะเป็นเลข 1 ไปเรื่อยๆ และเริ่ม 1 อีกครั้งเมื่อ data_year เปลี่ยนไป
    ชั้นนอกสุด คือ select * from (…) where row_no < 4 คือเอามาเฉพาะลำดับที่ 1-3 เท่านั้น
    สรุปคือต้องการเอาข้อมูลรายชื่อคณะที่มีค่า totals มากที่สุด 3 ลำดับแรกของแต่ละ data_year ออกมาแสดง

    RANK()

    ลักษณะการทำงานก็จะเหมือนตัวอย่าง ROW_NUMBER() จะเขียนอธิบายเฉพาะที่แตกต่างกันเท่านั้นดังนี้
    เป็นการแป๊ะตัวเลขลำดับให้กับชุดข้อมูลเดียวกัน แต่ค่าข้อมูลที่สนใจได้ลำดับเดียวกัน จะข้ามตัวเลขลำดับถันไปเท่ากับจำนวนลำดับที่เท่ากัน ดังรูปผลของ Query ด้านล่าง จากลำดับที่ 2 ไป 3 rows แล้วจะไปขึ้นลำดับที่ 5 เลย

    DENSE_RANK()

    จะเหมือนกัน RANK() แต่จะไม่มีการเว้นเลขลำดับ จะมีเลขที่ต่อเนื่องไปเลย ดังรูปผลของ Query ด้านล่าง จากลำดับที่ 2 ไป 3 rows แล้วจะต่อลำดับที่ 3 ต่อไป

    รูปตัวอย่าง Query
    รูปผลของ Query

    การแปลงข้อมูล

    CAST

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

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

    CASE

    ใช้เพื่อจัดการข้อมูลในหลายๆรูปแบบเป็นการกำหนดเงื่อนไขขึ้นมาเพื่อแปลงข้อมูล ดังตัวอย่าง ต้องการ Update Column ให้มีค่าแตกต่างกันให้เป็นไปตามเงื่อนไขที่กำหนด

    update student_regist
    set WITHDRAWAL_TYPE =  case
                                when lower(WITHDRAWAL_TYPE) = 'w' then  'ถอนติด W' 
                                when lower(WITHDRAWAL_TYPE) = 'c' then  'ถอนเพราะวิชาปิด'
                                when lower(WITHDRAWAL_TYPE) = 'r' then  'ถอน'
                                when lower(WITHDRAWAL_TYPE) = 'n' then  'ถอน'
                                when lower(WITHDRAWAL_TYPE) is null then  'ลงทะเบียนปกติ'
                                else 'xxx'
                                end;

    การสร้าง VIEW, Temporary Table, Materialized view

    การแยกข้อมูลออกเป็นกลุ่มๆอีกวิธีที่ใช้งานเยอะคือการสร้าง View, Temporary Table, Materialized view

    View

    มีการเปลี่ยนแปลงตาม Table ต้นทาง View ไม่น่าจะต้องพูดเยอะเพราะน่าจะใช้งานกันเป็นประจำอยู่แล้ว

    Materialized view

    เป็นตารางที่มีข้อมูลที่ได้มาจากผลของการ Run Query ที่เราต้องการและมีการ Refresh ข้อมูลตามเวลาที่กำหนดไว้ ที่แตกต่างจาก View คือต้องการที่จัดเก็บข้อมูลส่วนตัวนะครับ ตัวอย่างการสร้าง Materialized View

    DROP MATERIALIZED VIEW REGIST_ALL;
    
    CREATE MATERIALIZED VIEW REGIST59
    BUILD IMMEDIATE
    REFRESH COMPLETE
    START WITH sysdate
    NEXT sysdate+1
    WITH PRIMARY KEY
    AS
    SELECT *
    FROM REGIST where year >'2558';
    
    COMMENT ON MATERIALIZED VIEW REGIST59 IS 'snapshot table for snapshot REGIST';

    Temporary Table

    Temporary Table ใน Oracle ใช้แก้ไขปัญหาที่ซับซ้อนโดยการใช้  Stored Procedure หาข้อมูลแล้วนำไปเก็บไว้ใน Temporary table เพื่อประมวลผลในขั้นถัดๆไป

    CREATE GLOBAL TEMPORARY TABLE temp_table (
    
      student_id NUMBER(1),
    
      student_name            VARCHAR2(150),
    
      country_name    VARCHAR2(150)
    
    )
    ON COMMIT DELETE ROWS;

    ตัวอย่างการสร้าง temporary Table ไว้ใช้งาน ON COMMIT DELETE ROWS; เป็นการบอกว่า เมื่อใช้งานเสร็จให้ลบข้อมูล อย่างเช่นสั่ง insert ข้อมูลใน stored procedure เมื่อ stored procedure ทำงานเสร็จข้อมูลที่ insert ไว้ใน temporary table จะถูกลบทิ้งทั้งหมด เป็นต้น ยังมี option อีกมากมายสำหรับ temporary table ต้องศึกษาจาก Oracle Document

    Subquery

    subquery เป็นตัวช่วยทำงานในการแยกข้อมูลที่ต้องการออกมาเป็นส่วนๆแล้วนำไปประมวลผลต่อ ในกลุ่มนี้ with … as ถือว่ามีการใช้งานบ่อยครั้ง คำสั่ง With … as ข้อมูลที่ได้มาเปรียบได้กับ local temporary 1 table นำไป query ต่อได้

    WITH dept_count AS (
      SELECT deptno, COUNT(*) AS dept_count
      FROM   emp
      GROUP BY deptno)
    SELECT e.ename AS employee_name,
           dc.dept_count AS emp_dept_count
    FROM   emp e
           JOIN dept_count dc ON e.deptno = dc.deptno;

    จากตัวอย่างนี้เป็นการนับจำนวนหน่วยงานตาม deptno แล้วเอามา Join กับ table emp ที่มีค่า deptno ตรงกัน

    ตอนที่ 1 นี้ขอจบแต่เพียงเท่านี้ ตอนต่อๆไปจะเรียบเรียงข้อมูลตามที่ใช้ทำงานจริงๆในการทำงานด้าน ETL, Data Science และ Data Lake ต่อไปนะครับ ขอบคุณที่เข้ามาอ่านกันนะครับ

  • การตั้งค่า iPhone ไม่ให้ App ติดตามเก็บข้อมูลส่วนตัว

    สวัสดีค่ะ หลายท่านคงเคยเจอปัญหามีเบอร์แปลกๆ  โทรเข้ามานำเสนอสินค้า ขายประกัน หรือมี SMS โฆษณาเข้ามาทั้งที่เราไม่ได้เคยใช้บริการเหล่านี้ บางท่านอาจจะใช้ App ในการแสดงข้อมูลเพื่อที่จะปฏิเสธการรับสาย แต่เราก็สามารถจัดการตั้งแต่ต้นทางได้นะคะ ไม่ให้พวก App ที่อยู่ในมือถือของเราสามารถนำข้อมูลส่วนตัวของเราไปใช้ประโยชน์ได้  แต่ก่อนจะใช้งาน Feature นี้ ให้ตรวจสอบก่อนนะคะ ว่า ได้ Update iOS เป็นเวอร์ชัน 14.5 แล้วหรือยัง ถ้า  Update แล้วอย่ารอช้าค่ะ เราไปดูวิธีกันเลย

    1. เข้าไปในส่วนของ Settings

    2. เลือก Privacy

    3. Tracking


    4. จะปรากฏ App ที่ติดตามเราอยู่ ซึ่ง App พวกนี้แหละค่ะ ที่สามารถเก็บข้อมูลเราได้ หากเราไม่ต้องการให้ app ไหนติดตามเราก็เลือกที่จะปิด App นั้น แต่หากไม่ต้องการให้ติดตามทั้งหมดก็กดปิดในครั้งเดียวได้เลยที่ Allow Apps to Request to Track

    5. จากนั้นให้เลือก Ask Apps to Stop Tracking เพื่อบอกให้ Apps หยุดติดตาม

    6. Apps ทั้งหมดก็จะถูกปิดทั้งหมดเพื่อไม่ให้ติดตามได้อีก

    เท่านี้ก็เรียบร้อยค่ะ ลองไปทำตามกันดูนะคะ อย่าลืมว่าต้อง Update iOS เป็นเวอร์ชัน 14.5 ก่อนนะจ๊ะ

  • วิธีการรวมไฟล์ pdf หลายไฟล์และรูปภาพมาแสดงในครั้งเดียวด้วย iTextSharp (#C)

              ที่มาของบทความนี้ เนื่องด้วยงานที่ผู้เขียนได้รับมอบหมายให้พัฒนาอยู่นั้น มีส่วนหนึ่งที่เป็นการแนบไฟล์เอกสาร/หลักฐานทั้งในรูปแบบไฟล์ PDF และรูปภาพ เข้ามาจากผู้ใช้งาน ซึ่งเอกสารเหล่านี้จะต้องมีส่วนของการแสดงผลให้ทางฝั่งเจ้าหน้าที่ทำการตรวจสอบเอกสาร/หลักฐานดังกล่าวด้วย โดยเดิมทีจะมีการแสดงผลแยกเป็นรายการให้เจ้าหน้าที่เพื่อคลิกดูรายละเอียดทีละรายการ ดังภาพ

              ซึ่งในการทำงานจริงแล้วนั้นพบว่า การตรวจสอบเอกสาร/หลักฐานต่างๆที่ผู้ใช้แนบมาสามารถทำได้ยากและต้องใช้เวลา เนื่องจากเจ้าหน้าที่จะต้องทำการคลิกดูรายละเอียด/ดาวน์โหลดไฟล์คราวละ 1 ไฟล์เพื่อตรวจสอบ ซึ่งไฟล์จะแยกกันอยู่ หากต้องการดาวน์โหลดก็ต้องดาวน์โหลดทีละไฟล์ ดังภาพ

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

    1.ดึงข้อมูลไฟล์แนบเอกสารทั้งหมดของผู้ใช้

      DataTable dtFile = new DataTable();
    
        //เป็นส่วนของการสมมุติการดึงข้อมูลการแนบไฟล์ของผู้ใช้ออกมาในรูปแบบ Datatable
            dtFile.Columns.AddRange(new DataColumn[2] { 
                             new DataColumn("CITIZEN_ID", typeof(string)),
                            new DataColumn("FILE_PATH",typeof(string))});
            dtFile.Rows.Add("xxxxxxxxxxxx", "resumeFiles/Document1.pdf");
            dtFile.Rows.Add("xxxxxxxxxxxx", "resumeFiles/Document2.pdf");
            dtFile.Rows.Add("xxxxxxxxxxxx", "resumeFiles/Document3.pdf");
            dtFile.Rows.Add("xxxxxxxxxxxx", "resumeFiles/Image.png");
             
    
    //สร้างตัวแปรในการเก็บค่าชื่อไฟล์ เพื่อใช้ในการส่งเป็นพารามิเตอร์ในการรวมไฟล์
            List<string> listFiles = new List<string>();
            string path = Server.MapPath("../registPDF");
    
    //ตั้งชื่อไฟล์ที่จะรวมไฟล์ทั้งหมดไว้ และประกาศตัวแปรต่างๆที่จำเป็นต้องใช้ในการสร้างและรวมไฟล์
            string genName = Guid.NewGuid().ToString() + ".pdf";
    
    //ตัวแปร tmpPath เป็นตัวแปรที่เป็นชื่อไฟล์ที่สร้างขึ้นเพื่อรวมไฟล์ทั้งหมด
    
            string tmpPath = "../registPDF/" + genName;
            string[] files = Directory.GetFiles(path);
            string[] fileName;
            string DestName = "";
    
    //กำหนดนามสกุลที่ต้องการตรวจสอบว่าเป็นไฟล์รูปภาพหรือไม่
            string[] ImgExt = { "png","jpg","jpeg","gif"};
    
           /// เป็นการลบ Temporary file ที่ถูกสร้างขุึ้นเพื่อรวมไฟล์ เพื่อไม่ให้มีไฟล์ที่ไม่ใช้งานค้างอยู่ในเซิร์ฟเวอร์มากเกินไป *****
            foreach (string file in files)
            {
                FileInfo fi = new FileInfo(file);
                if (fi.LastAccessTime < DateTime.Now.AddMinutes(-30))
                    fi.Delete();
            }
          
    
          //วนค่าเพื่อเก็บตัวแปรชื่อไฟล์แนบก่อนรวมไฟล์
    
            for (int i = 0; i <= dtFile.Rows.Count - 1; i++)
            {
    
         //ตรวจสอบว่ามีไฟล์ดังกล่าวอยู่จริงหรือไม่บนเซิร์ฟเวอร์ตามที่อยู่ไฟล์ที่อ้างถึง
                if (File.Exists(Server.MapPath("../" + dtFile.Rows[i]["FILE_PATH"].ToString())))
                {
    
         //ตรวจสอบชนิดของไฟล์ว่ามีนามสกุลอะไร หากไม่ใช่ไฟล์ PDF จะต้องทำการแปลงและสร้างเป็นไฟล์ PDF ก่อนส่งไปรวมไฟล์
                    fileName = dtFile.Rows[i]["FILE_PATH"].ToString().Split('.');
                    if (fileName[1] == "pdf")
                        listFiles.Add(Server.MapPath("../" + dtFile.Rows[i]["FILE_PATH"].ToString()));
                    else 
                    {
    
         //กรณีที่พบว่าไม่ใช่ไฟล์ PDF จะตรวจสอบว่าเป็นไฟล์รูปภาพหรือไม่ หากใช่จะทำการแปลงและสร้างเป็นไฟล์ PDF ก่อนส่งไปรวมไฟล์
                        if (ImgExt.Contains(fileName[1].ToLower()))
                        {
                            DestName = "../registPDF/"  + Guid.NewGuid().ToString() + ".pdf";
                            ConvertImageToPdf(Server.MapPath("../" + dtFile.Rows[i]["FILE_PATH"].ToString()), Server.MapPath(DestName));
                            listFiles.Add(Server.MapPath( DestName));
                        }
                    }
                } 
     
            }
    
    //เรียกใช้งานเมธอดในการรวมไฟล์ โดยส่งค่าลิสต์ของชื่อไฟล์ PDF ทั้งหมดที่ต้องการรวม และชื่อไฟล์ปลายทางที่จะใช้ในการรวม
    
            CombineMultiplePDFs(listFiles.ToArray(), Server.MapPath(tmpPath));
    
    
    //แสดงผล PDF ไฟล์ที่ได้ทำการรวมเรียบร้อยแล้วด้วย Literal โดยตัวแปร tmpPath คือที่อยู่ของไฟล์ใหม่ที่ทำการรวมไฟล์ PDF ทั้งหมดเรียบร้อยแล้ว
    
    
            StringBuilder strObj = new StringBuilder();
    
            strObj.Append("<object id=\"pdfContainer\" type=\"application/pdf\"");
            strObj.AppendFormat(" data=\"{0}#toolbar=1&amp;navpanes=0&amp;scrollbar=1\" style=\" z-index:1000; width: 99%;  height: 600px;\">", tmpPath);
            strObj.AppendFormat(" <param name=\"src\" value=\"{0}#toolbar=1&amp;navpanes=0&amp;scrollbar=1\"> ", tmpPath);
            strObj.Append(" </object>");
    
            ltrPDF.Text = strObj.ToString();
            dtFile = null;
            

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

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

    2. เขียนเมธอดในการรวมไฟล์ดังกล่าวและสร้างเป็นไฟล์ PDF ตัวใหม่

        public static void CombineMultiplePDFs(string[] fileNames, string outFile)
        {
            // ขั้นที่ 1: สร้าง document ที่จะรวมไฟล์ทั้งหมดขึ้นมา
            Document document = new Document();
            // สร้าง FileStream object ที่จะใช้งานและต้องอย่าลืม dispose เมื่อใช้งานเรียบร้อยแล้ว
            using (FileStream newFileStream = new FileStream(outFile, FileMode.Create))
            {
                // ขั้นที่ 2: สร้างตัว
                PdfCopy writer = new PdfCopy(document, newFileStream);
                if (writer == null)
                {
                    return;
                }
    
                //ขั้นที่ 3:เปิดการใช้งานตัว document
                document.Open();
    
              // วนเพื่ออ่านค่าชื่อไฟล์ และทำการเพิ่มข้อมูลลงในเอกสารตัวใหม่ที่จะรวมไฟล์ทั้งหมด
    
               foreach (string fileName in fileNames)
                {
                    // สร้างตัว reader จากเอกสารแนบที่กำลังวน
                    PdfReader reader = new PdfReader(fileName);
                    reader.ConsolidateNamedDestinations();
    
                    // ขั้นที่ 4: ทำการเพิ่มหน้าข้อมูลจาก reader ให้กับตัว writer ทีละหน้า
                    for (int i = 1; i <= reader.NumberOfPages; i++)
                    {
                        PdfImportedPage page = writer.GetImportedPage(reader, i);
                        writer.AddPage(page);
                    }
    
                    PRAcroForm form = reader.AcroForm;
                    if (form != null)
                    {
                        writer.CopyAcroForm(reader);
                    }
    
                    reader.Close();
                }
    
                // ขั้นที่ 5: ปิดการทำงาน document และ writer
                writer.Close();
                document.Close();
            } 
        }
    

    3.เขียนเมธอดในแปลงไฟล์รูปภาพให้เป็นไฟล์ PDF

     public static void ConvertImageToPdf(string srcFilename, string dstFilename)
        {
            iTextSharp.text.Rectangle pageSize = null;
    
            using (var srcImage = new Bitmap(srcFilename))
            {
                pageSize = new iTextSharp.text.Rectangle(0, 0, srcImage.Width, srcImage.Height);
            }
            using (var ms = new MemoryStream())
            {
                var document = new iTextSharp.text.Document(pageSize, 0, 0, 0, 0);
                iTextSharp.text.pdf.PdfWriter.GetInstance(document, ms).SetFullCompression();
                document.Open();
                var image = iTextSharp.text.Image.GetInstance(srcFilename);
                document.Add(image);
                document.Close();
    
                File.WriteAllBytes(dstFilename, ms.ToArray());
            }
        }

    4.แสดงผลไฟล์ PDF ที่รวมเรียบร้อยแล้ว ด้วย Literal

            StringBuilder strObj = new StringBuilder();
    
            strObj.Append("<object id=\"pdfContainer\" type=\"application/pdf\"");
            strObj.AppendFormat(" data=\"{0}#toolbar=1&amp;navpanes=0&amp;scrollbar=1\" style=\" z-index:1000; width: 99%;  height: 600px;\">", tmpPath);
            strObj.AppendFormat(" <param name=\"src\" value=\"{0}#toolbar=1&amp;navpanes=0&amp;scrollbar=1\"> ", tmpPath);
            strObj.Append(" </object>");
    
            ltrPDF.Text = strObj.ToString();
    

    หมายเหตุ ในการใช้งานโค้ดข้างต้น มีไลบรารีที่จำเป็นต้องใช้งาน ดังนี้ค่ะ

    using System.IO;
    using iTextSharp.text;
    using iTextSharp.text.pdf;
    using System.Data;
    using System.Text;
    using System.Drawing;

    ผลลัพธ์ ตัวอย่างไฟล์ที่ได้จากการรวมไฟล์เอกสาร PDF 3 ไฟล์ และไฟล์รูปภาพ 1 ไฟล์

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

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

    https://stackoverflow.com/questions/6029142/merging-multiple-pdfs-using-itextsharp-in-c-net

    https://alandjackson.wordpress.com/2013/09/27/convert-an-image-to-a-pdf-in-c-using-itextsharp/

  • Data Visualization นำเสนอข้อมูลเป็นรูป/กราฟแบบไหนดี ? กับข้อมูลที่มีอยู่

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

    ต้องการเปรียบเทียบข้อมูล (Comparison)

    กลุ่มนี้ก็จะมี

    • Bar Chart
    • Line Chart
    • Bubble Chart
    • Grouped Bar
    • Table
    • Pivot Table

    Bar Chart และ Grouped Bar ใช้เปรียบเทียบข้อมูลตามเงื่อนไขที่สนใจ

    ใช้เปรียบเทียบมิติจำนวนข้อมูลที่สนใจกับช่วงที่สนใจ เช่น เปรียบเทียบเป้าหมายที่ตั้งไว้กับข้อมูลที่ทำได้จริงในแต่ละเดือน, จำนวนนักศึกษา ในแต่ละปีการศึกษา เป็นต้น

    Line Chart ใช้เปรียบเทียบเพื่อดูแนวโน้มของข้อมูล

    ใช้เปรียบเทียบมิติของข้อมูล ในเชิงต้องการดูเพื่อเปรียบเทียบการเปลี่ยนแปลง ดูแนวโน้ม (Trends) โดยอาจจะเทียบกับมิติของเวลา (Time Series) และยังนำไปใช้ร่วมกับ machine learning เพื่อพยากรณ์ข้อมูลในอนาคตได้ด้วย เช่น ข้อมูลการถอนรายวิชาในแต่ละเดือนเปรีบเทียบ 3 ปีที่ผ่านมา จำนวนนักเรียนที่สมัครเข้าเรียนใน มอ. แยกตามโครงการ 5 ปีที่ผ่านมา เป็นต้น ตัวอย่างเป็นเปอร์เซ็นต์นักศึกษาเพศชายกับเพศหญิง

    รูป Line Chart

    Bubble Chart ใช้แสดงข้อมูลที่มีความสัมพันธ์กันแบบ 3 มิติ

    ใช้เปรียบเทียบแบบ 3 มิติข้อมูล เช่น
    แกน X แสดง จำนวนอาจารย์
    แกน Y แสดง จำนวนเงินค่าลงทะเบียน
    ขนาดและจำนวนแต่ละฟอง แทน คณะและจำนวนนักศึกษา
    ถ้าเปรียบเทียบแบบนี้ก็จะเห็นความสัมพันธ์ของข้อมูลทั้ง 3 ข้อมูลและสามารถตั้งเป้าหมาย หาค่ามากที่สุด น้อยที่สุดที่สนใจได้

    รูป Bubble Chart ตัวอย่างเป็นข้อมูลสมมุติ

    Table ใช้เปรียบเทียบข้อมูลแบบแนวตั้ง

    เป็นการเปรียบเทียบพื้นฐานที่สุดเลย เป็นการเปรียบเทียบชุดข้อมูลที่ไม่ซับซ้อน เช่น

    รูป Table

    Pivot Table ใช้เปรียบเทียบข้อมูลแนวนอน

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

    รูป Pivot Table

    ต้องการดูการกระจาย (Distribution) สามารถใช้เมื่อต้องการดูความถี่ของข้อมูลว่ามีลักษณะการกระจายตัวอย่างไร

    Histogram
    Line Histrogram
    Scatter Plot
    Box Plot

    Histogram แสดงความสัมพันธ์ระหว่างข้อมูล ดูการการจายความถี่ของข้อมูล

    รูป Histogram

    Scatter Plot แสดงการกระจายของการจับคู่ข้อมูล

    ภาพจาก WHO

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

    จะเห็นอะไรจาก Scatter Plot
    -แนวโน้มของข้อมูลระหว่างตัวแปร
    -ความผิดปกติจากภาพรวม
    -กลุ่มก้อนภาพรวมของข้อมูล

    Box Plot เพื่อดูการกระจายของข้อมูลและมีค่าต่างๆประกอบอยู่ในกราฟคือ ค่ากลาง ค่าการการะจาย ค่ามากสุด น้อยที่สุดและข้อมูลห่างกลุ่มมาก (Outlier)

    รูป Box Chart

    Box Plot Chart จะมีข้อมูลแบ่งออกเป็น 3 ช่วงคือ
    25% (Q1) คือข้อมูล 25% แรกจากค่าต่ำขึ้นมา
    50% (Q2) คือข้อมูลตัวที่มากกว่า 25% จนถึงตัวที่ 75% โดยแสดงออกมาในรูป สี่เหลี่ยมผืนผ้า
    75% (Q3) คือข้อมูล 50% ของข้อมูลอยู่ เขียนแทนด้วยเส้นตรงอยู่ภายในรูปสี่เหลี่ยมผืนผ้า ค่านี้คือค่าค่ากลางของข้อมูลทั้งหมด (Median) และตรงค่า เฉลี่ย (Mean) จะแทนด้วย เครื่องหมายบวก
    สำหรับตัวอย่างที่น่าจะยกได้สำหรับการศึกษาอาจจะเป็นผลการเรียนของนุักศึกษา

    ดูการแบ่งสัดส่วน (Composition) ต้องการเห็นภาพรวมพร้อมกับส่วนต่างๆที่สนใจ

    Treemap
    Donut Chart
    Stacked Area Chart
    Stacked Bar
    Pie Chart
    Waterfall Chart

    Treemap

    รูป Treemap

    เป็น Chart ตารางสี่เหลี่ยม โดยใช้สีแยกกลุ่มของข้อมูล และขนาดของสีสี่เหลี่ยมบอกถึงปริมาณของข้อมูลแต่ละกลุ่ม เป็นกราฟที่ดูง่ายเข้าใจในทันทีที่เห็น

    Pie Chart

    รูป Pie Chart

    Pie Chart เป็น Chart ที่แสดงสัดส่วนของข้อมูลดังเดิมที่เข้าใจง่าย เห็นการแยกสัดส่วนตามสีของแต่ละส่วน (เหมือนพิสซ่ามากกว่าพาย)

    ดูความสัมพันธ์ (Relationship) ของข้อมูล

    Heatmap
    Worldmap
    Column/Line Chart
    Scatter Plot
    Bubble Chart

    World map

    รูป World Map

    world map ใช้รูปแผนที่โลกในการแสดงข้อมูลที่มีความสัมพันธ์กัน เป็น Chart สมัยใหม่เข้าใจง่ายและสวยงาม

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

    แหล่งข้อมูลที่ใช้ศึกษาและเขียนบทความ
    แหล่งที่ 1
    แหล่งที่ 2
    แหล่งที่ 3

  • ใช้มือถือแทน Mouse และ Keyboard ก็ได้นะ

    ชีวิตคนทำงาน Office ที่ต้องใช้ Computer เป็นประจำทุกวัน ทำงานกันอย่างหนักหน่วง ถ้าเกิด Mouse หรือ Keyboard ที่ใช้งานอยู่ มีปัญหาหรือเสียหายขึ้นมา จะทำยังไง? ส่งซ่อมหรือออกไปหาซื้อใหม่ ก็ทำให้เสียเวลา
    วันนี้เรามีวิธีมาช่วยแก้ไขปัญหาเฉพาะหน้า เปลี่ยนมือถือคุณเป็น Mouse และ Keyboard แบบ WiFi ใช้ร่วมกับคอมพิวเตอร์ของคุณได้ทั้ง Windows และ Mac รวมถึงคุมมือถือ iOS และ Android ได้ด้วย สามารถพิมพ์ผ่านมือถือได้ เพียงลง Application WiFi Mouse ลงบนเครื่อง iOS หรือ Android และคอมพิวเตอร์


    เริ่มต้นด้วย
    1. Download Application ที่ชื่อว่า WiFi Mouse มาไว้ที่มือถือของเรา ซึ่งสามารถ Download ได้ทั้งระบบปฏิบัติการ iOS และ Android



    2. Download โปรแกรมที่ http://wifimouse.necta.us/ มาไว้ที่เครื่องคอมพิวเตอร์ของเรา โดยเลือก Download ตาม OS ที่เราใช้งาน



    3. หลังจากติดตั้งให้เรียบร้อย จะได้โปรแกรม Mouse Server บนคอมพิวเตอร์ และ Application WiFi Mouse บนมือถือ ดังรูป


    4. เริ่มต้นการใช้งาน มือถือและ เครื่องคอมพิวเตอร์ ต้องเชื่อมต่อ ให้อยู่ใน WiFi วงเดียวกัน จากนั้นให้เปิดโปรแกรมและ Application ที่ติดตั้งนั้นขึ้นมา จะปรากฏชื่อคอมพิวเตอร์ของเราบนมือถือ เพื่อให้เลือกใช้งาน ดังรูป



    5. เมื่อเลือกเข้าใช้งานที่ชื่อคอมพิวเตอร์ของเราแล้ว มือถือของเราก็จะกลายเป็น Mouse และ Keyboard ได้ทันที…..ว้าวววว


    6. สำหรับ Function ที่สามารถใช้งานได้ฟรี มีดังนี้
    – Keyboard


    – Presentation


    – File Browser


    – Browser


    – Screen Picture เมื่อคลิกจะสามารถ Capture หน้าจอที่เราต้องการไปวางไว้บนหน้า Desk top



    – Applications



    – Shutdown



    ชีวิต คนทำงานง่ายขึ้นเยอะ ไว้มา update เคล็ดลับดี ๆ ที่คุณอาจจะยังไม่รู้กันอีกนะคะ
    ขอบคุณ : ความรู้ดีๆ จาก Youtube





  • ซ่อน/แสดง คอลัมน์ใน ASP.NET GridView จาก Code Behind

    จากบทความ ซ่อน/แสดง คอลัมน์ใน ASP.NET GridView ด้วย jQuery ท่านผู้อ่านที่ได้เข้าไปอ่านแล้วอาจจะมีคำถามว่าถ้าไม่อยากใช้ jQuery ล่ะ เนื่องด้วยสาเหตุอะไรก็แล้วแต่ วันนี้ผมก็จะมานำเสนอการซ่อน/แสดงคอลัมน์ใน ASP.NET GridView อีกวิธี ซึ่งจะเป็นการควบคุมจาก code behind ที่เป็นภาษา C# หรือ VB.NET ในที่นี่ผู้เขียนจะใช้ภาษา C#

    ซึ่งจะมีขั้นตอนต่างๆ ดังต่อไปนี้

    1. ตัวอย่างโค้ด HTML จะมีการแก้ไขเพิ่มขึ้นอีกเล็กน้อยจากบทความเดิม

    <asp:CheckBox ID="chkCallNo" Text=" Show CallNo from code behind" runat="server" Checked="true"
                  AutoPostBack="true" OnCheckedChanged="chkCallNo_CheckedChanged" />
    <hr />
    
    <asp:GridView ID="gvBib" runat="server" AutoGenerateColumns="False">
        <Columns>
            <asp:BoundField DataField="BIB_NO" HeaderText="Bib#" />
            <asp:BoundField DataField="TITLE" HeaderText="Title" />
            <asp:BoundField DataField="AUTHOR" HeaderText="Author" />
            <asp:BoundField DataField="CALL_NO" HeaderText="CallNo" />
            <asp:BoundField DataField="ISBN" HeaderText="ISBN" />
        </Columns>
    </asp:GridView>

    โดยสิ่งที่เพิ่มขึ้นมาจะอยู่ที่ CheckBox คือ

    • AutoPostBack=”true” เพื่อให้มีการ PostBack ทุกครั้งที่มีการคลิก Checkbox
    • OnCheckedChanged=”chkCallNo_CheckedChanged” คือ event ที่อยู่ใน code behind ที่จะถูกเรียกใช้เมื่อมีการคลิก

    2. โค้ดในส่วนของการจำลองข้อมูล สามารถใช้โค้ดเดิมจากบทความเก่าได้เลย

    if(!IsPostBack)
    {
        DataTable dt = new DataTable();
        dt.Columns.AddRange(new DataColumn[5] { new DataColumn("BIB_NO"), 
                                                new DataColumn("TITLE"), 
                                                new DataColumn("AUTHOR"),
                                                new DataColumn("CALL_NO"),
                                                new DataColumn("ISBN")});
        dt.Rows.Add(1, "1 ทศวรรษ ดัชนีสุขภาพคนไทย", "สำนักงานคณะกรรมการสุขภาพแห่งชาติ (สช.)", "WA13 ห159 2556", "");
        dt.Rows.Add(2, "ความลับในร่างกายมนุษย์ที่เราไม่เคยรู้", "ฟรานซิส, เกวิน", "QS4 ฟ133a 2560", "9786168022887");
        dt.Rows.Add(3, "The best ICU", "ดุสิต สถาวร", "WX218 B561 2560", "9786168122020");
        dt.Rows.Add(4, "COVID-19 โรคระบาดแห่งศตวรรษ", "นำชัย ชีววิวรรธน์", "QW168.5.C8 น515c 2563", "9789740217060");
        dt.Rows.Add(5, "Good health & smart life ในวัย 40+", "ไวต์, จอห์น", "WT104 ว967g 2560", "9786160827237");
        gvBib.DataSource = dt;
        gvBib.DataBind();
    }

    3. เพิ่มโค้ดใน chkCallNo_CheckedChanged เพื่อควบคุมการซ่อน/แสดงคอลัมน์ที่ต้องการ

    protected void chkCallNo_CheckedChanged(object sender, EventArgs e)
    {
        gvBib.Columns[3].Visible = chkCallNo.Checked;
    }

    โดย gvBib.Columns[3].Visible จะเป็นการกำหนดให้คอลัมน์ที่ 3 ของ Gridview แสดงผลหรือไม่ ซึ่งก็คือคอลัมน์ CallNo นั่นเอง (จะเริ่มนับตั้งแต่ 0)

    4. ทดสอบการใช้งาน

    5. จะเห็นว่าระบบสามารถทำงานได้ตามความต้องการ คอลัมน์จะซ่อน/แสดงได้ตามเงื่อนไขที่เราเลือกจาก checkbox แต่จะสังเกตเห็นว่า ทุกครั้งที่มีการเลือก checkbox ระบบจะ refresh หน้าจอใหม่ทุกครั้ง ซึ่งถ้าในหน้าจอที่เรากำลังทำงานมีข้อมูลอื่นๆ อีกหลายอย่าง ก็จะถูกโหลดซ้ำโดยไม่จำเป็น เพื่อแก้ไขปัญหานี้ ASP.NET จะมีเครื่องมือที่เรียกว่า UpdatePanel ซึ่งจะทำงานในแบบ Partial Load ได้ตามตามเงื่อนไขที่เรากำหนด โดยเราจะต้องปรับแก้โค้ด HTML เพิ่มเติมดังนี้

    <asp:ScriptManager runat="server"></asp:ScriptManager>
    
    <asp:CheckBox ID="chkCallNo" Text=" Show CallNo from code behind" runat="server" AutoPostBack="true" Checked="true" OnCheckedChanged="chkCallNo_CheckedChanged" />
    <hr />
    
    <asp:UpdatePanel ID="UpdatePanel1" runat="server">
        <ContentTemplate>
            <asp:GridView ID="gvBib" runat="server" AutoGenerateColumns="False">
                <Columns>
                    <asp:BoundField DataField="BIB_NO" HeaderText="Bib#" />
                    <asp:BoundField DataField="TITLE" HeaderText="Title" />
                    <asp:BoundField DataField="AUTHOR" HeaderText="Author" />
                    <asp:BoundField DataField="CALL_NO" HeaderText="CallNo" />
                    <asp:BoundField DataField="ISBN" HeaderText="ISBN" />
                </Columns>
            </asp:GridView>
        </ContentTemplate>
        <Triggers>
            <asp:AsyncPostBackTrigger ControlID="chkCallNo" EventName="CheckedChanged" />
        </Triggers>
    </asp:UpdatePanel>

    สิ่งที่เพิ่มเติมเข้ามาคือ

    • ScriptManager สำหรับใช้ควบคุมการทำงาน UpdatePanel
    • UpdatePanel ใช้ครอบ component หรือพื้นที่ที่เราต้องให้สามารถทำ Partial Load ได้ ในที่นี่คือเราครอบ Gridview นั่นเอง
    • Triggers จะเป็นการกำหนดเพื่อให้เกิด Partial Load ตาม control และ event ที่ได้ระบุเอาไว้ ในที่นี้คือ จะโหลดเมื่อ chkCallNo เกิด event CheckdChanged ซึ่งก็คือเหตุการณ์คลิกนั่นเอง

    ทดสอบ run ก็จะได้ผลลัพธ์ดังรูปด้านล่าง

    จะเห็นว่าหลังจากมีการปรับแก้โค้ดไปแล้ว ทุกครั้งที่มีการคลิก CheckBox และมีการซ่อน/แสดงคอลัมน์ จะไม่มีการ refresh หน้าจออีกแล้ว เพราะ UpdatePanel ที่เราได้เพิ่มเข้าไปจะควบคุมให้มีการโหลดข้อมูลใหม่เฉพาะ component ที่อยู่ภายใน UpdatePanel เท่านั้น

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


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