Tag Archive form

Byphunsanit

c#: Multiple Document Interface (MDI)

ตัวอย่างเขียน app แบบมีหลายฟอร์มย่อยอยู่ใน window หลัก หรือที่ฝรั่งเรียกไว้ว่า Multiple Document Interface (MDI)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace PrototypesCSharp
{
    public partial class FormMDI : Form
    {
        public FormMDI()
        {
            InitializeComponent();
        }

        public Form TryGetFormByName(string frmName)
        {
            if (panelMDI.Controls.Count > 0)
            {
                panelMDI.Controls.RemoveAt(0);
            }

            var formType = Assembly.GetExecutingAssembly().GetTypes()
                .Where(a => a.BaseType == typeof(Form) && a.Name == frmName)
                .FirstOrDefault();

            if (formType == null)
            {//if no form
                return null;
            }

            Form frm = (Form)Activator.CreateInstance(formType);

            //https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.form.acceptbutton?view=net-5.0

            frm.AutoSize = true;
            frm.AutoSizeMode = AutoSizeMode.GrowAndShrink;
            frm.Height = panelMDI.Height;
            frm.TopLevel = false;
            frm.TopMost = true;
            frm.WindowState = FormWindowState.Maximized;
            frm.Width = panelMDI.Width;

            frm.Show();

            panelMDI.Controls.Add(frm);

            return frm;
        }

        //on menu click
        private void menuStrip1_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            try
            {
                String formName = e.ClickedItem.Name.Split(new[] { "toolStripMenuItem" }, StringSplitOptions.None)[1];

                Form frm = TryGetFormByName(formName);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        //resize form inside when main windows resize
        private void panelMDI_Resize(object sender, EventArgs e)
        {
            foreach (Form frm in panel1.Controls)
            {
                frm.WindowState = FormWindowState.Normal;

                frm.WindowState = FormWindowState.Maximized;
            }
        }

    }
}

code ชุดนี้ทำงานโดยเมื่อ user click บน menuStrip (ไม่ใช้ตัว item นะ) จะอ่าน item name แล้วตัด toolStripMenuItem ออกเพื่อเรียกฟอร์ม ต้องขอขอบคุณ nemesv สำหรับคำตอบในเรื่อง Winforms, create form instance by form name ทำให้เขียน code สะอาดขึ้นเยอะเลย ไม่ต้องมา set ให้ item ทีละตัวแล้ว

Byphunsanit

joGet: AJAX Subform reload

ตัวอย่างการสร้าง ajax subform ในเรื่อง joGet: AJAX Subform มีโจทย์มาว่า input ที่ผูกกับซับฟอร์มถูกเปลี่ยนค่าจาก javascript ก็คิดว่าค่าใน subform จะเปลี่ยนตามไปด้วย แต่มันกลับไม่เปลี่ยนตาม เพราะ input ค่าเปลี่ยนก็จะทำให้มันโหลดข้อมูลมาใหม่ได้ไม่ยาก แต่มันไม่ทำงาน ลองศึกษาไฟล์ /jw/plugin/org.joget.plugin.enterprise.AjaxSubForm/js/jquery.ajaxsubform.js ดูก็พบว่ามันมีการสั่ง .off เอา event เก่าออกไปก่อน ทำให้แค่เปลี่ยนค่าเฉยๆ มันจะไม่ทำงาน

การสั่ง reload / refresh ajax subform โดยใช้ javascript ทำได้โดย

<button class="btn btn-info" id="refreshBtn" type="button">Reload form</button>
<script>
$(document).ready(function() {

$('#refreshBtn').on('click', function() {

let field1 = FormUtil.getField('field1');
field1.val('72817b0c-ac1430db-671b8000-37df4c8d');
field1.trigger('change');

});

});
</script>

แบบนี้เมื่อนเปลี่ยนค่า field1 subform จะถูกเปลี่ยนค่าตามไปด้วยแล้ว ลองดัดแปลงให้เหมาะกับงานดูนะครับ

Byphunsanit

joGet: AJAX Subform

AJAX Subform เป็น Form Element ที่ joget เตรียมไว้ให้สามารถใช้ ajax ดึงข้อมูลฟอร์มที่มีมาแสดงได้แบบไม่จำเป็นต้องโหลดทั้งหน้าใหม่ เหมือนระบบที่เราเขียนด้วยตัวเอง

การใช้งานทำได้ง่ายๆ โดยการ

    1. ในหน้า Form Builder ลาก AJAX Subform มาจากด้านซ้ายมือ
    2. คลิก Edit ไอค่อน
    3. กรอกข้อมูล
      ID
      id ของ ajax subform เช่น ajaxSubform
      Form
      เลือกฟอร์มที่ต้องการนำมาแทรกไว้ในหน้านี้
    4. คลิก Next >
    5. ติ๊ก
      Reload Subform when Parent Field value change?
      ติ๊กเพื่อจะให้ ajax ทำงานโหลด ฟอร์มใหม่เมื่อ input ที่ผูกไว้ เปลี่ยนค่า
    6. ผูกฟอร์มทั้งสองไว้ด้วยกัน
      Parent Field to keep Subform ID
      id input ในฟอร์มหลัก ที่จะใช้ลิงค์ให้ sub form ถูกดึงมาแทนที่โดยใช้ ajax
      Subform Field to keep Parent ID
      เป็น input ที่อยู่ใน subform ถ้าไม่มี การ load subform มาจาก server ก็ยังทำงานได้ตามปกติ

ทีนี้มาศึกษาการทำงานกันดูบ้าง

      1. joGet จะแทรกไฟล์ /jw/plugin/org.joget.plugin.enterprise.AjaxSubForm/js/jquery.ajaxsubform.js เพิ่มเข้ามาจากปกติ
      2. จะถูกเรียกใช้โดย javascript ที่เพิ่มเข้ามา เช่น
        <script type="text/javascript">
        $(document).ready(function() {
        $("#ajaxSubform_ajaxsubform_928").ajaxSubForm({
        contextPath: "/jw",
        id: "ajaxSubform",
        label: "",
        formDefId: "sendEmail",
        readOnly: "",
        readOnlyLabel: "",
        noframe: "",
        ajax: "true",
        parentSubFormId: "field1",
        prefix: "",
        hideEmpty: "",
        appId: "prototypes",
        appVersion: "1",
        processId: "",
        nonce: "%EF%BF%BD%00%3B%7D%28%EF%BF%BD%EF%BF%BD%EF%BF%BD",
        collapsible: "",
        collapsibleLabelExpanded: "Hide Details",
        collapsibleLabelCollapsed: "View Details",
        collapsibleExpanded: "true",
        collapsibleNoLoad: ""
        });
        });
        </script>

เมื่อไล่ดู code แล้วมันคือ plugin ที่ extended มาจาก jQuery ajax ตามปกติ ส่ง request ไปยัง /jw/web/json/plugin/org.joget.plugin.enterprise.AjaxSubForm/service โดยส่ง formDefId มีค่าเป็น form id ของ subform, parentSubFormId คือ field ในฟอร์มหลักที่ใช้ผูกกับซับฟอร์ม และจะคืนค่ากลับมาเป็น html ของฟอร์ม ในอนาคตคงได้ใช้เรียก ajax ไปโดยไม่ต้องสร้าง subform ก่อน

อ่านเพิ่มเติม

Byphunsanit

joGet: ฟอร์มส่งอีเมล์

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

สร้างฟอร์มที่จะส่งอีเมล์ได้โดยใช้ json

{"className":"org.joget.apps.form.model.Form","properties":{"id":"sendEmail","loadBinder":{"className":"org.joget.apps.form.lib.WorkflowFormBinder","properties":{}},"tableName":"prototypes","description":"","name":"Send Email","postProcessorRunOn":"both","storeBinder":{"className":"org.joget.apps.form.lib.WorkflowFormBinder","properties":{}},"postProcessor":{"className":"","properties":{}},"permission":{"className":"","properties":{}},"noPermissionMessage":""},"elements":[{"elements":[{"elements":[{"className":"org.joget.apps.form.lib.TextField","properties":{"id":"mailTo","workflowVariable":"mailTo","readonlyLabel":"","maxlength":"","encryption":"","validator":{"className":"org.joget.apps.form.lib.DefaultValidator","properties":{"message":"","custom-regex":"","mandatory":"true","type":"email"}},"value":"","label":"To","readonly":"","size":""}},{"className":"org.joget.apps.form.lib.TextField","properties":{"id":"mailCc","readonlyLabel":"","workflowVariable":"mailCc","maxlength":"","encryption":"","validator":{"className":"","properties":{}},"value":"","label":"CC","readonly":"","size":""}},{"className":"org.joget.apps.form.lib.TextField","properties":{"id":"mailBcc","readonlyLabel":"","workflowVariable":"mailBcc","maxlength":"","encryption":"","validator":{"className":"","properties":{}},"value":"","label":"BCC","readonly":"","size":""}},{"className":"org.joget.apps.form.lib.TextField","properties":{"id":"mailSubject","workflowVariable":"mailSubject","readonlyLabel":"","maxlength":"","encryption":"","validator":{"className":"org.joget.apps.form.lib.DefaultValidator","properties":{"message":"","custom-regex":"","mandatory":"true","type":""}},"value":"","label":"Subject","readonly":"","size":""}},{"className":"org.joget.apps.form.lib.TextArea","properties":{"id":"mailMessage","workflowVariable":"mailMessage","readonlyLabel":"","cols":"20","validator":{"className":"org.joget.apps.form.lib.DefaultValidator","properties":{"message":"","custom-regex":"","mandatory":"true","type":""}},"value":"","label":"Message","readonly":"","rows":"5"}},{"className":"org.joget.apps.form.lib.FileUpload","properties":{"id":"mailAttach1","fileType":"","validator":{"className":"","properties":{}},"label":"Attach","attachment":"","multiple":"","readonly":"","maxSize":"","maxSizeMsg":"File size limit exceeded","fileTypeMsg":"Invalid file type","permissionType":"","size":""}},{"className":"org.joget.apps.form.lib.HiddenField","properties":{"id":"mailAttachName1","workflowVariable":"mailAttachName1","value":"","useDefaultWhenEmpty":""}},{"className":"org.joget.apps.form.lib.HiddenField","properties":{"id":"mailAttachPath1","workflowVariable":"mailAttachPath1","value":"","useDefaultWhenEmpty":""}},{"className":"org.joget.apps.form.lib.HiddenField","properties":{"id":"mailAttachName2","workflowVariable":"mailAttachName2","value":"","useDefaultWhenEmpty":""}},{"className":"org.joget.apps.form.lib.HiddenField","properties":{"id":"mailAttachPath2","workflowVariable":"mailAttachPath2","value":"","useDefaultWhenEmpty":""}},{"className":"org.joget.apps.form.lib.HiddenField","properties":{"id":"mailAttachName3","workflowVariable":"mailAttachName3","value":"","useDefaultWhenEmpty":""}},{"className":"org.joget.apps.form.lib.HiddenField","properties":{"id":"mailAttachPath3","workflowVariable":"mailAttachPath3","value":"","useDefaultWhenEmpty":""}},{"className":"org.joget.apps.form.lib.HiddenField","properties":{"id":"mailAttachName4","workflowVariable":"mailAttachName4","value":"","useDefaultWhenEmpty":""}},{"className":"org.joget.apps.form.lib.HiddenField","properties":{"id":"mailAttachPath4","workflowVariable":"mailAttachPath4","value":"","useDefaultWhenEmpty":""}},{"className":"org.joget.apps.form.lib.CustomHTML","properties":{"id":"sendEmailTemplateA","autoPopulate":"","value":"<link href=\"\/jw\/plugin\/org.joget.plugin.enterprise.RichTextEditorField\/css\/jquery.richtext.css\" rel=\"stylesheet\" \/>\n<script src=\"\/jw\/js\/tiny_mce\/jquery.tinymce.js\"><\/script>\n<script src=\"\/jw\/plugin\/org.joget.plugin.enterprise.RichTextEditorField\/js\/jquery.richtext.js\"><\/script>\n<script src=\"\/jw\/assets\/scripts.js\"><\/script>\n<b>File Attached :: <\/b>\n<div class=\"accordion\" id=\"accordionA\"><\/div>\n<script>\n    $(document).ready(function() {\n\n        let mailMessage = $('#mailMessage');\n\n        let message = 'Dear'
+\n            '<br><br>The quick brown fox jumps over the lazy dog<br>' +\n            '<br><br>regard' +\n            '<br><br>Pitt Phunsanit';\n\n        mailMessage.val(message);\n        mailMessage.richtext({\n            \"contextPath\": \"\/jw\"\n        });\n\n        emailAttachment([{\n                \"name\": \"JasperReport 1\",\n                \"url\": \"#request.scheme#:\/\/#request.serverName#:#request.serverPort#\/jw\/web\/json\/plugin\/org.joget.plugin.enterprise.JasperReportsMenu\/service?action=report&appId=prototypes&appVersion=1&key=_&menuId=JasperReport1&type=pdf&userviewId=prototypes\"\n            },\n            {\n                \"name\": \"JasperReport 2\",\n                \"url\": \"#request.scheme#:\/\/#request.serverName#:#request.serverPort#\/jw\/web\/json\/plugin\/org.joget.plugin.enterprise.JasperReportsMenu\/service?action=report&appId=prototypes&appVersion=1&key=_&menuId=JasperReport2&type=pdf&userviewId=prototypes\"\n            },\n            {\n                \"name\": \"JasperReport 3\",\n                \"url\": \"#request.scheme#:\/\/#request.serverName#:#request.serverPort#\/jw\/web\/json\/plugin\/org.joget.plugin.enterprise.JasperReportsMenu\/service?action=report&appId=prototypes&appVersion=1&key=_&menuId=JasperReport3&type=pdf&userviewId=prototypes\"\n            },\n            {\n                \"name\": \"JasperReport 4\",\n                \"url\": \"#request.scheme#:\/\/#request.serverName#:#request.serverPort#\/jw\/web\/json\/plugin\/org.joget.plugin.enterprise.JasperReportsMenu\/service?action=report&appId=prototypes&appVersion=1&key=_&menuId=JasperReport4&type=pdf&userviewId=prototypes\"\n            }\n        ]);\n\n    });\n<\/script>","label":""}}],"className":"org.joget.apps.form.model.Column","properties":{"width":"100%"}}],"className":"org.joget.apps.form.model.Section","properties":{"id":"SendEmail","loadBinder":{"className":"","properties":{}},"visibilityControl":"","regex":"","visibilityValue":"","storeBinder":{"className":"","properties":{}},"label":"Send Email","permission":{"className":"","properties":{}}}}]}

การทำงานจะมี 2 ส่วนคือ ส่วนที่เรียกใช้ function สำเร็จรูป

<link href="/jw/plugin/org.joget.plugin.enterprise.RichTextEditorField/css/jquery.richtext.css" rel="stylesheet" />
<script src="/jw/js/tiny_mce/jquery.tinymce.js"></script>
<script src="/jw/plugin/org.joget.plugin.enterprise.RichTextEditorField/js/jquery.richtext.js"></script>
<script src="/jw/assets/scripts.js"></script>
<b>File Attached :: </b>
<div class="accordion" id="accordionA"></div>
<script>
$(document).ready(function() {

let mailMessage = $('#mailMessage');

let message = 'Dear' +
'<br><br>The quick brown fox jumps over the lazy dog<br>' +
'<br><br>regard' +
'<br><br>Pitt Phunsanit';

mailMessage.val(message);
mailMessage.richtext({
"contextPath": "/jw"
});

emailAttachment([{
"name": "JasperReport 1",
"url": "#request.scheme#://#request.serverName#:#request.serverPort#/jw/web/json/plugin/org.joget.plugin.enterprise.JasperReportsMenu/service?action=report&appId=prototypes&appVersion=1&key=_&menuId=JasperReport1&type=pdf&userviewId=prototypes"
},
{
"name": "JasperReport 2",
"url": "#request.scheme#://#request.serverName#:#request.serverPort#/jw/web/json/plugin/org.joget.plugin.enterprise.JasperReportsMenu/service?action=report&appId=prototypes&appVersion=1&key=_&menuId=JasperReport2&type=pdf&userviewId=prototypes"
},
{
"name": "JasperReport 3",
"url": "#request.scheme#://#request.serverName#:#request.serverPort#/jw/web/json/plugin/org.joget.plugin.enterprise.JasperReportsMenu/service?action=report&appId=prototypes&appVersion=1&key=_&menuId=JasperReport3&type=pdf&userviewId=prototypes"
},
{
"name": "JasperReport 4",
"url": "#request.scheme#://#request.serverName#:#request.serverPort#/jw/web/json/plugin/org.joget.plugin.enterprise.JasperReportsMenu/service?action=report&appId=prototypes&appVersion=1&key=_&menuId=JasperReport4&type=pdf&userviewId=prototypes"
}
]);

});
</script>
  • let message จะสร้างเนื้อหาอีเมล์เป็น template ให้ก่อน user จะได้ไม่ต้องเขียนเองใหม่ทุกครั้ง
  • .richtext() ทำหน้าที่เปลี่ยน textarea เป็น rich text box แทนที่จะใช้ rich text box ของ joGet เอง เพราะจะไม่มีที่ผูก workflow Variable ได้ (งงกับการออกแบบ tools ของพี่แกจริงๆ)
  • emailAttachment() จะสร้างส่วน ui ให้ user เลือกที่จะแนบไฟล์หรือไม่แนบไฟล์และแสดง preview pdf ให้ดูก่อนที่จะส่งอีเมล์ออกไป

และส่วน javascript ที่แยกออกไปเพื่อใช้ซ้ำในฟอร์มอื่นๆ

/*
pitt phunsanit
email attachment preview
version 1
*/
function emailAttachment(lists) {
let accordionA = $("#accordionA");
let expaned = "";

$.each(lists, function(a, item) {
let itemNo = a + 1;

$("#mailAttachName" + itemNo).val(item.name);
$("#mailAttachPath" + itemNo).val(item.url);

let id = "attach" + itemNo;

if (a == 0) {
expaned = " in";
} else {
expaned = "";
}

let card =
'<div class="accordion-group">' +
'<div class="accordion-heading">' +
'<input checked style="float: left; margin: 10px 10px 0 10px;" type="checkbox" value="' +
itemNo +
'">' +
'<a class="accordion-toggle" data-parent="#accordionA" data-toggle="collapse"  href="#' +
id +
'" style="margin: 0px 10px; width: 80%;">' +
item.name +
'</a><a class="fa fa-download" href="' +
item.url +
'" style="float: right; font-size: 30px; margin: -32px 20px;" target="_blank"></a></div>' +
'<div class="accordion-body collapse' +
expaned +
'" id="' +
id +
'"><div class="accordion-inner">' +
'<iframe frameborder="0" height="842" id="attachIframe" src="' +
item.url +
'" width="100%"></iframe>' +
"</div></div></div>" +
"</div>";
accordionA.append(card);
});

accordionA.on("change", 'input[type="checkbox"]', function() {
let checkbox = $(this);

let itemNo = checkbox.val();

let mailAttachName = $("#mailAttachName" + itemNo);
let mailAttachPath = $("#mailAttachPath" + itemNo);
if (checkbox.is(":checked")) {
mailAttachPath.prop("disabled", false);
mailAttachName.prop("disabled", false);
} else {
mailAttachPath.prop("disabled", true);
mailAttachName.prop("disabled", true);
}
});
}

เรื่องที่เกี่ยข้อง

Byphunsanit

Joget: แสดงเฉพาะ form / datalist

น้อง (รึเปล่า) ที่ทำงานต้องการจะแสดง datalist ในฟอร์ม ทำให้เกิดการค้นหาข้อมูลกันนิดหนึ่ง แต่จริงๆ แล้วก็มีอยู่ในแอปตัวอย่างของ Joget นั่นละ แต่ไม่ยักกะเขียนไว้ในคู่มือ Datalist Builder

สมมุติว่า url ของหน้าที่แสดง datalist คือ http://localhost:8080/jw/web/userview/crm/crm_userview_sales/_/UsersDashboard ถ้าจะแสดงเฉพาะส่วน body ไม่เอา header และ footer ได้ง่ายๆเพียงแค่ใส่ ?embed=true ไปเท่านั้น คือ http://localhost:8080/jw/web/userview/crm/crm_userview_sales/_/UsersDashboard?embed=true หลังจากนี้ก็จะเหมาะที่จะแสดงใน iframe หรือ เรียกใช้จาก ajax ได้แล้ว

จากนั้นหลังการค้นคว้าเพิ่มเติมก็ได้เจอกับ Embedded Mode ที่สามารถเพิ่ม /embed/ ก่อน /userview/ เช่น http://localhost:8080/jw/web/userview/crm/crm_userview_sales/_/UsersDashboard เป็น http://localhost:8080/jw/web/embed/userview/crm/crm_userview_sales/_/UsersDashboard

วิธีนี้สามารถใช้กับฟอร์มได้เหมือนกัน เช่น http://localhost:8080/jw/web/userview/crm/crm_userview_sales/_/UserForm?id=clark เป็น http://localhost:8080/jw/web/userview/crm/crm_userview_sales/_/UserForm?id=clark&embed=true และ http://localhost:8080/jw/web/userview/crm/crm_userview_sales/_/UserForm?id=clark&embed=true