2012年11月7日 星期三

寄註冊成功信(C#)

一般網頁申請帳號、修改密碼、密碼提醒等等作業,
都會使用寄信通知功能。
那當然若能將那些信件內容拉出來,
可以在外編輯,而不需動到程式該有多好,
下面這些程式因此產生。

我採用的是中間有一層gmail帳號,
透過此信箱傳送至那些使用者信箱去。
因此首先必須設定此gmail帳號資訊,
程式碼如下(須貼至Web.Config檔中):
<system.net>
    <mailSettings>
      <smtp deliveryMethod="Network" from="sample@gmail.com">
        <network host="smtp.gmail.com" userName="sample" password="sample"/>
      </smtp>
    </mailSettings>
  </system.net>

上面的"from"、"host"、"userName"、"password"變數
換成自己家的,
到這寄信帳號設定已完成。

接下來是準備mailBody的部分,
要準備一個htm文件,
當然要以html格式去編寫它,
變數需要用特殊符號前後包起來,
這樣才可以再組信件內容時
可以依照不同使用者填入個別的資訊。
範例如下:
<html>
<body>
    <h2>恭喜您註冊成功!</h2>
    <p>
        此電子郵件確認您的帳號及密碼。
    </p>
    <p>
        要登錄到該網站,請使用下列憑證:
    </p>
    <table>
        <tr>
            <td>
                <b>帳號:</b>
            </td>
            <td>
                <%UserName%>
            </td>
        </tr>
        <tr>
            <td>
                <b>密碼:</b>
            </td>
            <td>
                <%Password%>
            </td>
        </tr>
    </table>
    <p>
        如果您有任何疑問,或在登錄時遇到任何問題,請聯繫網管人員。
    </p>
</body>
</html>

上面變數使用<%%>包起來,
到時可以動態換上資訊。
//寄mail部分
            StreamReader reader = new StreamReader(Server.MapPath("~/MailSample/CreateAccountMail.htm"),System.Text.Encoding.GetEncoding("big5"));
            string readFile = reader.ReadToEnd();
            string MailBody = readFile;
            MailBody = MailBody.Replace("<%UserName%>", txtEMail.Text);
            MailBody = MailBody.Replace("<%Password%>", txtPass.Text);

            Configuration configurationFile = WebConfigurationManager.OpenWebConfiguration(Request.ApplicationPath);
            MailSettingsSectionGroup settings = configurationFile.GetSectionGroup("system.net/mailSettings") as MailSettingsSectionGroup;
            MailMessage mail = new MailMessage();
            SmtpClient SmtpServer = new SmtpClient(settings.Smtp.Network.Host);//"smtp.gmail.com"
            mail.From = new MailAddress(settings.Smtp.From);
            mail.To.Add(txtEMail.Text);
            mail.Subject = "恭喜您註冊成功!";
            mail.Body = MailBody;
            mail.IsBodyHtml = true;
            SmtpServer.Port = 587;
            SmtpServer.EnableSsl = true;
            SmtpServer.Credentials = new System.Net.NetworkCredential(settings.Smtp.Network.UserName, settings.Smtp.Network.Password);
            SmtpServer.Send(mail);

上面程式碼需要講解一下,
我把所有的htm檔案都放在MailSample資料夾中,
所以我在讀取htm檔時設定讀取路徑:~/MailSample/CreateAccountMail.htm,
當然htm檔案文字中有中文字時記得使用big5解碼。

讀到的檔案用MailBody變數接起來,
再用replace將變數換掉即可。
接下來寄信帳號資訊擷取,
使用MailSettingsSectionGroup。
記得信件內容設定為html格式,
"mail.IsBodyHtml = true;"
才會使得htm檔中的html語法才會有效果。

照著這幾個步驟設定,
就可以完成寄信的功能了,
當然你可以設定不同的htm檔案,
去完成不同作業。
最近學到的一招,
放在網路上以備不時之需。
下回見!!!


2012年11月5日 星期一

並未將物件參考設定為物件的執行個體(C#)

以下所寫是個人遇到問題解決方式,
不是通用方式,
因為太多太多種撰寫方式會出現這樣的錯誤,
但不外乎就是某個物件還在執行而卡住。
個人遇到的問題是,
資料庫存取時,
一開始會去以Datareader方式讀出目前密碼,
最後面以Update的Sql語法去進行更改資料庫中密碼欄位。

每次在Update語法執行時都會出現
正所謂"並未將物件參考設定為物件的執行個體",
檢查了一下程式,
就是上面的Datareader沒有關掉所致。
所以使用Datareader時要切記:
下了Read(),
一定要有一個Close()去相呼應,
才部會導致這樣的問題產生。

若爾後遇到這樣的問題,
由不同種原因所導致,
我會再更新此篇,
下回見。

2012年10月29日 星期一

指定動態組合頁面名稱的頁面顯示(C#)

最近有需求是需要動態組合頁面名稱,
指定頁面顯示。
程式碼如下:
string FormName = "NameSpaceName." + indexrow[0]["FrameItem"].ToString();
Form frm = ((Form)Assembly.GetExecutingAssembly().CreateInstance(FormName));
frm.Show();
FormName是NameSpaceName+"."+FormName,
須為一個完整的名稱,
利用Assembly.GetExecutingAssembly().CreateInstance,
找出剛湊出來的FormName,
轉成Form形式。
最後將此Form顯示即可。

2012年10月28日 星期日

計算機程式數字鍵盤設計(C#)

POS系統設計必須採用大圖形介面設計,輸入數量也需設計數字鍵盤讓使用者輸入,因此這個需求便產生。

但我又不想要一個button一個button去寫事件,這樣太累了。

若想要數字鍵盤,甚至是英文字母鍵盤,我想要將他們的click事件寫成通用的click事件要怎麼辦到呢?請繼續看下去。

首先必須從控制項Name命名下手,將那些要寫成同一個事件的控制項命名規則一致。

我下面將採用btnKey+數字(例:數字鍵盤1的Button.Name為btnKey1),接下來在Form_Load事件上定義那幾個Button的Click事件指向自己撰寫的Click事件。

   1: foreach (Control crl in this.Controls)            
   2: {                
   3:     if (crl.Name.Contains("btnKey"))                
   4:     {                    
   5:         crl.Click += new EventHandler(btnKey_Click);                
   6:     }            
   7: }

原理就是搜尋頁面中的Controls,若Control.Name包含btnKey則加入自寫的Click事件─btnKey_Click。下面是btnKey_Click事件:




   1: private void btnKey_Click(object sender, EventArgs e)        
   2: {                        
   3:     Button btn = (Button)sender;            
   4:     int start = txtInsert.SelectionStart;            
   5:     this.txtInsert.Text = txtInsert.Text.Insert(start, btn.Text);
   6:     txtInsert.SelectionStart = start + 1;       
   7:     txtInsert.Focus();        
   8: }


這裡面也有些需的技巧,此範例中接受輸入的文字方塊Textbox名稱為txtInsert。


因為文字方塊會有游標焦點位置,總不可能預設都判斷游標焦點位置在最後方,而直接寫txtInsert.Text += btn.Text吧。


因此需要動一點手腳,我將目前游標焦點位置用SelectionStart存至start變數,利用Textbox.Text.Insert的方式於指定位置內加入字串,最後將游標焦點位置調為start變數後一格。如此一來就可以達到數字鍵盤或者英文字母鍵盤了。


在這提供給大家一個不一樣的選擇,下回見。

2012年10月22日 星期一

條碼輸入與手動輸入判別─WinCE為例(C#)

使用PDA作業都需要使用條碼輸入
但有時沒有標籤可讀取亦可能人員手動輸入
此時判斷讀取條碼或手動輸入這個需求出現了
我們都知道讀取條碼採用模擬鍵盤事件輸入
表示只要將焦點(Focus)在文字方塊上
按下PDA上的[Scan]鍵就會讀取到的條碼會自動輸入到文字方塊中
然而最後它會輸入[Enter]鍵
因此只要在文字方塊的keypress事件中
在判斷式if(e.keychar == 13)底下寫你想要做的事情就可以了
寫到這,看不出來為何要判別讀取條碼或手動輸入

以下就是關鍵所在了
若你要第二次輸入時,將焦點放回文字方塊中
按下[Scan]鍵,會發現第二次讀取條碼會輸在原本已輸入的第一個條碼後面
它不會自動清除第一次輸入的條碼
總不可能教育訓練時,跟客戶講說要輸第二次時,須將第一次輸的條碼文字清除
因此你要判斷若是條碼輸入,必須將文字方塊中的文字清除

目前遇到的PDA讀取條碼API有兩種處理方式:
1.一個字一個字輸入進去
2.會存在自己的內存後,按下[Ctrl+V]
以上兩種最後都會按下[Enter]鍵

因應兩種處理方式,各有不同方法去處理:
1.針對第一種方法,一個字一個字輸進去的
需要以時間間隔去判斷條碼或手動輸入
以下為代碼:
//GetTickCountFunc
        [DllImport("Coredll.dll", EntryPoint = "GetTickCount")]
        private static extern int GetTickCount();
//條碼與手動輸入判斷變數
        private int i = 0;
        private string tmp = "";  //暫存字串                
        private bool ManualInput = false;
        private bool AutoInput = false;
        private int dstart = GetTickCount(); 

private void txtMTL_ID_KeyPress(object sender, KeyPressEventArgs e)
        {
int dend = GetTickCount();//結束時間
                string strtime = (dend - dstart).ToString();//"毫秒"

                if (Convert.ToInt32(strtime) < 100)//判斷時間間隔,間隔小於50豪秒,TextBox清空
                {
                    txtMTL_ID.Text = "";
                    AutoInput = true;
                    ManualInput = false;
                }
                else
                {
                    tmp = "";
                    ManualInput = true;
                    AutoInput = false;
                }
                dstart = dend;

                if (e.KeyChar > 32 && e.KeyChar <= 126)
                {
                    i++;
                    tmp += e.KeyChar.ToString();
                }

if (e.KeyChar == 13)
{
 i = 0;
 if (AutoInput == true)
 {
  txtMTL_ID.Text = tmp;
 }
 if (ManualInput == true)
 {
  txtMTL_ID.Text = txtMTL_ID.Text;
 }

 //這裡寫想要做的事情
 //這裡寫想要做的事情

//清除存放資訊
 AutoInput = false;
 ManualInput = false;
 tmp = "";

         }
}
判斷原理是因為條碼輸入比人快且平均
因此以每個字輸入的時間間隔(毫秒)去判別
GetTickCount()Func是因WinCE的關係才需要
一般winform只需用DateTime.Now後用TotalMilliSeconds去判斷就可以了
ManualInput=true即手動輸入,AutoInput=true則是條碼輸入
範例中的[<100]也因每一台PDA而不同,需要自己去抓準那個時間

2.第二種會按下[Ctrl+V]的方式解方法更為簡單且準確
以下為代碼:
if (e.KeyChar == 22)
   txtMTL_ID.Text = "";

if (e.KeyChar == 13)
{
  //這裡寫想要做的事情
  //這裡寫想要做的事情
}
非常之簡單,判斷有按下[Ctrl+V]鍵將文字方塊內的文字清除
基本上PDA上面的按鍵,人員不可能會去按到這種Ctrl+V這種案件組合
因此這個方法應該是滿準確的

以上解決方式或許會有些瑕疵
但已經是可以解決我目前手邊出現的問題
當然你也可以仿照微軟的設計風格
根本不用判斷是條碼或手動輸入
只要一按到文字方塊將文字全選
這樣條碼輸入亦可蓋掉之前的
再點一下亦可將游標移至想要的位置進行輸入
這個方法最常在Excel當中見到
將selectall()方法寫在Textbox的MouseClick事件中即可
因為WinCE的Textbox中無MouseClick事件
暫時無想到其他方式
因此才會選擇使用上面兩種方式
下回見



2012年10月11日 星期四

動態新增之button.text換行(C#)

最近著手開發TouchPanel系統
系統上的每個介面的流程控制均設置於Server上
為畫面美觀,有些按鈕之顯示文字須兩行顯示
查了一下網路C#之換行必須用(Char)13及(Char)10去實現
因此Server之config上顯示文字內容欄位須訂定一個符號表示換行
欄位內容:出勤#登入
名詞解釋:
1.drbuttonlist為Datarow,button設置資料
2.drbuttonlist[i]["Name"]為顯示文字內容欄位
3.特殊符號使用"#"
4.cntr為Form上之Control
if (drbuttonlist[i]["Name"].ToString().Contains("#"))
{                            
 string[] textarr = drbuttonlist[i]["Name"].ToString().Split('#');
 for (int j = 0; j < textarr.Length; j++)
 {
  //利用(Char)13及(Char)10換行
  cntr.Text += textarr[j] + Convert.ToChar(13) + Convert.ToChar(10);
 }
}
else
{
 cntr.Text = drbuttonlist[i]["Name"].ToString();//指定其text
}
參考網址:http://www.dotblogs.com.tw/a-law/archive/2009/11/10/11508.aspx

2012年4月30日 星期一

PDA連線BlueToothPrinter(使用InTheHand元件)

公司目前案子需求是利用 PDA 連線至 BTPrinter 印出條碼;PDA 系統:WinCE5.0,BTPrinter 型號:MP3200。 雖然有寫過 WinCE 系統下執行的程式經驗,但連線至 BT 設備還是頭一遭,所以利用 google 搜尋相關資訊後,發現很多人推薦 InTheHand 元件,因此決定要用它來試看看。 其實網路上有太多程式可以參考了,從中選一個最簡單明瞭的程式做為參考,並自己動手來做做看。 程式畫面 (借用 Winform 程式畫面):
畫面中 [SearchDevices] 尋找 BlueTooth 設備,選擇欲連線之設備 (此範例為:MP3200BT-8005),按下 [Connect] 與其設備連線。並且可利用 [SendByte] 鈕與 BTPrinter 溝通。

using與變數定義如下:
using InTheHand.Net.Bluetooth;
using InTheHand.Net.Sockets;

private BluetoothClient bluetoothClient;
private Guid service = BluetoothService.SerialPort;
BluetoothService.SerialPort這個搞了我好久,原本範例中的變數定義為BluetoothService.DialupNetworking,但始終都沒有連上線,所以提醒大家不妨先看看自己要連線的設備規格再去做選擇以甚麼樣的方式連線。
搜尋設備之程式如下:
private void btnSearch_Click(object sender, EventArgs e)
        {           
            // 先 new 一個 bluetoothClient
            bluetoothClient = new BluetoothClient();
            Cursor.Current = Cursors.WaitCursor;
            // 存放 bluetooth 設備資訊
            BluetoothDeviceInfo[] bluetoothDeviceInfo = { };
            // 利用 new 出來的 bluetoothClient 搜尋 buletoothDevices
            bluetoothDeviceInfo = bluetoothClient.DiscoverDevices(10);
            // 搜尋到的設備資訊放至 Combobox 中
            cmbBTDev.DataSource = bluetoothDeviceInfo;
            // 顯示為 DeviceName
            cmbBTDev.DisplayMember = "DeviceName";
            // 值為 DeviceAddress(Connect 時會用到)
            cmbBTDev.ValueMember = "DeviceAddress";
            cmbBTDev.Focus();
            Cursor.Current = Cursors.Default;
        }
[Connect] 程式如下:
private void btnConnect_Click(object sender, EventArgs e)
        {            
            if (cmbBTDev.SelectedValue != null)
            {
                try
                {
                    //先將Combobox之value轉換為BluetoothAddress,之後再轉為BluetoothEndPoint並利用serialport連線
                    bluetoothClient.Connect(new BluetoothEndPoint((BluetoothAddress)cmbBTDev.SelectedValue, service));
                    MessageBox.Show("Connected");
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }            
        }
[SendByte]程式如下:
if (bluetoothClient.Connected == true)
            {
                //FullCmd為byte[]
                bluetoothClient.Client.Send(FullCmd);
            }
目前以上面程式完成連線並成功傳值至Bluetooth設備,當然傳甚麼樣的值要參考每個設備的protocol,因此在此不做示範。下次見:)

MP3200 簡介:http://www.cino.com.tw/products/mp/mp3200.htm
InTheHand 元件:http://inthehand.com/content/32feet.aspx
程式參考:codeproject

2012年4月19日 星期四

Form間傳參數(C#)

由於測試程式 (以下稱 A) 需模擬成多台真實程式測試其效能。有一種方法是測試程式當作一個物件,程式開啟時依照設定創造多個物件出來以達到其效果。當然另一種更簡單的方法是撰寫另一隻程式 (以下稱 B) 呼叫執行測試程式並傳入不同參數達到。
以下是 B 程式參數傳入 Function 撰寫:
private void btnRun_Click(object sender, EventArgs e)
        {
            // 參數值
            int StartPort = Convert.ToInt32(txtSPort.Text);
            // 模擬數量
            int VirtualNum = Convert.ToInt32(txtVNum.Text);
            // 跑回圈的方式開啟多台
            for (int i = StartPort; i < StartPort+VirtualNum; i++)
            {
                // 要開啟的程式名稱寫在這
                string exename = "APLoadTesting.exe";

                string target = Application.StartupPath + @"\" + exename;                
                
                ProcessStartInfo pInfo = new ProcessStartInfo(target);
                // 要傳的參數寫在這
                pInfo.Arguments = i.ToString();
                using (Process p = new Process())
                {
                    p.StartInfo = pInfo;
                    p.Start();
                }

                // 避免開啟程式共用資料會錯亂
                Thread.Sleep(2000);
            }                                  
        }
A程式之Program.cs中的Application.Run改成以下方式:
static void Main(string[] Args)
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            //有參數則接參數的方式開啟
            if (Args.Length > 0)
            {
                Application.Run(new Form1(Args[0]));
            }             
            else//無參數傳入則正常開啟
            {
                Application.Run(new Form1());
            }            
        }
A程式的Form1.cs加入以下程式:
public Form1(string EQPID)//EQPID為傳進來的參數
        {
            InitializeComponent();            
            txtIP.Text = "127.0.0.1";
            txtPort.Text = EQPID;
            txtEQID.Text = EQPID;
        }