總: 董事長剛吃完飯,等等讓他吃藥
我: 好(看了一下桌面),只有一個杯子?
總: 對,你到底有沒有在顧(用心)?飯後都只有一杯藥
我: 有啊(心想早上明明看到2個杯子的藥,還是我看錯了?)
(之後)
總: 你...
我: 嗯(不想回答有意義的文字了)
(中午,問了一下護理師)
我: 請問早上餐後的藥是一杯還兩杯?
護: 兩杯呵,一杯裡面三顆藥,一杯喝的
我: 謝謝。(心想,掯!早上總經理是在唸不爽的嗎?到底是誰沒在顧?)
2018年4月29日 星期日
不爽 0001
早上總經理帶來早餐(一袋兩個餐+另一袋一杯飲料)
總: 看你要吃什麼餐
我: 隨手拿了一個吃完後
總: 有飲料
我: 打開一看只有一杯,你喝什麼?
總: 你喝
(我拿出來時不小心打翻飲料,桌上地上都有)
總: 龜龜毛毛(一手拿早餐,一手拿在處理飲料)
我: 我自己處理
總: 不用,代誌都不會做
(我待了幾秒後,就不爽的離開)
(過一會兒,總經理來電,我不想接,直接回來)
總: 突然跑去哪裡?
我: (沒回應,因為不爽,心想有必要這樣罵人嗎?)
總: 看你要吃什麼餐
我: 隨手拿了一個吃完後
總: 有飲料
我: 打開一看只有一杯,你喝什麼?
總: 你喝
(我拿出來時不小心打翻飲料,桌上地上都有)
總: 龜龜毛毛(一手拿早餐,一手拿在處理飲料)
我: 我自己處理
總: 不用,代誌都不會做
(我待了幾秒後,就不爽的離開)
(過一會兒,總經理來電,我不想接,直接回來)
總: 突然跑去哪裡?
我: (沒回應,因為不爽,心想有必要這樣罵人嗎?)
2018年4月3日 星期二
Windows Form 將 FormBorderStyle 設為 none 時,視窗的拖曳方式
因為專案畫面上的需求(Form 表頭的顏色與設計),
所以將 Windows Form 的 FormBorderStyle 設定 none 後,再自行加上 TableLayoutPanel (tableLayoutPanel1) 等控制項來偽裝
此時如果遇上使用者使用的電腦解析度,低於設計時,會造成畫面被切掉,此時因為 FormBorderStyle 設為 none,所以 Form 也拉不動,所以需要再對於拖曳功能加工(沒事找事做)
對於 Form 內的控制項可以單純的在 MouseDown(按下滑鼠)、DragEnter(拖曳開始)、DragDrop(拖曳結束) 事件中寫下相對應的語法,可參考 在 Windows Form 中執行拖放作業
但現在是在 Windows Form 外執行拖放作業,可以用win32 的角度來撰寫語法
此時如果遇上使用者使用的電腦解析度,低於設計時,會造成畫面被切掉,此時因為 FormBorderStyle 設為 none,所以 Form 也拉不動,所以需要再對於拖曳功能加工(
對於 Form 內的控制項可以單純的在 MouseDown(按下滑鼠)、DragEnter(拖曳開始)、DragDrop(拖曳結束) 事件中寫下相對應的語法,可參考 在 Windows Form 中執行拖放作業
但現在是在 Windows Form 外執行拖放作業,可以用win32 的角度來撰寫語法
// win32 的角度來撰寫語法 public const int WM_NCLBUTTONDOWN = 0xA1; public const int HT_CAPTION = 0x2; [System.Runtime.InteropServices.DllImportAttribute("user32.dll")] public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam); [System.Runtime.InteropServices.DllImportAttribute("user32.dll")] public static extern bool ReleaseCapture(); // 再在 tableLayoutPanel1 的 MouseDown 事件寫下 private void tableLayoutPanel1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) { if (e.Button == MouseButtons.Left) { ReleaseCapture(); SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0); } }或是在 tableLayoutPanel1 的 MouseDown、MouseMove 事件撰寫語法
// 在 tableLayoutPanel1 的 MouseDown、MouseMove 事件撰寫語法 private Point startPoint; // 紀錄目前視窗的位置 private void tableLayoutPanel1_MouseDown(object sender, MouseEventArgs e) { //當滑鼠擊以左點擊控制項的範圍內時,透過計算紀錄目前視窗的位置 if (e.Button == MouseButtons.Left) { startPoint = new Point(-e.X + SystemInformation.FrameBorderSize.Width, -e.Y - SystemInformation.FrameBorderSize.Height); } } private void tableLayoutPanel1_MouseMove(object sender, MouseEventArgs e) { // 當滑鼠擊按著左鍵移動時,記錄下移動的位置 if (e.Button == MouseButtons.Left) { // 滑鼠指標的位置 Point mousePos = Control.MousePosition; // 新視窗的位置(等於滑鼠指標目前的位置與先前視窗位置的位移) mousePos.Offset(startPoint.X, startPoint.Y); // 改變視窗位置 Location = mousePos; } }
2018年3月2日 星期五
BackgroundWorker 的使用
專案需求:一支常駐程式可以撈資料
想法大概是:一個 Timer 定期檢查、一個 ProgressBar 顯示進度、一個 backgroundworker 避免影響使用者、一個 NotifyIcon 可以出現桌面的右下角
程式寫法大致上是:
新增一個 Form,在上面放一個 ProgressBar
然後拉一個 Timer
一個 BackgroundWorker,並設定 WorkerReportsProgress = true,才可以修改 ProgressBar 的進度 然後在初始化後設定 form 的位置
把timer1_Tick()事件用另一個function 包起來,然後稍微修改一下
想法大概是:一個 Timer 定期檢查、一個 ProgressBar 顯示進度、一個 backgroundworker 避免影響使用者、一個 NotifyIcon 可以出現桌面的右下角
程式寫法大致上是:
新增一個 Form,在上面放一個 ProgressBar
然後拉一個 Timer
一個 BackgroundWorker,並設定 WorkerReportsProgress = true,才可以修改 ProgressBar 的進度 然後在初始化後設定 form 的位置
public Form1() { InitializeComponent(); // 縮小視窗,以觸發 notifyIcon (不直接觸發是為了保留之後若要點擊縮小的 Icon 時可以恢復視窗) this.WindowState = FormWindowState.Minimized; this.TopLevel = true; this.screenWidth = Screen.PrimaryScreen.Bounds.Width; this.screenHeight = Screen.PrimaryScreen.Bounds.Height; //this.Location = new Point(screenWidth - Width - 5, screenHeight); //設定視窗位置在右下角最高可以顯示的位置, 工具列上方 this.stopHightLocation = Screen.PrimaryScreen.Bounds.Height - (Screen.PrimaryScreen.Bounds.Height - Screen.PrimaryScreen.WorkingArea.Height + this.Height); this.Location = new Point(screenWidth - Width - 5, stopHightLocation); }在 BackgroundWorker (Name 設為 bw) 的事件上分別寫上
void bw_DoWork(object sender, DoWorkEventArgs e) { object myParameter = e.Argument; // myParameter 是帶過來要用的參數 // 開始執行要很久的動作 ...(略) // 結束 }
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e) { // 把視窗叫起來讓使用者看到進度條 if (this.WindowState == FormWindowState.Minimized) { this.Show(); // 顯示視窗 this.WindowState = FormWindowState.Normal; } // 修改進度條的進度 progressBar1.Value = e.ProgressPercentage; // 以文字的方式顯示進度條的百分比 label2.Text = string.Format("{0:f0}%", (progressBar1.Value / Convert.ToDouble(progressBar1.Maximum)) * 100); }
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { this.WindowState = FormWindowState.Minimized; // 隱藏視窗 timer1.Enabled = true; // 工作完成了,再次啟用 timer1 }再來在 form load 事件內啟用 timer1
private void timer1_Tick(object sender, EventArgs e) { // 如果背景程式忙碌中,就停止這次的背景 if (bw.IsBusy) return; timer1.Enabled = false; // 然後將 timer 停用 // 這邊可以收集要的參數 // 若收集參數也很耗時,則可以另開一個backgroundworker來做,然後在backgroundworker_completed事件內執行主要的忙碌工作 // 或是將收集參數的工作也被到bw裡面,但此時可能不單純只顯示progressbar,可能還需要顯示進度的內容說明 // 然後呼叫backgroundworker 執行 (可以加判斷是否真的需要呼叫背景程式,減少觸發次數) if (myParameter != null) bw.RunWorkerAsync(myParameter); }後來看到另一篇文章寫到,原來也可以在 BackgroundWorker_Completed 事件中,再次執行耗時的工作即可,減少使用一個 timer 元件,
把timer1_Tick()事件用另一個function 包起來,然後稍微修改一下
void PrepareToRunWorkerAsync() { // 如果背景程式忙碌中,就停止這次的背景 if (bw.IsBusy) return; // 這邊可以收集要的參數 // 若收集參數也很耗時,則可以另開一個backgroundworker來做,然後在backgroundworker_completed事件內執行主要的忙碌工作 // 或是將收集參數的工作也被到bw裡面,但此時可能不單純只顯示progressbar,可能還需要顯示進度的內容說明 // 然後呼叫backgroundworker 執行 bw.RunWorkerAsync(myParameter); }然後在 Form1() 建構式中直接執行上述的 function
public Form1() { InitializeComponent(); // 縮小視窗,以觸發 notifyIcon (不直接觸發是為了保留之後若要點擊縮小的 Icon 時可以恢復視窗) this.WindowState = FormWindowState.Minimized; this.TopLevel = true; this.screenWidth = Screen.PrimaryScreen.Bounds.Width; this.screenHeight = Screen.PrimaryScreen.Bounds.Height; //this.Location = new Point(screenWidth - Width - 5, screenHeight); //設定視窗位置在右下角最高可以顯示的位置, 工具列上方 this.stopHightLocation = Screen.PrimaryScreen.Bounds.Height - (Screen.PrimaryScreen.Bounds.Height - Screen.PrimaryScreen.WorkingArea.Height + this.Height); this.Location = new Point(screenWidth - Width - 5, stopHightLocation); PrepareToRunWorkerAsync(); }修改一下 BackgroundWorker_Completed 事件
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { this.WindowState = FormWindowState.Minimized; // 隱藏視窗 // 若使用者沒有下取消命令,則在Completed 事件下再次執行耗時的工作 if (e.Cancelled == false) PrepareToRunWorkerAsync(); }當然上述做法需再搭配一個啟動耗時工作的事件,不然一但取消後,就只有重開執行檔一途了
void buttonStart(object sender, EventArgs e) { PrepareToRunWorkerAsync(); }參考:C# 使用 BackgroundWorker 背景執行
2018年2月23日 星期五
動態控制 ContextMenuStrip 不出現
通常在按下滑鼠右鍵要出現選單時,都會用到 ContextMenuStrip 這個控制項,使用方式如下:
1.從工具箱點兩下 ContextMenuStrip,產生 contextMenuStrip1
2.點擊該 contextMenuStrip1,可以編輯清單內容
3.選擇 contextMenuStrip1 要出現在哪個控制項,在該控制項的 ContextMenuStrip 屬性上選擇 contextMenuStrip1
但是當要動態控制 contextMenuStrip1 要不要出現時,
無法寫成
// 假設 contextMenuStrip1 要出現在 treeView1 的右鍵時 treeView1.ContextMenuStrip = "";
只能在 contextMenuStrip1.Opening 事件上動手腳
boolean bContextMenuStripVisible = false; // 指定是否顯示 ContextMenuStrip private void contextMenuStrip1_Opening(object sender, CancelEventArgs e) { if (bContextMenuStripVisible == false) { e.Cancel = true; } }
2018年2月22日 星期四
TableLayoutPanel 內的控制項的 Dock 屬性無法完全作用
TableLayoutPanel 是可以讓控制項排列整理的一個控制項(有點繞舌)
因專案需要,在 TableLayoutPanel 內塞了幾種控制項(Label、TextBox、ListBox)
然後把TableLayoutPanel 內的控制項的 Dock 屬性設定為 Fill,塞滿格子
有沒有發現
TextBox 的 Dock 設為 Fill 後,沒完全 Work?
原來是因為 TextBox 的 MultiLine 是 False 的關係,因為只能有一行字,當然無法撐開控制項的高度,所以修改的方式有兩種,視需求選擇使用
1. 改變字型大小
2. 把 MultiLine 設為 True
ListBox 的 Dock 設為 Fill 後,沒完全 Work?
原來 ListBox 有一個屬性 IntegralHeight(表示清單是否可以只包含完整的項目),意思應該是指 ListBox 的高度不足以顯示完整的 Item 時,只會展開到完整顯示的 Item 的高度,所以只要將 IntegralHeight 設為 False,就可以達到 Dock=Fill 的完整效果了
如果要將控制項完全貼合 TableLayoutPanel,則調整內部的各控制項的 Margin 屬性即可
TextBox 的 Dock 設為 Fill 後,沒完全 Work?
原來是因為 TextBox 的 MultiLine 是 False 的關係,因為只能有一行字,當然無法撐開控制項的高度,所以修改的方式有兩種,視需求選擇使用
1. 改變字型大小
2. 把 MultiLine 設為 True
ListBox 的 Dock 設為 Fill 後,沒完全 Work?
原來 ListBox 有一個屬性 IntegralHeight(表示清單是否可以只包含完整的項目),意思應該是指 ListBox 的高度不足以顯示完整的 Item 時,只會展開到完整顯示的 Item 的高度,所以只要將 IntegralHeight 設為 False,就可以達到 Dock=Fill 的完整效果了
如果要將控制項完全貼合 TableLayoutPanel,則調整內部的各控制項的 Margin 屬性即可
2018年2月9日 星期五
NChar 與 Char 的差別
簡言之
NChar 是以位元組(2個位元)的方式儲存資料(NVarChar 亦同)
Char 是以位元的方式儲存資料(VarChar 亦同)
說明:
位元組:可以儲存半型字 或 全型字
位元:僅可以儲存半型字
有點雙人床與單人床的概念
雙人床:可以睡 1 個人或是 2 個人
單人床:僅可以睡 1 個人
所以當你要存的資料
只包含 英文、數字,即半型字,可以用 Char 即可
只包含 中文,即全型字,也可以用 Char 但記得長度要開 2 倍,或是直接用 NChar,長度就不用開到 2 倍
舉例:
以下語法會出現 字串或二進位資料會被截斷 的訊息
因為資料"五"是一個全型字,而欄位Char(1)僅能存一個半型字
若是 中文、英文、數字夾雜,那就看個人喜愛了
另外提供兩個SQL函數(Len、DataLength),可以幫助釐清上面的差別
將語法改成如下:
將欄位長度改成 3
再來,將資料內容修改一下(儲存一個全型字和一個半型字)
NChar 是以位元組(2個位元)的方式儲存資料(NVarChar 亦同)
Char 是以位元的方式儲存資料(VarChar 亦同)
說明:
位元組:可以儲存半型字 或 全型字
位元:僅可以儲存半型字
有點雙人床與單人床的概念
雙人床:可以睡 1 個人或是 2 個人
單人床:僅可以睡 1 個人
所以當你要存的資料
只包含 英文、數字,即半型字,可以用 Char 即可
只包含 中文,即全型字,也可以用 Char 但記得長度要開 2 倍,或是直接用 NChar,長度就不用開到 2 倍
舉例:
以下語法會出現 字串或二進位資料會被截斷 的訊息
因為資料"五"是一個全型字,而欄位Char(1)僅能存一個半型字
declare @MyTable Table (ColName Char(1)) insert into @MyTable Values ('五')改用 NChar
-- 改用 NChar declare @MyTable Table (ColName NChar(1)) insert into @MyTable Values ('五')或是改成 2 倍長度
--改成 2 倍長度 Char(2) declare @MyTable Table (ColName Char(2)) insert into @MyTable Values ('五')就OK了
若是 中文、英文、數字夾雜,那就看個人喜愛了
另外提供兩個SQL函數(Len、DataLength),可以幫助釐清上面的差別
將語法改成如下:
將欄位長度改成 3
-- 在 Char 的情況下 declare @MyTable Table (ColName Char(3)) insert into @MyTable Values ('五') select *, Len(ColName), DATALENGTH(ColName) from @MyTable -- 結果: -- Len(ColName) = 1,即資料的字元數 -- DATALENGTH(ColName) = 3,即欄位的位元數
-- 在 NChar 的情況下 declare @MyTable Table (ColName NChar(3)) insert into @MyTable Values ('五') select *, Len(ColName), DATALENGTH(ColName) from @MyTable -- 結果: -- Len(ColName) = 1,即資料的字元數 -- DATALENGTH(ColName) = 6,即欄位的位元數
再來,將資料內容修改一下(儲存一個全型字和一個半型字)
-- 在 Char 的情況下 declare @MyTable Table (ColName Char(3)) insert into @MyTable Values ('五5') select *, Len(ColName), DATALENGTH(ColName) from @MyTable -- 結果: -- Len(ColName) = 2,即資料的字元數 -- DATALENGTH(ColName) = 3,即欄位的位元數
-- 在 NChar 的情況下 declare @MyTable Table (ColName NChar(3)) insert into @MyTable Values ('五5') select *, Len(ColName), DATALENGTH(ColName) from @MyTable -- 結果: -- Len(ColName) = 2,即資料的字元數 -- DATALENGTH(ColName) = 6,即欄位的位元數
標籤:
Char,
DataLength,
Len,
NChar,
SQL Server
2018年1月19日 星期五
還原TreeView 的展開狀態
例:
如果在 A 下的 C 下加入 D,如果只是在 TreeView 上改變顯示,那麼要遍歷整個 Tree,找出所有的 C (B 下有一個 C)之後,改變畫面,雖然可以這樣做,但程式碼上似乎複雜了點
所以改變思路,在 C 下加入 D 之後,重新綁定 TreeView 的資料。
但是重新綁定 TreeView 之後,TreeView 會收合到剩下第一層,使用者就要重新展開之後,再往下加入其他子項目,這點其實很麻煩。
所以在重新綁定前先記錄下目前 TreeView 的展開狀態,綁定之後再還原展開狀態,程式碼加下
設定兩個全域變數
記錄展開狀態
還原展開狀態
使用方式
如果在 A 下的 C 下加入 D,如果只是在 TreeView 上改變顯示,那麼要遍歷整個 Tree,找出所有的 C (B 下有一個 C)之後,改變畫面,雖然可以這樣做,但程式碼上似乎複雜了點
所以改變思路,在 C 下加入 D 之後,重新綁定 TreeView 的資料。
但是重新綁定 TreeView 之後,TreeView 會收合到剩下第一層,使用者就要重新展開之後,再往下加入其他子項目,這點其實很麻煩。
所以在重新綁定前先記錄下目前 TreeView 的展開狀態,綁定之後再還原展開狀態,程式碼加下
設定兩個全域變數
private Dictionary<string, bool> NodesStatus = new Dictionary<string, bool>(); // 記錄展開狀態 private string SelectNodeFullPath = ""; // 記錄選擇到的節點
記錄展開狀態
/// <summary> /// 記錄展開狀態 /// </summary> /// <param name="nodes"></param> private void GetTreeNodesStatus(TreeNodeCollection nodes) { foreach (TreeNode node in nodes) { if (node.IsExpanded) { NodesStatus[node.FullPath] = true; } else { NodesStatus.Remove(node.FullPath); } if (node.IsSelected) { SelectNodeFullPath = node.FullPath; } GetTreeNodesStatus(node.Nodes); } }
還原展開狀態
/// <summary> /// 還原展開狀態 /// </summary> /// <param name="nodes"></param> private void SetTreeNodesStatus(TreeNodeCollection nodes) { foreach (TreeNode node in nodes) { if (NodesStatus.ContainsKey(node.FullPath)) { node.Expand(); } if (node.FullPath == SelectNodeFullPath) { treeView1.SelectedNode = node; } SetTreeNodesStatus(node.Nodes); } }
使用方式
// 對treeView1做了某些操作之後(例如加入子選項) GetTreeNodesStatus(treeView1.Nodes); BindTreeViewData(); // 綁定TreeView SetTreeNodesStatus(treeView1.Nodes);
2018年1月17日 星期三
C# Window Form - 動態指定Form尺寸
如果想要動態控制 Form 的尺寸,做法會先指定以某個控制項(或原點)做為基準,指定控制項的位置後,確定最右下角的那個控制項之後,才能決定 Form 的尺寸。
比方右下角有一個 Button(button1),指定的 Form 的語法會是
iControlSpacer 是控制項之間的間隔
比方右下角有一個 Button(button1),指定的 Form 的語法會是
this.Height = button1.Top + button1.Height; this.Width = button1.Left + button1.Width;但這樣加完的結果是看不到,因為 Form 的最上方有一條標題列,要再加上這標題列的高度才行,所以改成
this.Height = button1.Top + button1.Height + iControlSpacer + (this.Height-this.ClientSize.Height); this.Width = button1.Left + button1.Width + iControlSpacer + (this.Width - this.ClientSize.Width);其中 this.Height-this.ClientSize.Height 是標題列高度的計算方式,
iControlSpacer 是控制項之間的間隔
標籤:
動態,
標題列高度,
C#,
Form Size,
WindowForm
訂閱:
文章 (Atom)