最近项目结束了, 开发了其中的一个小系统,用作Video Processing, 我主要负责一些C#的界面开发,项目中学习了很多东西,写一篇文章来回顾一下学到的东西和遇到的问题吧。
从来没尝试过问答体,这次试试吧。
Q: 为什么要用C#做界面,相关的项目的内容是什么?
A: 因为项目涉密,所以不能开源,也不能说的太细。总之在项目中我主要负责的是这个系统的前端部分,因为算法以及很多业务逻辑的框架都搭好了。核心的算法是用C/C++写的混合模块,直接封装了dll,同时需要用到一些图像处理的库,考虑再三,决定搭配OpenCV的一些成熟的算法。
由于MFC这种已经老得掉渣的东西是有情怀的leader所摒弃的,加上之前用C#做过一个的小项目,于是就采用OpenCV for C#: Emgu 来调用算法并且用青出于WinForm而胜于WinForm的C#来完成系统。
Q: 开发工具和开发环境?C#中的设计UI的细节都有哪些?
A:第一个问题很简单,Win7 + VS2013 或者更新版本就好,完全安装了VS2013之后会有一个叫 Blend for VS 2013 的IDE,这个功能很强大,适用于界面的调整和设计,如果你用的是双屏,那么左边一个VS,右边打开Blend,爽得很!
第二个问题比较宏大,我的切入点比较小,就用一些私人的见解来写好了。本质的讲, C#是完全面向对象的,因为之前的C++和Java中已经掌握的一些OOP/OOD的思路都可以直接用,C#的源文件后缀名是.cs,而与界面的后缀名是.xaml。在.xaml中,有很多C#已经集成好了的控件(widget),比如经典的button, image, progressbar, Text, Checkbox, DocumentView, ListView, ScrollViewer等等,同时还提供了Canvas(画布),这样就可以直接拖动控件到画布上进行设计,当然也可以写代码实现,做过界面开发的人都懂的。
Q: 遇到的问题有哪些?
A: 这个问题就更多了,我分成几个部分来回答。
1. 设计一个Image控件,希望能实现点击Image中显示的图片并且响应点击事件。
- 遇到的第一个问题其实很基本,总结一下,换个表述其实就是:把Image对象做出和Button一样的效果。因为如果是修改Button的Background的话,当鼠标放到Button上的时候,Button会编程原来的样子,不好看,所以希望Image对象也响应点击事件。 解决的办法其实很简单,就在.xaml文件中对应的Image控件的定义里,重新定义MouseDown()事件即可
2. BitmapImage Image Bitmap的互转
这个问题很重要,因为在做的过程中出现了大量在这三种格式互相转换的情况。在stackoverflow上找到了很多答案,
比如:BitmapImage -> Bitmap
public static Bitmap Stream2Bitmap( BitmapImage bitmapImage)
{
using (MemoryStream outStream = new MemoryStream ())
{
BitmapEncoder enc = new BmpBitmapEncoder ();
enc . Frames. Add( BitmapFrame. Create(bitmapImage));
enc . Save(outStream);
System . Drawing. Bitmap bitmap = new System . Drawing. Bitmap (outStream);
return new Bitmap(bitmap);
}
}
Bitmap -> Image
Image<Rgb, Byte> imageNew = new Image<Rgb, Byte>(currentBitmap);
Image -> BitmapImage
需要先将图片写到Buffer中,在按照BitmapImage的格式写回
public static BitmapImage Buffer2Bitmap( byte [] rgbFrame, int width, int height, int streamlength, int bpl)
{
stream = new MemoryStream(streamlength);
bw = new BinaryWriter(stream, System . Text. Encoding .Default);
bw . Write('B' ); bw .Write( 'M' ); bw. Write(bpl * height + 54 );
bw . Write(0 ); bw .Write( 54 ); bw. Write( 40);
bw . Write(width); bw .Write(height);
bw . Write((ushort ) 1); bw . Write((ushort ) 24);
bw . Write(0 ); bw .Write(bpl * height); bw. Write( 0);
bw . Write(0 ); bw .Write( 0 ); bw. Write( 0);
byte [] data = new byte [bpl * height];
int gIndex = width * height;
int bIndex = gIndex * 2 ;
for (int y = height - 1 , j = 0 ; y >= 0; y -- , j++ )
{
for (int x = 0 , i = 0; x < width; x++ )
{
data[y * bpl + i ++] = rgbFrame[bIndex + j * width + x]; // B
data[y * bpl + i ++] = rgbFrame[gIndex + j * width + x]; // G
data[y * bpl + i ++] = rgbFrame[j * width + x]; // R
}
}
bw . Write(data, 0 , data .Length);
bitmapImage = new BitmapImage();
bitmapImage . BeginInit();
bitmapImage . StreamSource = stream;
bitmapImage . EndInit();
return bitmapImage;
}
3. 进度条(progressbar)控件的使用
首先在.xaml文件中拖入一个进度条控件,如下图:进度条命名为ProgressBar1
< ProgressBar HorizontalAlignment = "Right" Name= "ProgressBar1" Height ="7" Grid.Row ="4">
然后在对应的.cs文件中定义一段代码,用于加载本进度条:
private void pbControl() // update progressbar
{
string pg = null;
for (int i = 0 ; i <= 100; i ++ )
{
pg = "当前进度: " + i + "%." ;
if (! this .ProgressBar1 . Dispatcher. CheckAccess())
{
//更新UI界面
this .ProgressBar1 . Dispatcher. Invoke(
DispatcherPriority . Normal,
new UpdateProgressBarDelegate (( int progress) =>
{
this .ProgressBar1 . Value = progress;
this .TextBoxForProgressBar . Text = pg;
if (100 == i)
{
Thread .Sleep( 1500 );
this .ProgressBar1 . Value = 0;
this .TextBoxForProgressBar . Text = "完成!" ;
}
}),
i);
Thread .Sleep( 500 );
}
}
}
4. 和用户交互,弹出简易的系统对话框
string messageBoxTitle = "系统提示" ;
MessageBoxButton button = MessageBoxButton . OK;
MessageBoxImage icon = MessageBoxImage . Warning;
MessageBoxResult GetUserResult = MessageBox . Show(messageBoxText, messageBoxTitle, button, icon);
switch (GetUserResult)
{
case MessageBoxResult . Yes: break ;
default : break ;
}
系统还在继续的更新,我也继续学习新的方法, 积累到一定程度了再分享吧。