2015年12月11日 星期五

[ASP.NET]從後台透用C#呼叫前台寫好的JavaScript function()

此文章不是如何在從*.cs中編寫一段js並到前台執行
而是在*.cs中, 使用一段前台已經寫好的js功能
此篇介紹的是作用於page內, 而非updatepanel中的js, updatepanel的請參見頁尾的msdn

假設前台(*.aspx)中有這麼一段<script>
    <script type="text/javascript">
      var optionSubjectBar = {
            'title': 'Nya~',
            'width': 200,
            'height': 300,
            vAxis: { minValue: 0 }
        };
        ...................
        function drawAxisTickColors() {
            var dataAudit = google.visualization.arrayToDataTable([
            chart.draw(dataAudit, optionAuditStackedBar);        }

        function clicktest() {
            var data = new google.visualization.DataTable();
            ...................
            chart.draw(data, optionAuditStackedBar);
        };
    </script>

在要執行該段js的C#語法中視需求插入即可
例如我想要使用「clicktest()」這個功能

(*.cs)後台
建構式為
public static void RegisterStartupScript( Page page, Type type, string keystring scriptbool addScriptTags )
  • page:一般用「Page」就對了
  • type:一般用「GetType()」就對了
  • key:給後面的指令碼有個識別值, 像是ID般的存在, 不一定要跟function相同名稱
  • script: 當然就是要執行的function了,以這個例子就是輸入「clicktest()」,要直接寫一段程式碼也是可以
  • addScriptTage:true或false, 只是會不會加上<script>...<script>而已


ScriptManager.RegisterStartupScript(Page,GetType(),"clicktest","clicktest()",true)
如果在addScriptTags用false, 那就要寫成「<script>clicktest()<script>」
通常為了避免重複一直註冊key, 會多加上判斷式
-----------------------------------------------------------------------------------------------------------------------------------------------

if (!Page.ClientScript.IsStartupScriptRegistered("clicktest"))
        {
            ScriptManager.RegisterStartupScript(Page,GetType(),"clicktest","<script>clicktest()</script>",false)
        }
-----------------------------------------------------------------------------------------------------------------------------------------------

對於單純的page也許不太需要, 但是會不斷刷新的ajax就可以節省頻寬喔

使用UpdatePanel的話,請參考這MSDN的設定:(點我前往)
另外這篇文章也不錯,說明三種註冊方法的差異(本文只使用其中一種):動態註冊JavaScript至Client端


參考資料:



2015年11月26日 星期四

[EEP]Srvtools.WebDataSource元件Master發生錯誤,無法找到欄位:XXX的值

有些欄位作為Index, 又不想讓使用者看到時, 有很多種作法
像是用「System.Data.*」操作或是ViewState等狀態管理

這篇介紹兩個簡單的方法,各有其特色




  • HiddenField:還想利用JQuery或在Client端對其有動態影響時
  • Visible=False:Client看不到, 一定要PostBack
習慣用VS編寫的話,通常會直覺的利用Visible=False
但有可能導致使用者在操作WebFormView時出現
Srvtools.WebDataSource元件Master發生錯誤,無法找到欄位:XXX的值

這時先檢查一下該控制項的Bind設定
(錯誤)  <asp:Label ID="Label1" runat="server" Text='<%# Eval("CustID") %>'  Visible="False"></asp:Label>
(正確)  <asp:Label ID="Label1" runat="server" Text='<%# Bind("CustID") %>'  Visible="False"></asp:Label>

如果連這樣都無法解決,對於該欄位又沒有資安考量時,可以考慮使用HiddenField
HiddenField是VS內建的控制項

將其插入ItemTemplate的樣板(或其他有需求的樣板模式)
<asp:HiddenField ID="HiddenField1" runat="server" Value='<%# Bind("CustID") %>' />

由於該程式碼經編譯後,在Client端看的到
會轉換成「<input type="hidden" name="WebFormView1$HiddenField1" id="WebFormView1_HiddenField1" value="1">
與利用JQuery的hide();有相同的缺點,如果是資安考量的欄位,請勿直接使用「HiddenField」

2015年11月24日 星期二

[JQuery]視條件隱藏GridView中同列首欄的內容


功能的訴求,是送出後仍能檢視,但不能再做修改(除非被更高權限的退回)
(大致上如下圖, 點下「to QC」後,會變成「Keyin OK!」,且轉為唯讀模式)

$(".AR_OK").css("color",
  function () {
        if ($(this).text() > 0) {
            $(this).prev().hide();
            $(this).text("Keyin OK!");

            $(this).parents("tr").first().find(":image").hide();
            return "green";
        }
        else {
            $(this).hide();
            return "black";
        }
    }
 )



這功能的實作,是利用class為「AR_OK」觸發
原本應該是「$(selector).css(attr, value)」,在給value值時,可以利用function(){}做變化
因為不是統一變化, 故有用到「$(this)」,對於選擇性變化的很好用
是沒用each()的另一種遍歷

【Step欄位】
先做if判斷, 是true則轉為唯讀並用綠色的「Keyin OK!」提示使用者, 並隱藏「to QC」的按鈕
如果判斷false, 則不顯示文字,留著「to QC」的按鈕

【第一欄】
四個選一個用吧, 越上面是越土法煉鋼
由於我要隱藏的剛好都是「input type=image」
Selector的部分可以用「:image」直接撈

$(this).parent().prev().prev().prev().prev().prev().prev().prev().children().hide();
$(this).parent().parent().first().children().children(":image").hide();
$(this).parents("tr").first().children().children(":image").hide();
$(this).parents("tr").first().find(":image").hide();

利用.parents("tr")一直退到<tr>
.first()表示要第一個
然後用find(":image")省略n次的children()直接到元素
至於.hide()就隨喜好了, 要改變style或繼續串燒都可以直接接

2015年11月12日 星期四

[DNS]網域未正常跳轉至新伺服器 - Name Server解析異常 / 錯亂

案件狀況

M公司的網頁伺服器,是由U公司的伺服器暫時代管
但是網頁更新後,使用者反映網頁仍然是顯示時為新的時為舊

當下就馬上用nslookup測試,還真的某些DNS解析伺服器如預期,部分不同

  • nslookup "測試網址" [測試DNS(不填則用預設DNS Server)]

果然在不同的dns server,部分會指向新的伺服器,部分卻指回舊的伺服器
測試過的DNS Server:
  • dns.hinet.net
  • 61.63.0.67
  • dns.wifly.com.tw




網域是向TISNet申請,設定為「自管DNS」
DNS Server中的設定只是在相應的「正向對應區域」,增加一個
名稱「www」, 類型「別名(CNAME)」, 資料「(U公司提供的伺服器網址)」

解決方法

這類的情況,可往幾個方向想
  • 自管的DNS Server設定異常(檢查「主機(A), 別名(CNAME)」這兩承接與轉出的設定)
  • 各層的NAME SERVERS尚未同步完成,導致部分是舊的(等愛24~48, 快則8小時)
  • A紀錄設定錯誤(本身非使用Unix, 只能朝「A紀錄」類似作用的想)
  1. 首先確認DNS Server中的設定沒有錯
  2. 再來是「等待NAME SERVER同步」... 經過了一週,原本可以看到新網頁的卻跳回就網頁,那肯定不是這個的問題了

最後發現是TISNet中,設定的問題
由於重複設定www,導致BIND的錯亂,移除原先的第一個就正常了


2015年11月5日 星期四

[T-SQL]類似Excel的VLOOKUP(或EEP的RefVal效果)

類似Excel的VLOOKUP (或是有人用find + index + match拼起來)

大致上的思路是:
  1. 利用子查詢來產生參照後的值
  2. 由於我是用一個Base的Table放所有的List, 所以還要再加上WHERE篩分類
  3. 將兩者LEFT JOIN
  4. SELECT不顯示索引而是選擇參照過去的值
↓上排是before, 下排是after。


這方法會產生大量的子查詢, 請視需求使用
應該有更好的方法, 只是T-SQL的語法待精進


【原本】
SELECT  
Design.PNo, Design.DesignVer
      ,[Drawal1]
      ,[Drawal2]
(中略)
      ,[Drawaln]
FROM  dbo.eCRF_Design
left JOIN (SELECT PNo,DesignVer ,[State] FROM eCRFProj) as ProjDesc ON ProjDesc.PNo = eCRF_Design.PNo AND ProjDesc.DesignVer=eCRF_Design.DesignVer

【模擬RefVal】
SELECT  
Design.PNo, Design.DesignVer,
D1.Desc_en AS w1,
D2.Desc_en AS w2,
(中略)
Dn.Desc_en AS wn

FROM         Design
left JOIN (SELECT PNo,DesignVer ,[State] FROM Proj) as ProjDesc ON ProjDesc.PNo = Design.PNo AND ProjDesc.DesignVer=Design.DesignVer
left JOIN (SELECT Code, Desc_en FROM Base AS Base WHERE (Category = 'Design_drawal')) AS D1 ON D1.Code = Design.Drawal1
left JOIN (SELECT Code, Desc_en FROM Base AS Base WHERE (Category = 'Design_drawal')) AS D2 ON D2.Code = Design.Drawal2
(中略)
left JOIN (SELECT Code, Desc_en FROM Base AS Base WHERE (Category = 'Design_drawal')) AS Dn ON Dn.Code = Design.Drawaln

2015年10月22日 星期四

[筆記]安裝IIS與簡易上線

安裝 IIS 7.5

在 Windows 7 Professional、Enterprise 或 Ultimate 上安裝 IIS 7.5

在VM中安裝Server 2012, MSSQL 2012, IIS(for aspx)


首先把Server 2012安裝起來

伺服器管理員→管理→新增角色及功能精靈→下一步直到「伺服器角色」
→勾選「網頁伺服器(IIS), 應用程式伺服器」→下一步到「功能」→勾選「.NET Framework 3.5功能」→下一步至「應用程式伺服器--角色服務」→勾選「網頁伺服器(IIS)支援」→新增功能下一步直到安裝完成
(安裝中提示輸入檔案來源時,將路徑指向CD:\Source\SxS)

::開始安裝SQL工具::
執行「setup」→安裝→「新增SQL Server 獨立安裝或將功能加入制憲有安裝」→確定→產品金鑰「XXX」→(跑一段安裝)→換對話視窗→下一步至「特徵」→勾選「Database Engine Services(細項隨意), 管理工具-基本, 管理工具-完整, 使用者連線SDK」→下一步至開始安裝

新增網站 & 預設首頁

  1. 進入「IIS管理員」
  2. 在「站台」→右Click「新增網站」→在「繫結」「IP位址」指定為本機IP
  3. 在剛新增的網站→切至「功能檢視」→「IIS」→「預設文件」雙Click進入設定→右Click「新增...」→輸入該目錄中的預設開啟頁

更新網頁

除了使用EEP的DataSource, 還有部分是使用VS原生的DataSource, 就要對連線字串稍作設定

遇到 HTTP Error 503. The service is unavailable

[NuGet]NPOI初探

從googlesite中搬過來的, 與其說介紹文, 比較像是個人「筆記」之類的存在
公司excel檔案都改成xlsx後, XLS相關就不再使用了
並沒有全面的介紹NPOI, 如果打算深入NPOI, 可以直接參考最底下的外部來源 

另一個與NPOI類似用途的工具:OpenXml 介紹

簡介 / 安裝方法
【簡介】
可以操作excel檔案的
Microsoft Developer Network > 學習園地 > CodePlex 教學

【安裝NPOI】
於VS中→右鍵網站或專案→管理NuGet套件→搜尋NOPI→安裝→完成
(沒錯, 就是這麼簡單)



XLS範例

使用的NPOI版本 v2.1.3.1 (供參考, 舊版本可能有部分程式碼不相容)

引入的參考

  1. using System.IO;  
  2. using NPOI.HSSF.UserModel;  
  3. using NPOI.HSSF.Util;  
  4. using NPOI.SS.UserModel;  

檔案IO

建立一個新活頁簿

  1. //建立物件  
  2. HSSFWorkbook workbook = new HSSFWorkbook();  
  3. workbook.CreateSheet("試算表A");  
  4.   
  5. //將物件寫到記憶體並產生另存  
  6. MemoryStream ms = new MemoryStream();  
  7. workbook.Write(ms);  
  8. Response.AddHeader("Content-Disposition"string.Format("attchment; filename=EmptyWorkbook.xls"));   
  9. Response.BinaryWrite(ms.ToArray());  
  10.   
  11. //清空  
  12. workbook = null;  
  13. ms.Close();  
  14. ms.Dispose();  

取出Server中已存在的活頁簿

  1. string strFilePath = string.Format(Server.MapPath(".") + "\\format_eCRF\\EmptyWorkbook.xls"); //抓取ASP.NET網頁的程式位址    
  2. HSSFWorkbook workbook;    
  3. using (FileStream fs = new FileStream(strFilePath, FileMode.Open, FileAccess.ReadWrite))    
  4. {    
  5.     workbook = new HSSFWorkbook(fs);    
  6. }  
  7.   
  8. /*--IT'S SHOW TIME!--*/  
  9.     
  10. //定番    
  11. MemoryStream ms = new MemoryStream();    
  12. workbook.Write(ms);    
  13. Response.AddHeader("Content-Disposition"string.Format("attchment; filename=EmptyWorkbook.xls"));    
  14. Response.BinaryWrite(ms.ToArray());    
  15.     
  16. workbook = null;    
  17. ms.Close();    
  18. ms.Dispose();  

cell操作 

建立cell

可以用CreateSheet / CreateRow / CreateCell串燒
(範例待續, 請先參考下方)

找尋目標cell

可以用GetSheet / GetSheetAt / GetRow / GetCell串燒到目標cell
(範例待續, 請先參考下方)


填入值

再用SetCellValue(value),可容許相當多種type

  1. //插入資料。  
  2. HSSFSheet sheet = (HSSFSheet)workbook.CreateSheet("My Sheet"); //新建資料表同時插入  
  3. sheet.CreateRow(0).CreateCell(0).SetCellValue(0);  
  4. HSSFSheet at_sheet = (HSSFSheet)workbook.GetSheetAt(0);//用索引取得資料表並插入  
  5. at_sheet.CreateRow(0).CreateCell(0).SetCellValue("at sheet");  
  6. HSSFSheet _sheet = (HSSFSheet)workbook.GetSheet("試算表 A");   //用資料表名取得資料表並插入  
  7. _sheet.GetRow(0).GetCell(0).SetCellValue("get sheet by name"); 

欄位寬度
利用SetColumnWidth(第n攔, 寬度多少)
  1. workbook.GetSheetAt(0).SetColumnWidth(3, 2048);  

幫cell上色

物件HSSFCellStyle中的各項方法
  1. //建立儲存格樣式  
  2. HSSFSheet teststyle=(HSSFSheet)workbook.CreateSheet("sheet1");  
  3. HSSFCellStyle style1 = (HSSFCellStyle)workbook.CreateCellStyle();  
  4. style1.FillForegroundColor = HSSFColor.Pink.Index;  
  5. style1.FillPattern = FillPattern.SolidForeground; //版本2.0的操作方法  
  6.   
  7. HSSFCell cell = (HSSFCell)teststyle.GetRow(0).GetCell(0);  
  8. cell.CellStyle = style1;  

函數應用

在cell中產生與欄位相呼應的值

利用LastRowNum和LastCellNum取得陣列範圍
  1. for (int i = 0; i < workbook.GetSheetAt(0).LastRowNum ; i++)  
  2. {  
  3.     int n = 65;  
  4.     for (int j = 0; j < workbook.GetSheetAt(0).GetRow(1).LastCellNum; j++)  
  5.     {  
  6.         workbook.GetSheetAt(0).GetRow(i).GetCell(j).SetCellValue(Convert.ToChar(n + j).ToString() + (i+1));  
  7.     }  
  8. }  

Q&A

有些程式碼無法起作用

如果範例是以2.0以前的版本去實作,可能會遇到這些問題
【需要強制轉型】HSSFSheet sheet = (HSSFSheet)workbook.CreateSheet("My Sheet");

【變更調用方法】style1.FillPattern = NPOI.SS.UserModel.FillPattern.SolidForeground;
建議下載官方範本去看該如何在C#中使用

CreateRow 和 GetRow 有何差異

相CreateRow想像成初始化Row會更容易理解
同:Sheet層的設定維持不變, 會在同一列(不會產生插入列的效果)
異:前者移除style後者保留, 前者Cell需要重新create cell後者維持




XLSX範例

基本上跟XLS很像, 遇到特別的再說

讀取樣板, 並另存新檔

只是能用而不太講究的話, 將HSSF全部改成XSSF就可以了
缺點是開啟檔案時, 會彈出警告視窗
參考官方範例並加以修改後(改成檔案是在記憶體中產生, 不留檔), 只要將下面這段
  Response.AddHeader("Content-Disposition", string.Format("attchment; filename=EmptyWorkbook.xlsx"));
  Response.BinaryWrite(ms.ToArray());

  workbook = null;
  ms.Close();
  ms.Dispose();
替換成這段就ok了(前半仍需完成HSSF替換成XSSF)
  Response.Clear();
  Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
  Response.AddHeader("Content-Disposition", string.Format("attachment;filename={0}", "editedWorkbook.xlsx"));
  Response.BinaryWrite(ms.ToArray());
  Response.Flush();
  Response.End();

將DataSet的資料用NPOI倒入Excel

如果有要特定格式, 可善用「string.format()」
例如日期:
  • 將「SetCellValue(column.ColumnName);」
  • 替換成「SetCellValue(string.Format("{0:yyyy/MM/dd HH:mm:ss}", row[column]));」
DataSet相關請參考:DataSet.Tables 屬性

/*--此處開始編輯檔案*/
  XSSFSheet sheet = (XSSFSheet)workbook.GetSheetAt(2);

  for (int i = 0; i <= 2;i++ )
  {
      int rowIndex = i*3;
      
      dt = ds.Tables.Count > 0 ? ds.Tables[i] : new DataTable();
      XSSFRow headerRow = (XSSFRow)sheet.CreateRow(rowIndex);
      foreach (DataColumn column in dt.Columns)
    headerRow.CreateCell(column.Ordinal).SetCellValue(column.ColumnName);

      foreach (DataRow row in dt.Rows)
      {
    XSSFRow dataRow = (XSSFRow)sheet.CreateRow(rowIndex+1);
    foreach (DataColumn column in dt.Columns)
    {
        dataRow.CreateCell(column.Ordinal).SetCellValue(row[column].ToString());
    }
    rowIndex++;
      }
  }




2015年10月13日 星期二

[JavaScript]可顯示英文大寫月份, 僅輸入時間可存完整datetime

在Actual Time僅輸入時間後, 能在DB中存入完整日期

DayDateNo.Actual TimeComments
1Dec/31/20151
2Jan/1/20162
5Jan/4/20163



【Read】(使用者要求要顯示MMM/dd/yyyy)
Actual Time:<asp:Label ID="D1Label" runat="server" Text='<%# Bind("D1","{0:HH:mm}") %>' />
Dosing Date:<asp:Label ID="CaptionD1" runat="server" Text="D1:"></asp:Label>
●後台
從後台撈資料出來後放入Json,再丟到前台進行格式化處理
由於涉及安全性,程式碼不完全公開
        DataTable dt = ds.Tables.Count > 0 ? ds.Tables[0] : new DataTable(); //將查詢結果放到DataTable
        string str_json = JsonConvert.SerializeObject(dt, Formatting.Indented); //將DataTable的內容轉換成JSON格式
        PdD.Text = str_json; //將JSON格式(文字)傳遞到前台

●前台
將抓到的月份轉換成使用者想看的
            calDate(); //呈現計算後的Dosing Date
         
            function calDate() { //日期計算
                var month = new Array(12);
                month[0] = "Jan";
                month[1] = "Feb";
                month[2] = "Mar";
                month[3] = "Apr";
                month[4] = "May";
                month[5] = "Jun";
                month[6] = "Jul";
                month[7] = "Aug";
                month[8] = "Sep";
                month[9] = "Oct";
                month[10] = "Nov";
                month[11] = "Dec";

                    var d = new Date(eval("jsonP[0].Period" + $("#lbPeriod").text() + "_dose"));
                    d.setDate(d.getDate() + Number($("#FormView1_CaptionD1).text()) - 1); //Dosing Date = Period + (Day-1)
                    var strD;
                    if (d.getFullYear() >= 2000)
                        strD = month[d.getMonth()] + "/" + +d.getDate() + "/" + d.getFullYear();
                    else
                        strD = "Undefined";
                    $("#FormView1_DateLabel").text(strD); //顯示使用者想看的格式
                }

藍字的:這時還是顯示數字,作用是依照Peroid與計算的天數差,顯示預計的日期
橘子的:將數字表示的日期,置換月份(js不自帶日期加減,需各自get再串接),>=2000的作用是避免使用者未設定日期(未設定日期的年份為19xx)

foreach的變化型就賣個關子啦


【Write】
1. 設定輸入欄位的屬性(多一個隱藏欄位)
Actual Time: <asp:TextBox class="HHmm" ID="D1TextBox" runat="server" Text='<%# Eval("D1","{0:HHmm}") %>' /></asp:TextBox>
Actual Time(隱藏的): <asp:TextBox ID="TextBox1" runat="server" Text='<%# Bind("D1") %>'></asp:TextBox>

2. 隱藏欄位
將「 $(".HHmm").next().hide();」放在「  $(document).ready(function () { ... })」中

3. 利用「jquery.maskedinput.js」的功能來產生遮罩與限制輸入
            function setMasked() {
                $(".HHmm").mask("99:99", {
                    placeholder: "hh:mm", completed: function () {}
             });

4. 在「completed: function () { ... }」中放入輸入完成時要執行的動作,也就是當輸入完成時,將使用者輸入的稍加處理,放入實際上要回存DB的欄位
            $(this).next().val( //改變輸入框的值
              $(this).parent().prev().prev().children().text()  //抓取Dosing Date的日期
              + " " //在日期與時間之間加個空白
              + $(this).val() //取得使用者輸入的時間
            );

參考資料:JavaScript Date() 日期與時間