2014年11月12日 星期三

[Xamarin] Xamarin的奇幻旅程


最近因為公司需要,開始學習如何用Xamarin開發 IOS,雖然主打用C#開發APP,但無論生命週期跟呼叫的function跟傳統的windows forms或ASP MVC都有相當大的差異性,有很大的一段GAP要跨過就是.

這邊紀錄一下我開發IOS時遇到的問題跟心得
  • AppDelegate,程式的進入點
    public override bool FinishedLaunching(UIApplication app, NSDictionary options) 
    { 
        // 指定APP符合螢幕大小
        UIWindow window = new UIWindow(UIScreen.MainScreen.Bounds);
        // 指定APP Root的View,這邊Config.MainStoryboard是我為了避免每次重複一直寫一樣的code
        // 所以把抓取Storyboard的程式碼整合到config,原本應該是UIStoryboard.FromName ("MainStoryboard", null)
        window.RootViewController = Config.MainStoryboard.InstantiateInitialViewController () as UIViewController; 
        // make  the window visible 
        window.MakeKeyAndVisible(); 
        return true; 
    }
    
  • Storyboard,編輯流程的地方,只要把對應的Controller抓進來並且命名,Xamarin Studio會將Class檔案自動建好
  • 取得Storyboard裡面的ViewController方法
    UIStoryboard.FromName ("MainStoryboard", null).InstantiateViewController (viewcontroller_name) as UIViewController;
    
  • IOS頁面間的流程靠的是對NavigationController的操作,以下是針對NavigationController的一些設定
    public override void ViewDidLoad ()
    {
       //edit control in viewdidload 
       //NavigationBar的背景色
       NavigationController.NavigationBar.BarTintColor = new UIColor(0.58f,0.761f,0.231f,1f);
    
       //字的顏色
       NavigationController.NavigationBar.TitleTextAttributes = new UIStringAttributes (){ ForegroundColor = UIColor.White }; 
       
       //NavigationBar Button的顏色
       NavigationController.NavigationBar.TintColor = UIColor.White; 
       
       //設定NavigationBar的右邊按鈕為何
       UIBarButtonItem barOptionIcon = new UIBarButtonItem (UIImage.FromBundle ("icon-options"),                 
       UIBarButtonItemStyle.Plain,
       (sender, args) => 
       {              
          // button clicked     
          NavigationController.PushViewController(Config.GetStoryboardViewController("OptionViewController"),true);                 
       });
       NavigationController.NavigationBar.TopItem.SetRightBarButtonItem (barOptionIcon,true); //OptionIcon
    }
    
  • IOS抓取對應圖片的方式
    //FromBundle的方式IOS會自動對應不同的裝置抓取預備好給他的圖
    //圖片檔名命名方式 
    //[imageName]~iphone.png
    //[imageName]~ipad.png
    //[imageName]@2~ipad.png for retina顯示器
    UIImage.FromBundle ("icon-options");
    //FromFile不管任何解析度都是用這張來顯示,所以用前者的方式較佳
    UIImage.FromFile ("icon-options.png");
    
  • TableViewController的製作方式
    //製作TableSource的Class
    public class TableSource : UITableViewSource
    {
       string cellIdentifier = "OptionsTableCell";
       public List TABLE_ITEMS{ get; set;} //每列資料 
       public event EventHandler Cell_RowSelected;//給外層cell click事件
    
       public TableSource ()
       {
          TABLE_ITEMS = new List ();
       }
    
       public override nint RowsInSection (UITableView tableview, nint section)
       {
          return TABLE_ITEMS.Count;
       }
    
       public override UITableViewCell GetCell (UITableView tableView, Foundation.NSIndexPath indexPath)
       {
          UITableViewCell cell = tableView.DequeueReusableCell (cellIdentifier);
    
          //if there are no cells to reuse , cerate a new one
          if (cell == null) 
     cell = new UITableViewCell (UITableViewCellStyle.Default, cellIdentifier);
    
          cell.TextLabel.Text = TABLE_ITEMS [indexPath.Row].TITLE;
          cell.TextLabel.Font = UIFont.SystemFontOfSize(15f);
          cell.TextLabel.TextColor = new UIColor (0.453f, 0.453f, 0.453f, 1);
          cell.ImageView.Image = UIImage.FromBundle (TABLE_ITEMS[indexPath.Row].IMAGE); //cell front Image
          cell.Accessory = UITableViewCellAccessory.DisclosureIndicator;
          return cell;
        }
    
        public override void RowSelected (UITableView tableView, Foundation.NSIndexPath indexPath)
        {
     //把值拋到OptionViewController的Cell_RowSelected的事件
     if (Cell_RowSelected != null) 
             Cell_RowSelected(this,new TableRowSelectedEventArgs(){ indexPath = indexPath});
       
     tableView.DeselectRow (indexPath, true); //cancel select row style
        }
    }
    
    //製作TableSource的Class
    public class TableSource : UITableViewSource
    {
       string cellIdentifier = "OptionsTableCell";
       public List TABLE_ITEMS{ get; set;} //每列資料 
       public event EventHandler Cell_RowSelected;//給外層cell click事件
    
       public TableSource ()
       {
          TABLE_ITEMS = new List ();
       }
    
       public override nint RowsInSection (UITableView tableview, nint section)
       {
          return TABLE_ITEMS.Count;
       }
    
       public override UITableViewCell GetCell (UITableView tableView, Foundation.NSIndexPath indexPath)
       {
          UITableViewCell cell = tableView.DequeueReusableCell (cellIdentifier);
    
          //if there are no cells to reuse , cerate a new one
          if (cell == null) 
     cell = new UITableViewCell (UITableViewCellStyle.Default, cellIdentifier);
    
          cell.TextLabel.Text = TABLE_ITEMS [indexPath.Row].TITLE;
          cell.TextLabel.Font = UIFont.SystemFontOfSize(15f);
          cell.TextLabel.TextColor = new UIColor (0.453f, 0.453f, 0.453f, 1);
          cell.ImageView.Image = UIImage.FromBundle (TABLE_ITEMS[indexPath.Row].IMAGE); //cell front Image
          cell.Accessory = UITableViewCellAccessory.DisclosureIndicator;
          return cell;
        }
    
        public override void RowSelected (UITableView tableView, Foundation.NSIndexPath indexPath)
        {
     //把值拋到OptionViewController的Cell_RowSelected的事件
     if (Cell_RowSelected != null) 
             Cell_RowSelected(this,new TableRowSelectedEventArgs(){ indexPath = indexPath});
       
     tableView.DeselectRow (indexPath, true); //cancel select row style
        }
    }
    
    //Table Cell Class 
    public class TableCell 
    { 
       public string TITLE { get; set; } //標題 
       public string IMAGE { get; set;} //圖片 
       public TableCell () { } 
    
       public TableCell (string title , string image) 
       { 
          TITLE = title; IMAGE = image; 
       } 
    } 
    
    然後在ViewController這麼做
    public OptionViewController (IntPtr handle) : base (handle) 
    { 
       _tableview = SetTable (); 
       this.Add (_tableview); //把Table加進去View裡面 
    } 
    /// <summary> 
    /// Set Table Data 
    /// </summary> 
    /// <returns>The table.</returns> 
    private UITableView SetTable() 
    { 
       UITableView tv = new UITableView (View.Bounds); 
       TableSource _source = new TableSource (); 
       List<tablecell> celllist = new List<tablecell> (); 
       //將options的menu選項加進table cell裡面 
       foreach (Options item in Enum.GetValues(typeof(Options))) 
       { 
          string[] descript = Tools.GetEnumDescription (item).Split(new String[]{";"},  
          StringSplitOptions.RemoveEmptyEntries); 
    
          celllist.Add(new TableCell (descript[0], descript[1]));//標題;圖片 
        } 
        
        _source.TABLE_ITEMS = celllist; tv.Source = _source; 
        //cell click觸發的事件 
        _source.Cell_RowSelected += (object sender, TableRowSelectedEventArgs e) =&gt; 
        { 
           switch ((Options)e.indexPath.Row) 
           { 
              case Options.BuildAge:  
    NavigationController.PushViewController(Config.GetStoryboardViewController("Options_BuildAgeViewController"),true); 
           break; 
           }
         }; 
         return tv; 
    }
    

