Category: การพัฒนา ASP.NET Web Application

  • Refresh ข้อมูลในกรณีที่ฐานข้อมูลมีการอัพเดทใน LINQ และ Entity Framework (Refresh Query in LINQ)

    จากปัญหาที่เคยเจอในกรณีที่ฐานข้อมูลมีการอัพเดทไปแล้ว พอ Select ข้อมูลออกมาข้อมูลไม่ refresh ในกรณีนี้จะยกตัวอย่างการใช้งานฟังก์ชัน reload ของ System.Data.Entity.Infrastructure

    public class DbEntityEntry where TEntity : class

    // Summary:
    // Reloads the entity from the database overwriting any property values with values
    // from the database. The entity will be in the Unchanged state after calling this
    // method.
    public void Reload();

    โดยการใช้งานนั้นจะยกตัวอย่างตามโค้ดด้านล่าง

    ProjectEntities pe = new ProjectEntities();

    var project = pe.PROJECT.Where(w => w.ID == projectID && w.YEAR == year).FirstOrDefault();

    if (project != null)

    {
    pe.Entry(project).Reload();

    }

    หวังว่าคงจะได้ช่วยโปรแกรมเมอร์ทีมีปัญหาเรื่องการ refresh ข้อมูลผ่าน LINQ และ Entity Framework

  • การสร้างเงื่อนไขแบบหลายตัวแปรในการค้นหาข้อมูลผ่าน LINQ (Multiple Search In LINQ)

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

    • ตัวแปร “ชื่อ/นามสกุล/เลขประจำตัวประชาชน”
      โดยใช้ control TextBox ที่ชื่อ ID=”txtSearch”
    • ตัวแปร “โครงการรับ” โดยใช้ control DropDownList ที่ชื่อ ID=”ddProject”
    • ตัวแปร “สถานะการตรวจเอกสาร”
      โดยใช้ control DropDownList ที่ชื่อ ID=”ddStatus”

    จากนั้นเราสร้าง Entity ยกตัวอย่างเป็น UploadEntities ซึ่งในที่นี้ สร้าง DbSet ที่เชื่อมต่อฝั่งฐานข้อมูลยกตัวอย่างเป็น V_REGISTRATION_UPLOAD ผ่าน Entity Framework 4.5 จากนั้นใช้ LINQ ในการเขียนเงื่อนไข ยกตัวอย่างตามโค้ดด้านล่าง

    project = ddProject.SelectedValue;

    status = ddStatus.SelectedValue;

    search = txtSearch.Text.Trim();

    outList = ue.V_REGISTRATION_UPLOAD

    .Where(w => (w.STUD_FNAME.Contains(search) || w.STUD_LNAME.Contains(search) || w.CITIZEN_ID.Contains(search)) || string.IsNullOrEmpty(search))

    .Where(x => x.PROJECT_ID == project || string.IsNullOrEmpty(project))

    .Where(y => y.APPROVED_STATUS == status || string.IsNullOrEmpty(status)) .ToList();

    gvUploadedList.DataSource = outList;

    gvUploadedList.DataBind();

    แสดงการใช้งานฟังก์ชัน Where ของ LINQ ในส่วนของ code behide
    ผลลัพธ์ที่ได้จากการเรียกใช้เงื่อนไขฟังก์ชัน Where ของ LINQ

    สรุปได้ว่าการนำฟังก์ชัน Where ของ LINQ มาใช้งานนั้น ทำให้โปรแกรมเมอร์สะดวกและลดการเขียนโค้ดให้ง่ายขึ้นจากเมื่อก่อนที่ต้องตรวจสอบเงื่อนไขเป็นแบบทีละเงื่อนไข

  • Stencil : Decorators

    Component Decorator

    แต่ละ Stencil component จะต้องขึ้นต้นด้วย @Component() decorator เสมอ โดย import มาจาก @stencil.core package ซึ่งภายใต้ @Component() decorator สามารถกำหนด tag name และ styleUrl ของ component

    import { Component } from '@stencil/core';
    
    @Component({
      tag: 'todo-list',
      styleUrl: 'todo-list.scss'
    })
    export class TodoList {
      ...
    }
    

    @Component() decorator ให้ความสามารถในการกำหนด CSS classes และ attributes บน componnet ที่สร้างโดยใช้ host option ดังนี้

    import { Component } from '@stencil/core';
    
    @Component({
      tag: 'todo-list',
      styleUrl: 'todo-list.scss',
      host: {
        theme: 'todo',
        role: 'list'
      }
    })
    

    เมื่อใช้งาน component ตัวนี้ ก็จะมีการกำหนดค่า class เป็น todo และกำหนด role attribute ให้อัตโนมัติ

    <todo-list class='todo' role='list'></todo-list>

     

    Prop Decorator

    @Prop() decorator ใช้ระบุ attribute หรือ properties สำหรับ element ที่ผู้พัฒนาสามารถกำหนดค่าให้กับ component นั้นได้  เนื่องจาก Children component จะไม่สามารถเข้าถึง properties หรือ reference ของ parent component ได้จึงใช้ Props ในการผ่านข้อมูลจาก parent มาสู่ child และเมื่อใดก็ตามที่ค่าของ Props เปลี่ยนแปลงจะทำให้ component ทำการ re-render

    type ของ Props ที่รองรับมีหลากหลาย ไม่ว่าจะเป็น number, string, boolean หรือแม้กระทั่ง Object หรือ Array

    import { Prop } from '@stencil/core';
    ...
    export class TodoList {
      @Prop() color: string;
      @Prop() favoriteNumber: number;
      @Prop() isSelected: boolean;
      @Prop() myHttpService: MyHttpService;
    }
    

    ภายใน function ใน TodoList class สามารถเรียกใช้งาน Props ผ่าน this operator

    logColor() {
      console.log(this.color)
    }
    

    สำหรับการใช้ภายนอก ใน HTML การกำหนดค่าทำได้โดย set ค่าให้ attributes ของ component tag โดยใช้ dash-case  เช่น Prop favoriteNumber ใช้ attribute favorite-number

    <todo-list color="blue" favorite-number="24" is-selected="true"></todo-list>

    สำหรับการใช้ใน JSX การกำหนดค่าทำได้โดย set ค่าให้ attributes ของ component tag โดยใช้ camelCase

    <todo-list color="blue" favoriteNumber="24" isSelected="true"></todo-list>

    Prop สามารถเรียกใช้งานผ่านทาง element ของ  javascript

    const todoListElement = document.querySelector('todo-list');
    console.log(todoListElement.myHttpService); // MyHttpService
    console.log(todoListElement.color); // blue
    

     

    Component State

    @State() decorator ใช้ในการจัดการ internal data ของ component ซึ่งผู้ใช้ไม่สามารถแก้ไขข้อมูลจากภายนอก component  เมื่อข้อมูลมีการเปลี่ยนแปลง จะทำให้ component ทำการ re-render เช่นเดียวกับ Prop

    import { State } from '@stencil/core';
    
    ...
    export class TodoList {
      @State() completedTodos: Todo[];
    
      completeTodo(todo: Todo) {
        // This will cause our render function to be called again
        this.completedTodos = [...this.completedTodos, todo];
      }
    
      render() {
        //
      }
    }
    

     

    Element Decorator

    @Element() decorator ใช้ในการเข้าถึง host element ภายใน class instance โดย return type คือ HTMLElement  ทำให้สามารถใช้ standard DOM methods/events ได้

    import { Element } from '@stencil/core';
    
    ...
    export class TodoList {
    
      @Element() todoListEl: HTMLElement;
    
      addClass(){
        this.todoListEl.classList.add('active');
      }
    }
    

     

    อ้างอิง : https://stenciljs.com/docs/decorators

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

  • 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

  • การเรียกใช้งาน (Register) Bootbox.js ด้วย C#.NET

    ในการออกแบบเว็บฟอร์มบันทึกข้อมูลลงฐานข้อมูลนั้น นักพัฒนาเว็บแอ๊พปลิเคชั่นส่วนใหญ่จะออกแบบเป็นลำดับขั้นตอนการทำงานประมาณนี้

    1. ผู้ใช้เปิดเว็บฟอร์มขึ้นมาด้วยเว็บบราวเซอร์
    2. ผู้ใช้กรอกข้อมูลลงในเว็บฟอร์ม
    3. ผู้ใช้กดปุ่มบันทึกข้อมูล
    4. ระบบตรวจสอบ (Validate) ข้อมูลที่ผู้ใช้กรอกเข้ามา
      1. ถ้าผ่านก็บันทึกข้อมูลลงฐานข้อมูล
      2. ถ้าไม่ผ่านก็จะแจ้งข้อความแจ้งเตือนผู้ใช้งานให้ตรวจสอบข้อมูลใหม่อีกครั้ง
    5. ระบบจะแจ้งผลการบันทึกข้อมูลว่า
      1. “บันทึกข้อมูลสำเร็จ” กรณีที่บันทึกสำเร็จ หรือ
      2. “เกิดข้อผิดพลาดในการบันทึกข้อมูล” ในกรณีที่มีข้อผิดพลาด

    การพัฒนาเว็บด้วย ASP.NET นั้นในขั้นตอนที่ 4 จะถูกเขียนด้วยโค้ด C#.NET ซึ่งเป็น Server Side Script ส่วนขั้นตอนที่ 5 นั้นเราสามารถใช้ Dialog ของ Bootbox.js ซึ่งเป็น Client Side Script มาช่วยได้ ซึ่งในบทความนี้จะแสดงวิธีการเขียนโค้ด C#.NET ให้เรียกใช้ Bootbox Dialog ได้เลย  (อ่านบทความ การสร้าง JavaScript Dialog ด้วย Bootbox.js ได้ที่นี่) และสร้างเป็นฟังก์ชั่นสำเร็จรูปไว้เรียกใช้งานใหม่ได้ (Reuse)

     

    การรันคำสั่ง Bootbox.js ซึ่งเป็น JavaScript ผ่าน C#.NET นั้นจะต้องใช้ ScriptManager ช่วยลงทะเบียนสคริปต์ (Register Script) ดังนี้

    รูปที่ 1 การ Register Script ด้วย ScriptManager

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

    รูปที่ 2 การแสดงข้อความแจ้งผู้ใช้หลังการบันทึกข้อมูล

    ผลลัพธ์ที่ได้จากโค้ดข้างต้นจะเป็นดังรูปที่ 3

    รูปที่ 3 ผลลัพธ์ของข้อความแจ้งเตือนจากการบันทึกข้อมูล

    แน่นอนว่าในการพัฒนาเว็บแอ๊พปลิเคชัน 1 ครั้ง จะประกอบด้วยฟอร์มบันทึกข้อมูลมากมาย ทำให้ต้องเขียนโค้ดซ้ำๆ กันหลายครั้ง จากโค้ดข้างต้นเราสามารถ Optimize โค้ดได้อีกโดยให้สามารถเรียกใช้งานและแก้ไขโค้ดในภายหลังได้ง่ายขึ้น โดยการย้ายส่วนลงทะเบียนสคริปต์ไปไว้ในฟังก์ชั่นกลางสร้างเป็น Library (ในที่นี้จะตั้งชื่อว่า CoreBLL) สำเร็จรูปไว้ใช้ต่อไปดังนี้

    รูปที่ 4 CoreBLL

    หลังจากที่สร้าง CoreBLL เสร็จแล้วให้แก้ไขโค้ดบันทึกข้อมูลดังรูปที่ 5

    รูปที่ 5 โค้ดบันทึกข้อมูลลงฐานข้อมูล

    จะเห็นว่า CoreBLL ทำให้โค้ดในการบันทึกข้อมูลฟอร์มสั้นลง ง่ายขึ้น และช่วยลดความผิดพลาดในการเขียนโค้ดลง ในขณะที่หน้าจอผลลัพธ์ (รูปที่ 3) ยังคงเหมือนเดิม

     

  • การสร้าง JavaScript Dialog ด้วย Bootbox.js

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

    JavaScript Confirm บน Google Chrome

     

    JavaScript Confirm บน Mozilla Firefox

    ตัวอย่างโค้ดเรียกใช้งาน JavaScript Confirm

    จากรูปจะเห็นว่า JavaScript Confirm บน Google Chrome และ Mozilla Firefox หน้าตาไม่เหมือนกัน ทั้งๆที่ใช้โค้ดเดียวกัน และปัญหาอีกอย่างหนึ่งที่พบคือ หากเราต้องการให้ข้อความ “คุณต้องการลบข้อมูลรายการนี้ใช่หรือไม่?” มีการขึ้นบรรทัดใหม่ ใส่สี เพิ่มขนาดฟอนต์ จัดตัวหนา ตัวเอียงก็ไม่สามารถทำได้เลย แต่ถ้าต้องการให้แก้ปัญหาเหล่านี้ได้ก็ต้องใช้ตัวช่วย นั่นคือ Bootbox.js ซึ่งเป็น JavaScript library ที่ใช้งานร่วมกับ Bootstrap โดยใช้ Bootstrap modal มาทำหน้าที่แทน JavaScript Dialog ต่างๆ ทั้ง Alert, Confirm, Prompt และรวมถึง Custom Dialog ด้วย ทำให้หน้าตาของ Dialog จะเหมือนกันทุก Browser และสามารถจัดรูปแบบการแสดงผลได้ตามต้องการโดยใช้ CSS

    วิธีการติดตั้ง

    1. เข้าไปที่เว็บไซต์ http://bootboxjs.com และเลือกดาวน์โหลดไฟล์ชื่อ bootbox.min.js หรือถ้าไม่ต้องการดาวน์โหลดก็สามารถใช้ CDN (https://cdnjs.cloudflare.com/ajax/libs/bootbox.js/4.4.0/bootbox.min.js) ได้เช่นกัน
    2. ใส่โค้ดอ้างอิงไปยัง bootbox.min.js ใน header tag ดังนี้
      <script src=”path/to/script/bootbox.min.js”></script>
      หรือ
      <script src=https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js></script>

    ตัวอย่างการใช้งาน Confirm Dialog

    1. กำหนดชื่อ CSS class ให้กับปุ่มลบ ในที่ตั้งชื่อว่า “confirm”

      กำหนดชื่อ CSS Class ให้กับปุ่มลบ
    2. แทรกโค้ด JavaScript ใน HTML ดังนี้

      JavaScript สำหรับ Confirm Dialog
    3. ลองเปิดเว็บด้วย Google Chrome และ Mozilla Firefox ก็จะได้ผลลัพธ์เหมือนกันดังรูป

      Confirm Dialog ที่สร้างขึ้นด้วย Bootbox.js

    นอกจาก Confirm Dialog แล้ว ท่านสามารถดูตัวอย่าง Dialog แบบอื่นๆ ได้จากที่นี่ เพียงเท่านี้ก็จะสามารถสร้าง Dialog สวยๆ และมีความยืดหยุ่นไว้ใช้งานได้แล้ว

     

  • การเชื่อมต่อ OAuth2 ด้วย .NET C#

    อยาก  Login ด้วย OAuth2 กับ .NET C# ต้องทำอย่างไร

                 สำหรับตัวอย่างนี้เอามาจาก GitHub Project : https://github.com/titarenko/OAuth2[1]
    ซึ่งเป็น Code ตัวอย่างนำมาเพิ่ม ในส่วนของการเชื่อมต่อ PSU Passport ดังนี้

    • เพิ่ม class file ใน project OAuth2->Client->Impl ชื่อ PassportClient.cs เนื้อหาดังนี้
      using Newtonsoft.Json.Linq;
      using OAuth2.Configuration;
      using OAuth2.Infrastructure;
      using OAuth2.Models;
      
      namespace OAuth2.Client.Impl
      {
       /// <summary>
       /// Passport authentication client.
       /// </summary>
       public class PassportClient : OAuth2Client
       {
          /// <summary>
          /// Initializes a new instance of the <see cref="PassportClient"/> class.
          /// </summary>
          /// <param name="factory">The factory.</param>
          /// <param name="configuration">The configuration.</param>
          public PassportClient(IRequestFactory factory, IClientConfiguration configuration)
              : base(factory, configuration)
          {
          }
      
          /// <summary>
          /// Defines URI of service which issues access code.
          /// </summary>
          protected override Endpoint AccessCodeServiceEndpoint
          {
              get
              {
                  return new Endpoint
                  {
                       BaseUri = "https://oauth.psu.ac.th",
                       Resource = "?oauth=authorize"
                  };
              }
          }
      
          /// <summary>
          /// Defines URI of service which issues access token.
          /// </summary>
          protected override Endpoint AccessTokenServiceEndpoint
          {
              get
              {
                  return new Endpoint
                  {
                       BaseUri = "https://oauth.psu.ac.th",
                       Resource = "?oauth=token"
                  };
              }
          }
      
          /// <summary>
          /// Defines URI of service which allows to obtain information about user which is currently logged in.
          /// </summary>
          protected override Endpoint UserInfoServiceEndpoint
          {
              get
              {
                  return new Endpoint
                  {
                       BaseUri = "https://oauth.psu.ac.th",
                       Resource = "?oauth=me"
                  };
              }
          }
      
          /// <summary>
          /// Friendly name of provider (OAuth2 service).
          /// </summary>
          public override string Name
          {
              get { return "Passport"; }
          }
      
      
          /// <summary>
          /// Should return parsed <see cref="UserInfo"/> from content received from third-party service.
          /// </summary>
          /// <param name="content">The content which is received from third-party service.</param>
          protected override UserInfo ParseUserInfo(string content)
          {
              var response = JObject.Parse(content);
              return new UserInfo
              {
                   Id = response["user_login"].Value<string>(),
                   FirstName = response["user_login"].Value<string>(),
                   LastName = "",
                   Email = response["user_email"].SafeGet(x => x.Value<string>())
              };
          }
        }
      }
      
    • จากนั้นทำการแก้ไข Web.config ใน Project OAuth2.Example เพิ่มข้อความดังนี้
       <add clientType="PassportClient"
       enabled="true"
       clientId="testclient3"
       clientSecret="testpass3"
       redirectUri="~/auth" />
    • โดยต้องแจ้ง Redirect URI ให้ผู้ดูแลระบบ จากนั้นทางผู้ดูแลระบบจะแจ้ง Client ID และ Client Secret รวมถึงชื่อ Server ในการใช้งานให้ทราบ (ชื่อ server ใส่ในไฟล์ก่อนหน้านี้)
    • เมื่อรันโปรแกรม จะปรากฎหัวข้อ Login มากมายให้เลือก ให้ทดสอบในส่วนของ Login passport
    • หลังจากนั้นจะปรากฎหน้า Login ซึ่งอันนี้หน้าตาแล้วแต่ผู้ดูแลจะสร้างครับ (อันนี้ผมทำแค่เป็นตัวอย่างนะครับ)
    •  หลักจากนั้นจะปรากฎหน้าถามเพื่อขออนุญาตให้สิทธิ์การใช้งาน
    • จากนั้นระบบจะเด้งกลับมายังหน้าแรกแต่จะแสดงข้อความ Profile ดังรูป
    •   ตัวอย่างนี้ในการใช้งานจริงต้องนำไปประยุกต์เองครับ ซึ่งแสดงให้เห็นวิธีการใช้งาน OAuth สำหรับ .NET C# ว่าวิธีไม่ได้ต่างกับการใช้งาน CMS แต่อย่างใดครับ

    ==================================

    Reference :

    [1] OAuth2 client implementation for .NET : https://github.com/titarenko/OAuth2

  • คู่มือเทคนิคการใช้งาน Function พื้นฐานใน Itextsharp สำหรับมือใหม่ ตอนที่ 1

    หลังจากที่ผู้เขียนได้ทดลองใช้งาน Itextsharp มาเป็นระยะเวลานึง ในระหว่างที่ได้ทำการใช้งานนั้น ก็เกิดปัญหาต่างๆจากการใช้งานมากมาย ซึงมาจากความไม่รู้ของผู้เขียนเอง เลยได้ทำการรวบรวมข้อมูลวิธีใช้งานเบื้องต้น ให้กับผู้ที่สนใจใช้งาน Itextshap ได้สะดวกมากขึ้น ซึ่งจริงๆแล้วมีพี่ท่านนึงได้เขียนบทความเกี่ยวกับเรื่องนี้ไว้แล้วบ้างส่วน สำหรับผู้ที่สนใจสามารถอ่านได้จาก Link นี้ครับ สร้างเอกสาร PDF ด้วย iTextSharp ส่วนในบทความนี้จะทำการขยายรายละเอียดลงไปในแต่ละ Function ครับ โดย Function ที่จะพูดถึงในบทความนี้มีดังต่อไปนี้

    1. BaseFont และ Font คืออะไร

    ถ้าจะให้พูดถึง Function Basefont ให้เข้าใจง่ายๆแล้วละก็ หน้าที่ของมันคือเป็นการประกาศให้ตัว Itextsharp ทราบว่าเราต้องการใช้ Font อะไรในการทำงานบ้าง สามารถเทียบได้กับช่องเลือก Font ในโปรแกรม Office นั้นแหละครับ และ Function Font จะสร้างรูปแบบของ Font ได้ตามที่เราต้องการ ไม่ว่าจะเป็น ตัวหนา เอียง ขีดเส้นใต้ ขีดเส้นทับ เป็นค่าเริ่มต้นไว้ แล้วหลังจากนั้นเราก็สามารถนำไปใช้งานได้ตลอดการสร้างเอกสาร โดยอ้างอิง Font ที่ใช้งานมาจาก BaseFont อีกทีนึง

    ตอนนี้ก็มาดูรูปแบบการสร้าง BaseFont และทำ Font ต้นแบบเป็นตัวหนานะครับโดยสามารถทำได้ 2 วิธีคือ

    • กรณีที่เรามีชุดของ Font มาแล้วนะครับ(คือแยกตัวหนา ตัวเอียง ขีดเส้น)
    BaseFont bf_bold = BaseFont.CreateFont(@"C:\WINDOWS\Fonts\THSarabunNewBold.TTF", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED, true);
    
    Font fnt = new Font(bf, 12);
    
    
    • กรณีที่เรามี Font แค่รูปแบบเดียว
    BaseFont bf = BaseFont.CreateFont(@"C:\WINDOWS\Fonts\THSarabun.TTF", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED, true);
    
    Font fnt = new Font(bf, 12,Font.BOLD);

    จากตัวอย่างทั้ง 2 แบบ เราจะมี Font ที่มีรูปแบบของตัวหนาในชื่อของตัวแปร fnt ไว้ใช้งานได้เหมือนกันครับ โดยจะมีความแตกต่างกันคือ แบบที่ 1 นั้น จะเป็นการนำเอารูปแบบของ Font ที่ได้อ้างอิงเอาไว้มาแสดงผลบนเอกสารโดยตรง ต่างจากแบบที่ 2 จะเป็นการนำเอา Font ที่ได้ประกาศเอาไว้แบบตัวอักษรปกติมาแปลงผ่านตัว Itextsharp ให้กลายเป็นตัวหนา อีกทีโดยผ่านทาง property Font.BOLD ครับ แล้วหลายๆท่านคงสงสัยว่าทั้ง 2 แบบมีข้อดีข้อเสียอย่างไร ในแบบที่ 1 การแสดงผลของ font จะถูกต้อง สวยงามตามต้นฉบับ font ที่เราได้ทำการอ้างอิงไว้ครับ แต่ข้อเสียคือ ถ้าเราต้องการสร้าง Font ต้นแบบไว้ในหลายลักษณะ เราก็ต้องอ้างอิงตัวรูปแบบ Font ที่เราต้องการทั้งหมดไปด้วย ส่วนแบบที่ 2 นั้น เราสามารถใช้ Font อ้างอิงเพียงอันเดียว แล้วสร้างรูปแบบ Font ตามที่เราต้องการได้ไม่จำกัด แต่การแสดงผลอาจไม่สวยงามเท่ากับแบบที่ 1

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

    BaseFont bf = BaseFont.CreateFont(@"C:\WINDOWS\Fonts\THSarabun.TTF", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED, true);
    Font fnt = new Font(bf, 12,Font.BOLD,BaseColor.red);
    Font fnt = new Font(bf, 16,Font.Italic,BaseColor.green);
    Font fnt = new Font(bf, 12,Font.BOLD | Font.Italic);
    Font fnt = new Font(bf, 12 Font.BOLD | Font.Underline, BaseColor.blue)

    จากตัวอย่างที่ยกให้ดูในข้างต้นนั้น เมื่อนำไปใช้จะได้ผลลัพท์ดังต่อไปนี้

    ตัวอย่างที่ 1  AAA

    ตัวอย่างที่ 2  AAA

    ตัวอย่างที่ 3  AAA

    ตัวอย่างที่ 4  AAA

    จากตัวอย่างทั้ง 4 แบบนั้น ผมได้นำวิธีการทำแบบที่ 2 มาใช้คือการอ้างอิง font จากรูปแบบเดียว แล้วมาแปลงรูปแบบจาก Function Font อีกรอบ ตามที่เราต้องการครับ ดูแล้วไม่ยากเลยใช่ไหมครับ

    ตอนนี้ก็มาถึงข้อสรุปการใช้งาน Function BaseFont และ Font คือ

    • สร้าง Basefont ขึ้นมาโดยอ้างอิงไปยัง Font หรือ ชุดรูปแบบ Font ที่เราต้องการใช้งาน
    • สร้าง Font ตามรูปแบบที่เราต้องการ โดยอ้างอิงจาก Basefont
    • รูปแบบที่สามารถกำหนดได้มี ขนาดของFont รูปแบบของ Font(สามารถผสมได้) และสีของ Font

    2. Function สำหรับสร้างเอกสารและ export ออกไปเป็น PDF

    ส่วนต่อมาที่ผู้เขียนจะพูดถึงนั้น เป็นส่วนค่อนข้างสำคัญมากเลยทีเดียว ถ้าเราไม่ทำการเรียกใช้งาน เราก็ไม่สามารถที่จะสร้างเอกสารได้เลยครับ ซึ่ง Function ที่จะพูดถึงหลักๆคือ Document และ Function สำหรับสร้างไฟล์(ขึ้นอยู่กับภาษาที่ใช้งาน) โดยเจา Function Document ทำหน้าที่เหมือนดังตัวเอกสารหรือกระดาษนั้นเอง เหมือนเราใช้งานคำสั่งนี้ เราก็จะได้หน้ากระดาษปล่าวๆพร้อมใช้งานแล้ว โดยเราสามารถกำหนดค่าต่างๆของกระดาษได้ตามที่เราต้องการ มาดูตัวอย่างกันเลยดีกว่าครับ

    Document pdfDoc = new Document(PageSize.A4, 30, 30, 20, 20);

    จากตัวอย่าง จะเห็นว่าเราทำการสร้างเอกสารขึ้นมาโดยใช้ชื่อว่า pdfDoc เป็นเอกสารขนาด A4 โดยมีการกั้นขอบกระดาษไว้ ด้านซ้าย 30 ด้านบน 30 ด้านขวา 20 และด้านล่าง 20  แล้วค่าตัวเลขนี้มาจากไหน เราจะรู้ได้ยังไงว่าโปรแกรมจะเว้นที่ว่างบนกระดาษเท่าไร ผมมีคำตอบครับ จากการค้นหามามีการกำหนดเป็นมาตรฐานของกระดาษ โดยสามารถอ้างอิงได้ตามนี้ครับ 1 in = 2.54 cm = 72 points. ซึงตัวเลข 30,30,20,20 ก็คือค่า point นั้นเอง จากตัวอย่าง 30 point เท่ากับ 0.4 นิ้ว หรือประมาณ 1 เซนติเมตรนั้นเอง แล้วถ้าเราต้องการเปลี่ยนขนาดกระดาษละ จะต้องทำอย่างไร เราสามารถเปลี่ยนขนาดของกระดาษได้จาก Property PageSize นั้นเอง ซึ่งมีรูปแบบกระดาษให้เลือกมากมายไม่ว่าจะ ซองจดหมาย A4 A3 A2 เป็นต้น ซึ่งการเลือกขนาดของกระดาษก็ขึ้นอยู่กับลักษณะงานที่เราต้องการจะแสดงว่าต้องการขนาดเท่าไร แล้วเราสามารถตั้งค่ากระดาษเป็นแนวตั้งหรือแนวนอนได้ไหม สามารถทำได้เช่นกันครับ ตามตัวอย่างนี้เลย

    Document pdfDoc = new Document(PageSize.A4.Rotate(), 20, 15, 10, 10);

    จากตัวอย่างเราสามารถทำได้ง่ายๆโดยใช้ property PageSize และกำหนดให้ทำการพลิกกระดาษ โดยคำสั่ง Rotate() ต่อท้ายขนาดกระดาษที่เราได้เลือกเอาไว้ ต่อมาเมื่อเราทำการตั้งค่ากระดาษเรียบร้อยแล้ว เราต้องทำการเปิดตัวเอกสารของเรา ให้สามารถบันทึกข้อมูลตามที่เราต้องการลงไปได้ ด้วยคำสั่ง Open() และทำการปิดเอกสารเมื่อทำการบันทึกข้อมูลเสร้จเรียบร้อย ด้วยคำสั่ง Close() มาดูตัวอย่างกัน

    Document pdfDoc = new Document(PageSize.A4, 30, 30, 20, 20);
    pdfDoc.Open();
    ...
    pdfDoc.Close();

    นี้ก็ถือว่าจบส่วนของการสร้างเอกสารและตั้งค่ากระดาษแล้วครับ ส่วนต่อมาเป็นส่วนการเขียนเอกสารจริงผ่าน Function ของภาษาที่เราใช้งานกัน โดยตัวอย่างของผู้เขียนนั้นใช้งานภาษา C# บน .Netframework ไปดูตัวอย่างการเขียนได้เลยครับ

    Document pdfDoc = new Document(PageSize.A4.Rotate(), 20, 15, 10, 10);
    PdfWriter.GetInstance(pdfDoc, System.Web.HttpContext.Current.Response.OutputStream);
    pdfDoc.Open();
    ...
    pdfDoc.Close();
    HttpContext.Current.Response.ContentType = "application/pdf";
    HttpContext.Current.Response.AddHeader("content-disposition", "attachment; filename=StatSummary_" + DateTime.Now.ToString("yyyyMMdd") + ".pdf");
    System.Web.HttpContext.Current.Response.Write(pdfDoc);
    

    จากตัวอย่าง ผู้เขียนได้ทำการระบุว่าเอกสารที่เราสร้างนั้นกำหนด ContentType เป็น application/pdf เพื่อ Response เป็น Pdf ในบรรทัดที่ 6 และทำการตั้งชื่อเอกสาร ในบรรทัดที่ 7 สุดท้ายก็ทำการสร้างเอกสารและบันทึกไว้บนเครื่อง ในบรรทัดที่ 8 ก็ถือว่าจบในส่วนของการสร้างเอกสารแล้วครับผม


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