Saturday, July 9
Saturday, July 17
Monday, February 27
วิธีก๊อบปปี้เปลี่ยนชื่อ Solution ใน Visual studio 2015 community
วิธีก๊อบปปี้เปลี่ยนชื่อ Solution ใน Visual studio 2015 community
บางครั้งเราอาจะมีงานเคยซ้ำๆ กับโปรเจคเก่า แค่ อาจจะแก้ไขบาง class หรือปรับแต่งหน้าตาอีกหน่อยก็ทำงานได้แหละ แต่ เราก็ไม่อยากจะไปเพิ่มไฟล์ให้กับโปรเจคเก่า กลัวว่าจะกระทบในภายหน้า และเมื่อจะก๊อปปี้โปรเจคเก่ามาเป็นโปรเจคใหม่ namespace ก็ไม่เหมือนกันอีก อยากจะแก้ให้ namespace เป็นชื่อใหม่ โฟลเดอร์เป็นชื่อใหม่ ไฟล์ .sln เป็นชื่อใหม่ ทีนี้จะทำอย่างไร
พอดี ผมไปค้นเจอวิธีที่ work สุดสำหรับตัวผมเองใน Stackoverflow มา ก็เลยเอามาแชร์ ทดลองทำตามแล้วใช้ได้เลย
อันดับแรก เราก็ก๊อปปี้ทั้งโปรเจคโฟล์เดอร์มาเลยไปวางไว้ตรงไหนก็ได้
1. ทำการ เปลี่ยนชื่อโฟลเดอร์นี้ซะตามต้องการ เป็น โฟลเดอร์ใหญ๋ที่เก็บไฟล์ทั้งโปรเจคนะครรับ
2. ทำการดับเบิลคลิกไฟล์ .sln ของเรา มันจะเป็นโปรแกรม Visual studion ขึ้นมาตามปกติที่เราเคยเปิดนั้นแหละ
3. คลิกขวาที่ชื่อโปรเจค ใน Solution Explorer แล้วเปลี่ยนเป็นชื่อใหม่
4. คลิกขวาที่โปรเจคที่เปลี่ยนชื่อแล้ว แล้วเลือก Properties บน Application tab ให้เปลี่ยนชื่อใหม่บน "Assembly name" และ "Default namespace"
5. ที่ main cs ไฟล์ที่ใช้สร้างโค๊ดแรกของโปรเจค ให้ไปแก้ namespace โดยแก้เป็นชื่อใหม่ แล้วดับเบิลคลิกเพื่อไฮไลท์ namespace นั้น แล้วทำการเลือก edit>refactor->rename มันจะทำการ rename ทุกๆไฟล์ที่เจอ namespace เก่า สังเกตโค๊ดเราจะไม่มีสีแดงแสดง error
6. ไปที่่ Properties/AssemblyInfo.cs ทำการแก้ AssemblyTitle และ AssemblyProduct
[assembly: AssemblyTitle("New Name Here")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("New Name Here")]
[assembly: AssemblyCopyright("Copyright © 2013")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
7. บันทึกโปรเจค แล้วปิดโปรแกรม Visual studio
8. ทำการลบโฟลเดอร์ bin และ obj
9. ทำการเปิดไฟล์ .sln อีกครั้งบนโปรแกรม notepad หรือ text editor ตัวไหนก็ได้ ให้ทำการแก้ path ที่เราต้องการ ให้ตรงกับชื่อโฟลเดอร์ใหม่ของเรา แล้วบันทึก
10. ทำการดับเบิลคลิกที่ไฟล์ .sln เราอีกครั้งเพื่อเปิด Visual studio อีกครั้ง ทำการ clean project , rebuild project อีกครั้ง เป็นอันสำเร็จ
Tuesday, August 5
MCP2200 with Visual C# 2010 express
ในบางครั้ง เรามีความจำเป็นต้องทำปุ่มกดพิเศษขึ้นมานอกเหนือจากการใช้แป้นจากคีย์บอร์ดคอมพิวเตอร์ปกติ เพื่อให้โปรแกรมที่เราเขียนขึ้นมาเองทำงานฟังก์ชั่นพิเศษ หรือในบางงานเราอาจจะไม่สะดวกที่จะให้ User ทำการกดบนแป้นคีย์บอร์ด แต่ อยากให้มีปุ่มที่ทำงานเฉพาะเพื่อรับค่าการกดปุ่ม แล้วนำไปประมวลผลต่อในโปรแกรม
วันนี้ ผมมีตัวอย่างง่ายๆ ด้วยการรับค่าจากปุ่มกด Push button แบบกดติดปล่อยดับ โดยต่อเข้ากับอินพุตพอร์ตของไอซี MCP2200 ของทางบริษัทไมโครชิพ แล้วให้โปรแกรมที่เขียนด้วย C# มาอ่านค่าจากพอร์ต GPIO ของ MCP2200 และแสดงค่าบนหน้าต่างโปรแกรมของเรา
ไม่ขอกล่าวถึงรายละเอียดของ MCP2200 มากนัก ซึ่งเราสามารถหาอ่านรายละเอียดได้จากเว็บ www.microchip.com ได้เลย เพียงแต่จะบอกรายละเอียดย่อๆ ไว้ แท้จริงแล้ว MCP2200 นั้นก็คือ ไมโครคอนโทรลเลอร์เบอร์ 18F14K50 ที่ได้บรรจุ firmware ที่ทำหน้าที่เป็น USB to Serial converter ไว้แล้ว นอกจากนี้ ยังมี GPIO เหลือให้ใช้อีก จำนวนหนึ่ง (ไม่เกิน 8 pin) และยังสามารถเรียกใช้หน่วยความจำ EEPROM ให้ใช้ได้อีก 256 byte แต่ในตัวอย่างที่ผมจะทดสอบให้ดูวันนี้ เป็นแค่การเรียกใช้ GPIO2 - GPIO5 ของมันเท่านั้น
ทำการต่อวงจรตามรูป
เสร็จแล้วทดลองจ่ายไฟเข้าบอร์ดผ่านทางสาย USB ถ้าวงจรที่เราทำนั้นทำงานได้ปกติ ตัว Windows เองจะพยายามทำการติดตั้ง Driver ให้เราทำการโหลด Driver จากเว็บแล้วติดตั้งลงไป ตรงนี้ไม่ขออธิบายมากนัก ก็เหมือนๆ กับการติดตั้ง Driver ทั่วๆไป
http://www.microchip.com/wwwproducts/devices.aspx?dDocName=en546923
เสร็จแล้ว ลองทดสอบด้วยการเปิดโปรแกรม MCP2200 Configuration Utility v1.3.1 โหลดได้จากที่นี่ เหมือนกัน
http://www.microchip.com/wwwproducts/devices.aspx?dDocName=en546923
หากสามารถเชื่อมต่อไป โปรแกรมจะแสดงคำว่า Connected ที่ด้านล่าง ตามรูป
ปิดโปรแกรมไป ทีนี้ เราจะมาสร้าง Windows application เพื่ออ่านค่าจาก push button ที่เราต่อไว้กับ GPIO2, GPIO3, GPIO4, GPIO5 ของ MCP2200 เพื่ออ่านค่าสถานะ push button แล้วแสดงค่าบนฟอร์มที่เราออกแบบไว้บน Visual C# 2010 express
หน้าต่างโปรแกรม ครับ
ทำการ add reference ไฟล์ SimpleIO-M.dll ซึ่งต้องไปดาวน์โหลดที่
http://ww1.microchip.com/downloads/en/DeviceDoc/MCP2200_DLL_2013-01-28.zip
จากนั้นแตกไฟล์ออก ไฟล์จะอยู่ที่โฟวเดอร์ Managed\ เนื่องจากเราพัฒนาโปรแกรมด้วย Visual C# ซึ่งมองว่าเป็นแบบ Managed code แล้ว add reference ไปใส่ในโปรเจค C# ของเรา และในโฟวเดอร์เดียวกันนี้ เขาจะมีไฟล์ SimpleIO DLL (Managed) User Manual.pdf ให้เรา เพื่ออธิบายการทำงานแต่ละ method ศึกษาเพิ่มเติมได้จากนี่แหละครับ
ทำการ using SimpleIO; เพื่อเรียกใช้ class ใน namespace แล้วทำการเขียนโค๊ด
อธิบายการทำงานของโปรแกรม
ทำการ Initial constructor ของ SimpleIOClass ด้วยการกำหนดค่า VID, PID ที่ได้จากการตั้งค่าจากโปรแกรม MCP2200 Configuration Utility v1.3.1
Timer1 ทำหน้าอ่านค่าสถานะของ push button ที่ต่ออยู่กับ GPIO ทุกๆ 200ms
ทำการอ่านค่า Port ด้วยเมธอด SimpleIOClass.ReadPort ซึ่งจากให้ค่าเป็นแบบ One-at-a-time มีลักษณะเปลี่ยนแปลงแบบครั้งเดียว ไม่คงค่าไว้ ดังนั้นเราจึงต้องใช้ Timer1 ช่วยในการ sampling ตลอดเวลา
================= โค๊ด==========================
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using SimpleIO;
namespace SimpleMCP2200
{
public partial class Form1 : Form
{
const uint mcp2200_VID = 0x04D8;
const uint mcp2200_PID = 0x00DF;
bool isConnected = false;
public Form1()
{
InitializeComponent();
SimpleIOClass.InitMCP2200(mcp2200_VID, mcp2200_PID);
timer1.Interval = 200;
timer1.Stop();
}
private void Form1_Load(object sender, EventArgs e)
{
isConnected = SimpleIOClass.IsConnected();
if (isConnected)
{
connectedLabel.Text = "The device is connected";
connectedLabel.ForeColor = Color.Green;
if (SimpleIOClass.ConfigureIO(0xFF))
{
configLabel.Text = "Success";
configLabel.ForeColor = Color.Green;
timer1.Start();
}
else
{
configLabel.Text = "Invalid command";
configLabel.ForeColor = Color.Red;
}
}
else
{
connectedLabel.Text = "The device is NOT connected";
connectedLabel.ForeColor = Color.Red;
errorReadPortLabel.Text = "Read port Fail";
errorReadPortLabel.ForeColor = Color.Red;
configLabel.Text = "Not config";
configLabel.ForeColor = Color.Red;
}
}
private void timer1_Tick(object sender, EventArgs e)
{
unsafe
{
uint add = 0x00;
uint* returnValue = &add;
if (SimpleIOClass.ReadPort(returnValue))
{
int ret = SimpleIOClass.ReadPortValue();
if (ret != 0x8000)
{
errorReadPortLabel.Text = "Read port Success";
errorReadPortLabel.ForeColor = Color.Green;
readPortValueTextBox.Text = String.Format("{0:x2}", ret);
readPortValueTextBox.Text = String.Concat("0x",readPortValueTextBox.Text.ToUpper());
switch(ret)
{
case 0x00FB:
gpio2Label.Text = "ON";
break;
case 0x00F7:
gpio3Label.Text = "ON";
break;
case 0x00EF:
gpio4Label.Text = "ON";
break;
case 0x00DF:
gpio5Label.Text = "ON";
break;
default:
gpio5Label.Text = "OFF";
gpio4Label.Text = "OFF";
gpio3Label.Text = "OFF";
gpio2Label.Text = "OFF";
break;
}
}
else
{
errorReadPortLabel.Text = "Read port Fail";
errorReadPortLabel.ForeColor = Color.Red;
}
}
}
}
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
System.Diagnostics.Process.Start("http://visual-studio-express-project.blogspot.com/");
}
}
}
ทดสอบกด push button แล้วดูการเปลี่ยนแปลงที่หน้าโปรแกรมที่เราสร้างขึ้นมาครับ
หมายเหตุ : Dll ไฟล์นี้ ใช้ได้กับ .NET Framework 3.5 ไม่เกินนี้ ตอน Build project จำเป็นต้องเช็คบ๊อกซ์ Allow unsafe code ด้วย เพราะมีการเรียกใช้ pointer ในโค๊ด
โหลดไฟล์โปรเจคของผมไปศึกษากันดูครับ
https://dl.dropboxusercontent.com/u/65353188/SimpleMCP2200.zip
Tuesday, January 28
Emgu.CV.CvInvoke invoke OpenCV function
เนื่องด้วยข้อเสียของ Emgu คือ เรื่องเอกสาร Tutorial หาอ่านยากมากๆ (ไม่นับรวม api class referrence) ในเว็บ wiki ของเขาเอง ก็ใช่ว่าจะมีตัวอย่างครบ นั่นอาจจะเป็นเพราะว่า Emgu เอง มันเองเป็น wrapper function ที่ครอบอยู่บน OpenCV อยู่ เลยทำให้ Developer เอง ไม่อยากจะทำ Tutorial ออกมา คงเข้าใจว่า ผู้นำ Emgu ไปใช้ คงมีความรู้พื้นฐานอยู่บ้าง และหากสงสัยใน Class ไหน ก็ให้หาอ่านเอาในเอกสารของ OpenCV หล่ะมั้ง (อันนี้ ผมคิดไปเองนะครับ)
ในเมื่อไม่มีทางเลือกที่จะต้องเรียกใช้ OpenCV Function ตรงๆ ใน .NET แล้ว ทาง Emgu ก็มี CvInvoke ที่จะทำให้เราสามารถเรียกใช้ OpenCV ได้โดยตรง และเมื่อเรียกใช้โดยตรงแล้ว ก็ให้อ้างอิงเอกสารของ OpenCV ได้เลย (ซึ่งมีตัวอย่างการใช้งาน และอื่นๆ ค่อนข้างครบ) เอาหล่ะ เรามาลองเขียน Emgu application โดยใช้ความสามารถของ CvInvoke กันดูครับ
สร้าง win application ขึ้นมา โดยทำตามขั้นตอนแรกๆ Getting started Emgu with Visual C# 2010 Express จากนั้นบนหน้าฟอร์มเปล่าๆ ก็สร้าง button ขึ้นมา 1 อัน แล้วก็เขียนไปบน Event double click ของมัน โดยดับเบิลคลิกที่ปุ่มบนฟอร์ม แล้วเขียนโค๊ดเหล่านี้ลงไป
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Emgu.CV;
using Emgu.Util;
using Emgu.CV.Structure;
namespace Emgu_cvInvoke
{
public partial class Form1 : Form
{
MCvScalar color_text = new MCvScalar(255, 255, 255);
MCvFont font = new MCvFont(Emgu.CV.CvEnum.FONT.CV_FONT_HERSHEY_SIMPLEX, 0.5, 0.5);
IntPtr img = CvInvoke.cvCreateImage(new Size(200, 200), Emgu.CV.CvEnum.IPL_DEPTH.IPL_DEPTH_8U, 1);
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
CvInvoke.cvPutText(img, "Hello Emgu", new Point(50, 50), ref font, color_text);
CvInvoke.cvShowImage("Simple Emgu application", img);
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
CvInvoke.cvDestroyWindow("Simple Emgu application");
CvInvoke.cvReleaseImage(ref img);
}
}
}
จริงๆ แล้ว ตัวอย่างนี้ เราใช้่ cvCreatImage (Creates an image header and allocates the image data ) เป็นแค่การจองหน่วยความจำสำหรับเก็บข้อมูลรูปภาพ แล้วก็ใส่ตัวอักษร ด้วย cvPutText ไปบนพื้นที่ว่างบนรูปภาพ (คือพื้นสีดำว่างๆ นั่นแหละ) โดยกำหนดตำแหน่งข้อความและขนาดและสีของตัวอักษรเท่านั้นเอง
ซึ่งหากจะเปรียบเทียบรูปแบบฟังก์ชั่นของ cvCreatImage และ cvPutText กับ cvShowImage แล้วจะพบว่า รูปแบบของการใส่ Parameter เข้าไปในฟังก์ชั่นก็ยังเหมือนเดิมในเอกสารของ OpenCV ถึงแม้ว่าเรากำลังเขียนอยู่บน Emgu Framework ก็ตาม เพีิยงแค่เรียก cvInvoke แล้วก็ตามด้วยฟังก์ชั่นบน OpenCV จากนั้นก็เติมพารามิเตอร์ให้ครบตามเอกสาร เท่านี้ เราก็เรียกใช้ OpenCV function ได้แล้วครับ
แล้วทดสอบรันโปรแกรมดูครับ
งาน Image Processing บางอย่างเราอาจจะเจอวิธีแก้ไข ด้วย OpenCV Function ซึ่งถ้าหากเราต้องการนำมาใส่ใน Emgu Application ของเรา ก็เพียงเรียกใช้ cvInvoke เข้ามา แล้วก็ปรับแก้นิดหน่อยให้เป็นไปตาม .NET syntax เราก็อาจจะจบงานของเราได้เหมือนกัน ลองเอาไปประยุกต์ดูครับ
แล้วสนุกกับ Emgu นะครับ
ปล. และหากต้องการนำข้อมูลรูปภาพที่อยู่ใน pointer img กลับไปใส่ใน picturebox control ก็สามารถทำได้ ด้วยการใช้ CvInvoke.cvCopy Method โดยปลายทางของข้อมูล อาจจะเป็น bitmap แต่ size และ channel ต้องเท่ากันกับภาพต้นฉบับ และใช้ pointer properties ของตัวแปรปลายทางด้วย
private void button1_Click(object sender, EventArgs e)
{
Image<Gray, byte> img2 = new Image<Gray, byte>(200,200);
CvInvoke.cvPutText(img, "Hello Emgu", new Point(50, 50), ref font, color_text);
CvInvoke.cvShowImage("Simple Emgu application", img);
CvInvoke.cvCopy(img, img2.Ptr, IntPtr.Zero);
pictureBox1.Image = img2.ToBitmap();
}
Passing Values Between Windows Forms
ตัวอย่างการส่งข้อมูลข้ามไปมา ระหว่างฟอร์ม ใน C# วันนี้จะนำเสนอ 3 รูปแบบ อย่างง่ายๆ จริงๆ อาจจะมีรูปแบบมากกว่านี้ แต่ ผมว่า 3 รูปแบบนี้ ก็ใช้งานดีครับ หรือถ้าใครมีรูปแบบอื่นๆ นำเสนอ ก็โพสมาได้เลยครับ
รูปแบบการส่งข้อมูลจากฟอร์มหลัก ไปฟอร์มอื่นๆ 3 รูปแบบดังนี้
- การส่งข้อมูลด้วยวิธี Delegate Method
- Constructor Method
- Function Method
โดยเราจะสร้างรูปแบบการส่งข้อมูลจากฟอร์มหลักที่อยู่ในฟอร์ม 1 แล้วส่งไปหาฟอร์มที่ 2 ,3 และ 4 ในแต่ละรูปแบบ เริ่มจากวางคอมโพเนนต์บนฟอร์มต่างๆ ตามรูป
โค๊ด Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Passing_Values_Between_Windows_Forms
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
// ส่งค่าระหว่างฟอร์ม ด้วยวิธี delegate
public delegate void SendData(CheckedListBox chkList);
private void delegateButton_Click(object sender, EventArgs e)
{
Form2 frm2 = new Form2();
SendData send = new SendData(frm2.GetData);
send(checkedListBox1);
frm2.Show();
}
// ส่งค่าระหว่างฟอร์ม ด้วยวิธี constructor
private void constructorButton_Click(object sender, EventArgs e)
{
Form3 frm3 = new Form3(textBox1.Text);
frm3.Show();
}
// ส่งค่าระหว่างฟอร์ม ด้วยวิธี function
private void button1_Click(object sender, EventArgs e)
{
Form4 frm4 = new Form4();
frm4.GetData(SendValue());
frm4.Show();
}
public string SendValue()
{
return textBox2.Text;
}
private void linkLabel1_LinkClicked_1(object sender, LinkLabelLinkClickedEventArgs e)
{
System.Diagnostics.Process.Start("http://visual-studio-express-project.blogspot.com/");
}
}
}
โค๊ด Form2.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Passing_Values_Between_Windows_Forms
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
label1.Text = null;
}
public void GetData(CheckedListBox chkList)
{
System.Collections.IEnumerator i = chkList.CheckedItems.GetEnumerator();
while (i.MoveNext())
{
label1.Text += i.Current.ToString() + "\n";
}
}
}
}
โค๊ด Form3cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Passing_Values_Between_Windows_Forms
{
public partial class Form3 : Form
{
public Form3(string data)
{
InitializeComponent();
label1.Text = null;
label1.Text = data;
}
}
}
โค๊ด Form4.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Passing_Values_Between_Windows_Forms
{
public partial class Form4 : Form
{
public Form4()
{
InitializeComponent();
}
public void GetData(string str)
{
label1.Text = str;
}
private void Form4_Load(object sender, EventArgs e)
{
}
}
}
เป็นโค๊ดอย่างง่ายๆ ดูแล้วไม่น่างง งั้นผมไม่ขออธิบายนะครับ หุๆๆๆ
Friday, November 15
Let ‘s Change Scheme of Visual Studio
โปรแกรมเมอร์ส่วนใหญ่ ที่ต้องทำงานเกี่ยวกับการเขียนโค๊ด ที่ต้องอยู่หน้าจอนานๆ มักไม่ค่อยชอบที่จะเขียนโค๊ดบนหน้าจอ ที่มี scheme ที่โทนสี ออกสว่างๆ เนื่องจาก จะทำให้สายตาเกิดความล้าได้ง่าย จึงมึกจะชอบแก้ไข scheme ให้มีโทนสีทึบๆ แต่ ให้สีสันของฟังก์ชั่น ขื่อตัวแปร ต่างๆ มีสีสันสดสน เพื่อให้ง่ายต่อการสังเกต
วันนี้ ผมมี scheme สวยๆ มาแนะนำ ซึ่งทำให้การเขียนโค๊ดของเรา ดูน่าสนใจยิ่งขึ้น สามารถเลือกดาวน์โหลด scheme ที่น่าสนใน และได้รับความนิยม จาก http://studiostyl.es/
ขั้นตอน ก็ไม่ยาก ให้เลือกดาวน์โหลด scheme ที่ชอบได้เลย ให้ตรงกับเวอร์ชั่น Visual Studio ของเรา
เปิดโปรแกรม Visual C# express 2010 (หรือเวอร์ชั่น ที่มีอยู่) จากนั้น คลิกที่เมนู Tools->Setting แล้ว เลือก Import and Export Setting…..
เลือก Import Seleted environment setting
แล้วทำการ Browse ไปหา Setting ไฟล์ที่เราดาวน์โหลดมา ซึ่งโปรแกรมจะเก็บ setting เก่าไว้ด้วย หากเราเลือกให้เก็บไว้ ดังรูป
Wednesday, October 23
Emgu Convert Colour image to Gray,Binary Image
พอดีมีคนถามหาโค๊ด ซึ่งเป็นโค๊ดที่ผมเริ่มศึกษา Emgu ใหม่ๆ (ตอนนี้ ก็ยังใหม่อยู่เหมือนเดิม ;P ) ก็เลยเอามาอัพไว้ที่บล๊อกสักหน่อย
ไม่มีอะไรมากครับ เป็นการโหลด Image เข้ามา แล้วทำการแปลงเป็นภาพ Gray , Binary Image เฉยๆ
ไลบรารี่ที่ดึงเข้ามาใช้ในโปรเจค
อันนี้เป็นโค๊ด
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using Emgu.CV;
using Emgu.CV.Structure;
namespace CovertImage
{
public partial class Form1 : Form
{
string imageFileName;
Image<Bgr, Byte> imageCoverted;
public Form1()
{
InitializeComponent();
}
private void button2_Click(object sender, EventArgs e)
{
string extension;
OpenFileDialog openFile = new OpenFileDialog();
openFile.Filter = "Image Files(*.BMP;*.JPG;*.GIF)|*.BMP;*.JPG;*.GIF|All files (*.*)|*.*";
openFile.FilterIndex = 1;
openFile.RestoreDirectory = true;
if (openFile.ShowDialog() == DialogResult.OK)
{
extension = Path.GetExtension(openFile.FileName);
if (extension == ".jpg" | extension == ".gif" | extension == ".bmp")
{
imageFileName = openFile.FileName;
Image<Bgr, byte> img = new Image<Bgr, byte>(openFile.FileName);
imageCoverted = new Image<Bgr, Byte>(openFile.FileName);
pictureBox1.Image = img.ToBitmap(pictureBox1.Width, pictureBox1.Height);
pictureBox2.Image = null;
}
else
{
pictureBox1.Image = null;
pictureBox2.Image = null;
MessageBox.Show("File does not supported");
}
}
}
private void button1_Click(object sender, EventArgs e)
{
processImage(imageCoverted, 0);
}
private void button4_Click(object sender, EventArgs e)
{
processImage(imageCoverted, 1);
}
private void processImage(Image<Bgr,byte> img,int Mode)
{
switch (Mode)
{
case 0:
label2.Text = "Gray";
pictureBox2.Image = img.Convert<Gray, byte>().ToBitmap(pictureBox2.Width, pictureBox2.Height);
break;
case 1:
label2.Text = "Binary";
pictureBox2.Image = img.Convert<Gray, Byte>().ThresholdBinary(new Gray(127), new Gray(255)).ToBitmap(pictureBox2.Width, pictureBox2.Height);
break;
}
}
private void toolStripStatusLabel1_Click(object sender, EventArgs e)
{
System.Diagnostics.Process.Start("http://vs-visual-studio.blogspot.com/");
}
}
}
ดาวน์โหลดซอร์สโค๊ดและโปรเจค https://dl.dropboxusercontent.com/u/65353188/CovertImage.zip
Saturday, October 19
Seven-segment LED Control for .NET
บางที การแสดงผลตัวเลข แบบ 7-segment บน C# application form ก็ดูดีเหมือนกันนะ ยิ่งถ้าเรากำลังออกแบบหน้าต่างสำหรับงานควบคุม แสดงผล แล้วหล่ะก็ ดูดีเลยทีเดียว
วันนี้ เรามาติดตั้้ง 7-Segment ลงบน C# win form กันนะครับ พอดี มีคนทำไว้แล้ว เราก็แค่ ดึงมาใช้งานเลย
เริ่มจากดาวน์โหลด ซอร์สโค๊ดมาก่อนครับ จาก CodeProject จากนั้นทำการแตกไฟล์ไว้ที่ไหนก็ได้ เราต้องการแค่ไฟล์ SevenSegment.cs และ ไฟล์ SevenSegmentArray.cs ของเขามาใส่ไว้ที่ toolbox ของเรา
ทีนี้ เรามาสร้างโปรเจคใหม่ บน C# ของเรา ให้เลือกเป็น windows form นะครับ เราจะได้ form เปล่าๆ ขึ้นมา 1 form ยังไม่ต้องทำอะไร กับมันครับ ให้เราไปคลิกขวาที่ project ของเรา แล้วเลือก Add->Existing Items… แล้วก็เลือกไฟล์ SevenSegment.cs และ ไฟล์ SevenSegmentArray.cs ที่ดาวน์โหลดมา เอามาไว้ที่โปรเจคของเรา
จากนั้น ก็ไปที่เมนู Build—> Build Solution (F6) ไม่น่าจะมี Error อะไรเกิดขึ้น เพราะเรายังไม่ได้ Coding อะไรเลย น่าจะ success นะครับ จากนั้น ให้สังเกตที่ Toolbox ของเรา จะเกิดกลุ่ม Tab ใหม่ขึ้นมา ชื่อ SenvenSegTest Components
เพียงแค่นี้ เราก็ได้ 7-segment มาไว้ใช้งานบนหน้าฟอร์มของเราแล้วครับ ที่เหลือ ก็แล้วแต่จะโปรแกรมมิ่ง กันเอาเองนะครับ ลองศึกษาดูวิธีการใช้งานจาก source code ที่ดาวน์โหลดมาแล้วกันครับ ไม่น่าจะยากเกินความสามารถกันนะครับ
C# Random number
ปกติก็ใช้ไม่บ่อยนะ แต่เวลาจะใช้ทีไรลืมทุกที ปกติจะใช้เวลาทดสอบโปรแกรม ให้มันสุ่มตัวเลขขึ้นมา แล้วเอาไปแสดงที่หน้า control แต่ด้วยความที่มักจะลืม ก็เลยเขียนไว้ที่บล๊อกซะเลย ดูซิ มึงจะลืม อีกไหม 55555+
ประกาศตัวแปรไว้ใน class project ของเรา เป็นตัวแปร Random
Random num = new Random();
ทีนี้ พอเราจะนำมันไปใช้งาน ก็แค่ สั่งให้มันสุ่มตัวเลขมาให้เรา โดยจะมี 2 เมธอดที่สำคัญคือ
Next และ Double ( 2 ตัวนี้ ก็น่าจะเพียงพอแล้ว )
ถ้าเราต้องการให้มันสุ่มตัวเลขจำนวนเต็ม ตั้งแต่ 0 จนถึง System.Int32.MaxValue เช่น เราต้องการให้มีค่าตั้งแต่ 0 ถึง 1000 เราก็ใช้รูปแบบนี้ num.Next(1000)
หากต้องการสุ่มจำนวนเต็มตั้งแต่ 500 ถึง 1000 ก็ใช้รูปแบบนี้ num.Next(500,1000)
หากเราต้องการสุ่มเลยทศนิยม ก็ใช้ num.NextDouble() มันจะสุ่มตัวเลขระหว่าง 0.0 ถึง 1.0 ซึ่งเป็นตัวเลขชนิด Double
แล้วถ้าอยากได้ค่าอยู่ระหว่า่ง 0.0 ถึง 100.99 หล่ะ ง่ายๆ ก็แค่เอา
num.NextDouble() * 100 แค่นี้ เราก็จะได้ตัวเลขที่ถูกสุ่มมาระหว่าง 0.0 ถึง 1.0 แล้วคูณด้วย 100
หวังว่า คงไม่ลืมแล้วหล่ะ เขียนเอง อ่านเอง แบบนี้
Saturday, October 5
C# ติดต่อโลกภายนอกด้วย SerialPort Class
ถึงแม้ว่าคอมพิวเตอร์จะพัฒนาไปไกล จนถึงระดับ 64 บิตแล้วก็ตาม สิ่งหนึ่งที่ยังเป็นที่นิยม ยังมีใช้อยู่ทั่วไปก็คือ พอร์ตอนุกรม โดยเฉพาะในโลก Embedded system แล้ว หากคุณต้องการที่จะรับส่งข้อมูลระหว่างอุปกรณ์ภายนอก และโปรแกรมภายในคอมพิวเตอร์แล้ว พอร์ตอนุกรม หรือ Serial Port ก็ยังเป็นที่นิยมอยู่ ด้วยความที่มันติดต่อกันในรูปแบบที่ไม่ซับซ้อน สามารถเข้าใจได้ง่าย ช่วยให้งานจบได้เร็ว จึงทำให้ทุกวันนี้ เรายังพบเห็นโปรแกรมที่รับส่งข้อมูลทางพอร์ตอนุกรมอยู่ ถึงแม้่ว่าคอมพิวเตอร์บางเครื่องจะไม่มีพอร์ตอนุกรมแล้วก็ตาม เราก็ยังสามารถที่จะใช้ตัวแปลง USB to Serial ทำงานได้เหมือนๆ กัน
วันนี้เรามาเขียนโปรแกรมรับส่งข้อมูลทางพอร์ตอนุกรมกันครับ โดยผมจะเขียน application เพื่อรับส่งข้อมูลระหว่างคอมพิวเตอร์ กับ ไมโครคอนโทรลเลอร์ผ่านทาง Serial Port โดยมีเงือนไขดังนี้
MCU ทำการส่งข้อมูลออกไป โดยมีรูปแบบดังนี้ #ADC1,ADC2,ADC3* โดยเราให้
- # แสดงการเริ่มต้นส่งข้อมูล
- ADCx ค่าอนาล๊อกที่อ่านได้จากช่อง Analog input ของ MCU มีค่าอยู่ระหว่าง 0-1023
- * แสดงจุดสิ้นสุดข้อมูล
ซึ่ง Visual C# จะรับข้อมูลแล้วไปประมวลผลตามที่เราออกแบบไว้ จากนั้น Visual C# สามารถส่งข้อมูลออกไปหาที่ตัว MCU ได้ด้วยรูปแบบดังนี้ #Button1,Button2,Button3* โดยเราให้
- # แสดงการเริ่มต้นส่งข้อมูล
- Buttonx แสดงค่าสถานะการกดปุ่ม 0:ปุ่มไม่ถูกกด, 1:ปุ่มถูกกด
- * แสดงจุดสิ้นสุดข้อมูล
ผมออกแบบหน้าต่างฟอร์มแบบนี้ครับ
ในส่วนของโค๊ด
เริ่มจากเราต้อง using System.IO.Ports; เพื่อจะสร้างออปเจค serial port จาก class SerialPort เมื่อเราได้ using เข้ามาแล้ว เราจึงสามารถทีจะสร้างออปเจคได้
serial = new SerialPort(comportComboBox.SelectedItem.ToString(), 9600, Parity.None, 8, StopBits.One);
เราทำการเพิ่ม event handler ให้กับออปเจค เพื่อจะแยกโค๊ดไปเขียนการรับค่าทาง serial port
serial.DataReceived += new SerialDataReceivedEventHandler(serial_DataReceived); // พิมพ์ serial.DataReceived += แล้่วกด Tab 2 ครั้ง โปรแกรม Visual C# ide เขาจะสร้าง method serial_DataReceived ให้อัตโนมัติ
แล้วผมก็เขียนโค๊ดจัดการการรับข้อมูลใน event handler นี้
void serial_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
string d = serial.ReadLine();
int intBegin = d.IndexOf("#");
int intEnd = d.IndexOf("*");
int length = intEnd - (intBegin+1);
char[] ch = { ','};
raw = d;
data = d.Substring(intBegin + 1, length).Split(ch);
this.BeginInvoke(new updateAnalogLabel(updateLabel));
}
ส่วนการอัพเดทตัวเลขที่ได้รับมาไปที่ control ต่างๆบนฟอร์มนั้น เราต้องอาศัย delegate พังก์ชั่นจัดการในส่วนนี้ เพราะในระหว่างที่รับข้อมูลอยู๋นั้น มันไม่มีส่วนไหนของโปรแกรมไปอัพเดทข้อมูลบนฟอร์มเลย เราจึงต้องพึง delegate เพื่อทำ callback ฟังก์ชั่น
private delegate void updateAnalogLabel();
private void updateLabel()
{
analog1Label.Text = data[0];
analog2Label.Text = data[1];
analog3Label.Text = data[2];
serialRecieveTextBox.AppendText(raw);
}
ในส่วนของการส่งข้อมูลออกจากโปรแกรมไปทาง serial port ที่เราได้ตกลงกันไว้คือ โปรแกรม C# จะทำการส่งสถานะการกดปุ่มบนฟอร์ม เพื่อไปบอกให้ MCU ขับหลอด LED
private void sendStateButton()
{
string s = "#" + Convert.ToInt32(stateButton1).ToString() + "," + Convert.ToInt32(stateButton2).ToString() + "," + Convert.ToInt32(stateButton3).ToString() + "*";
if (serial != null)
{
if (serial.IsOpen)
{
serial.Write(s);
}
}
}
ผมอัพไว้ที่ dropboxแล้ว
https://dl.dropboxusercontent.com/u/65353188/easySerial2013.zip
อ้างอิง : SerialPort Class , Delegates Tutorial
หรืออ่านเต็มๆ ได้ที่นี่ครับ คลิก
Thursday, September 26
Properties.Settings.Default.Save(); จำค่าไว้ก่อนปิด
พอดีมีคนถามมา ก็เลยลองหาดูในเนต ก็เจอว่ามีวิธีการเก็บบันทึกค่าต่างๆ ที่เรากรอกทิ้งไว้ในฟอร์ม ก่อนปิดหน้าต่างโปรแกรม แล้ว สามารถเรียกกลับมาคืนได้ ผมเคยคิดว่าจะเก็บลง Registry ของ windows (ซึ่งไม่น่าจะง่าย) แต่ มาเจอวิธีที่ง่ายกว่านั้นอีก เรามาดูกันเลย
เริ่มแรกก็สร้างฟอร์มง่ายๆ ก่อน ให้มีแค่ Textbox อันเดียวก็พอ
จากนั้นคลิกขวาที่โปรเจคของเรา ในหน้าต่าง Solution Explorer แล้วเลือก Properties เลือกที่ Tab setting และทำการกรอกค่าตามรูป
------+-------+-------
Name Type Scope
------+-------+-------
text string User
x int User
y int User
ในที่นี้ เราจะให้ text เก็บค่าใน textbox ที่เคยพิมพ์ไว้ ส่วน x และ y จะเก็บค่าตำแหน่ง form ก่อนปิดหน้าต่างโปรแกรม เมื่อกำหนดเรียบร้อยแล้วให้ทำการบันทึกไฟล์ไว้ก่อน
จากนั้นทำการกำหนดและเขียนโค๊ดดังนี้
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace persistingForm
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.StartPosition = FormStartPosition.Manual;
}
private void textBox1_KeyUp(object sender, KeyEventArgs e)
{
Properties.Settings.Default.text = textBox1.Text; // เก็บค่าสตริงจาก textbox1 ลงใน properties text
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
Properties.Settings.Default.x = this.Location.X;
Properties.Settings.Default.y = this.Location.Y;
Properties.Settings.Default.Save(); // อย่าลืมสั่ง save ก่อนปิดโปรแกรม
}
private void Form1_Load(object sender, EventArgs e)
{
this.Location = new Point(Properties.Settings.Default.x, Properties.Settings.Default.y);
textBox1.Text = Properties.Settings.Default.text;
}
}
}
ลองรันโปรแกรม ดูครับ แล้วลองเปรียบเทียบกับโปรแกรมก่อนๆ หน้านี้ดูครับ จะเห็นว่า โปรแกรมนี้ที่เราเขียนขึ้น จะมีการจดจำค่าก่อนปิดโปรแกรมตามที่เรากำหนดไว้ในโค๊ด ลองเอาไปประยุกต์ดูครับ
ขอให้สนุกกับ .NET ครับ
อ้างอิง: http://msdn.microsoft.com/en-us/library/aa730869(v=vs.80).aspx
Saturday, September 14
List video devices from PC by DirectShowLib
ในกรณีที่เราอยากทำให้ Application ของเราสามารถเลือกได้ว่าจะติดต่อกับกล้องตัวใน ถ้าเครื่องคอมพิวเตอร์ของเรามีมากกว่า 2 ตัว เราจะทำอย่างไร ถึงจะทำให้ application ที่เรากำลังสร้างนั้นมองเห็นกล้องทั้งหมดในเครื่องคอมพิวเตอร์เครื่องนั้นได้
วันนี้ผมจะแนะนำความสามารถของ Library ตัวหนึ่งชื่อ DirectShowLib ที่เอาไว้ดึงรายชื่อกล้องทั้งหมดที่ต่ออยู่ในเครื่องคอมพิวเตอร์ของเราออกมาเป็นตัวแปร array จากนั้นเราก็สามารถที่จะใช้ ComboBox ใช้แสดงรายชื่อกล้องทั้งหมด ไว้ให้ user เอาไว้เลือกต่อได้ ว่าจะติดต่อกับกล้องตัวไหน
อันดับแรก ก็ให้ไปดาวน์โหลด Library ตัวนี้ก่อนครับ อยู่ใน sourceforge ก็มี ดาวน์โหลดได้ที่นี่ http://sourceforge.net/projects/directshownet/files/ เมื่อดาวน์โหลดมาได้แล้ว เราต้องการไฟล์ DirectShowLib-2005.dll ที่อยู่ในโฟลเดอร์ \DirectShowLibV2-1\lib ของมัน
เรามาเริ่มสร้างโปรเจคกัน ในที่นี้ผมให้เป็น Windows Forms Application ที่เมนู Project เลือก Add reference หรือคลิกขวาที่ References ที่ ใต้ชื่อ Project เราที่ solution explorer ทำการ Browse หาไฟล์ DirectShowLib-2005.dll
สร้างหน้าต่าง form ตามรูป แล้วทำการเขียนโค๊ด (อันนี้ผมไม่บอกละเอียดแล้วนะ ถือว่า พอมีประสบการณ์กันบ้างหล่ะ)
เรามาดูที่โค๊ดกันครับ หัวใจของเรื่องนี้ ก็อยู่ตรงที่ เราสร้างตัวแปร object จาก Class DsDevice ที่อยู่ใน namespace ของ DirectShowLib (ฉะนั้นอย่าลืมที่ using DirectShowLib; ด้วยนะครับ)
ผมประกาศตัวแปร DsDevice[] videoDevices; ก่อน
จากนั้น ผมก็ทำการสร้างอินสแตนซ์จาก Class DsDevice แล้วก็เอามาวนลูปแสดงใน ComboBox
videoDevices = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice);
if (videoDevices.Length == 0)
{
throw new Exception();
}
for (int i = 1, n = videoDevices.Length; i <= n; i++)
{
string cameraName = i + " : " + videoDevices[i - 1].Name;
cameraCombo.Items.Add(cameraName);
}
cameraCombo.SelectedIndex = 0;
ทีเหลือก็ไม่มีอะไรมาก เอา index ที่ได้จากการเลือก combobox สร้างอินสแตนซ์ Capture แล้วก็โยนให้ picturebox แสดงผล
ลองดูโค๊ดเต็มๆ
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Emgu.CV;
using Emgu.Util;
using Emgu.CV.Structure;
using DirectShowLib;
namespace AnyCameras
{
public partial class Form1 : Form
{
DsDevice[] videoDevices;
private Capture capture = null;
public Form1()
{
InitializeComponent();
timer.Interval = 100;
// show device list
try
{
videoDevices = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice);
if (videoDevices.Length == 0)
{
throw new Exception();
}
for (int i = 1, n = videoDevices.Length; i <= n; i++)
{
string cameraName = i + " : " + videoDevices[i - 1].Name;
cameraCombo.Items.Add(cameraName);
}
cameraCombo.SelectedIndex = 0;
}
catch
{
cameraCombo.Items.Clear();
cameraCombo.Items.Add("No cameras found");
cameraCombo.SelectedIndex = 0;
cameraCombo.Enabled = false;
}
}
private void StartCamera()
{
capture = new Capture(cameraCombo.SelectedIndex);
timer.Start();
}
private void StopCamera()
{
timer.Stop();
if (capture != null)
{
capture.Dispose();
}
}
private void timer_Tick(object sender, EventArgs e)
{
Image<Bgr, Byte> frame = capture.QueryFrame();
cameraPictureBox.Image = frame.ToBitmap();
}
private void startButton_Click(object sender, EventArgs e)
{
StartCamera();
startButton.Enabled = false;
stopButton.Enabled = true;
}
private void stopButton_Click(object sender, EventArgs e)
{
StopCamera();
startButton.Enabled = true;
stopButton.Enabled = false;
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
timer.Stop();
if (capture != null)
{
capture.Dispose();
}
}
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
System.Diagnostics.Process.Start("http://vs-visual-studio.blogspot.com/");
}
}
}
ลองดาวน์โหลดโค๊ดไปเล่นกันครับ https://dl.dropboxusercontent.com/u/65353188/AnyCameras.zip