2014年8月10日 星期日

【XML】將資料匯出到EXCEL - 透過ClosedXML套件


最近有寫Excel報表的需求,ClosedXML真的是個不錯的套件

 var workbook = new XLWorkbook(); //new Excel
 var sheet_A = workbook.Worksheets.Add("Sheet_A"); //新增Sheet到 Excel
 
 //塞資料到Sheet中
 worksheet.Cell(_index, 1).Value = "欄位一";
 worksheet.Cell(_index, 2).Value = "欄位二";
 worksheet.Cell(_index, 3).Value = "欄位三";
 worksheet.Cell(_index, 4).Value = "欄位四";
 worksheet.Cell(_index, 5).Value = "欄位五";

用法還滿簡單的,如果有更深入的用法想瞭解也可以到ClosedXml網站看文件
在使用的過程有遇到一個情況就是從DB裡面撈出大量的資料倒入Excel時,匯出打開檔案會顯示Excel已經損毀,可是直接把DB撈出來的資料用複製貼上卻可以完整地貼到Excel中,存檔再打開也不會壞掉。這個問題搞了好久...最後查到大陸網站得到以下的解答
/// 
/// 提示错误,错误信息如下: 十六进制值 0x0B 是无效的字符错误
///产生原因是xml文件中包含低位非打印字符造成的
///处理方法:在产生xml文件的时候,过滤低位非打印字符
/// 
/// 
/// 
public static string ReplaceLowOrderASCIICharacters(string tmp)
{
   StringBuilder info = new StringBuilder();
   foreach (char cc in tmp)
   {
      int ss = (int)cc;
      if (((ss >= 0) && (ss <= 8)) || ((ss >= 11) && (ss <= 12)) || ((ss >= 14) && (ss <= 32)))
         info.AppendFormat(" ", ss);//&#x{0:X};
      else info.Append(cc);
   }
   return info.ToString();
}

把字元丟到這個function去過濾後就可以避免錯誤了!!

2014年6月3日 星期二

【.NET】防止XSS攻擊


一直都有聽過XSS攻擊的原理跟方式,但都沒有好好認真研究過,剛好最近看到一個小遊戲在這邊推薦給大家:
瞭解XSS攻擊的小遊戲

查了一下.NET防止XSS攻擊的有效方法,剛好保哥有寫過相關的文章
保哥-推薦使用 Microsoft Anti-Cross Site Scripting Library v3.1
裡面介紹的非常詳細,這邊就不再多做贅述了。

唯一需要特別注意的地方是Anti-XSS Library在4.2版的時候將GetSafeHtml 、GetSafeHtmlFragment移到了新的DLL了。所以如果依照文章去下載這個DLL會發現沒有這兩個方法可用,請改用Nuget搜尋HtmlSanitizationLibrary


使用方法也變成
Sanitizer.GetSafeHtmlFragment(inputext);

真是很好很強大阿!!!!

2014年5月27日 星期二

【SQL】WAITFOR DELAY


有時候大量資料批次匯入時,我們會用寫SQL語法的方式增加效率(LinqToSql會讓你慢到server timeout),但常常發生SQL處理速度太快,造成datein的時間完全一樣,讓人分不清楚哪筆先哪筆後 這種先後順序在權限的處理上又異常的重要,畢竟權限通常都是拿最後一筆的時間再繼續往下疊加下去,如果分不出哪一筆應該是最後一筆可就慘了阿!!!

SQL表示:今天的我沒有極限


所以要在每一筆寫入的時候告訴SQL Server說,"來,您辛苦了。休息個0.1秒再繼續吧!!"
這時候你就會需要WAITFOR DELAY語法了(MSDN解說)

--每insert一筆都呼叫一次
WAITFOR DELAY '00:00:00.010'
打完收工

2014年3月13日 星期四

【SQL】SQL的foreach,逐筆執行


需求:將Table_A的資料塞到Table_B,並把Table_B產生的ID編號回寫回Table_A的對應欄位
-- 宣告變數
DECLARE @para1 VARCHAR(50) 
DECLARE @para2 uniqueidentifier
DECLARE @para3 VARCHAR(100)

 
-- 宣告指標變數
DECLARE _cursor CURSOR FOR
SELECT s1,s2,s3 FROM Table_A where eiplstatus = 1
 
-- 啟用指標變數
OPEN _cursor
-- 以指標變數為基準點, 將取出的欄位值,依序塞給我們自訂的變數,亦即將每筆s1 => @para1、 s2 => @para2 、s3 => @para3
FETCH NEXT FROM _cursor INTO @para1,@para2,@para3
 
WHILE @@FETCH_STATUS = 0
BEGIN
    insert into Table_B(c1,c2,c3) values(@para1,@para2,@para3)
    SET @id = SCOPE_IDENTITY();
    
    update Table_A set id = @id where CURRENT of _cursor
    
    -- 指標變數往下移動, 並將取出的欄位值, 設定給我們自訂的變數
    FETCH NEXT FROM _cursor INTO @para1,@para2,@para3
END
 
-- 關閉指標變數
CLOSE _cursor
-- 釋放指標變數
DEALLOCATE _cursor