# 控件使用
添加控件前,先选定添加的父控件
# Label
- this.label1.AutoSize = false 否则它会自己计算所需的大小
# TextBox
- this.textBox1.AutoSize = false 否则它会自己计算所需的大小
- 自定义背景色:this.txtmoble.BackColor = System.Drawing.Color.FromArgb(241,242,247);
# PictureBox
- 背景透明:backColor属性设置成Transparent,再设置picturebox.Parent = 父容器(注意控件顺序)
//注意控件的顺序,先写的在上面
this.picHome.Parent = this.picMain;
this.picPre.Parent = this.picMain;
this.picNext.Parent = this.picMain;
this.panelMenu.Parent = this.picMain;
this.panelCon.Parent = this.picMain;
- Image:抽象类,图像的统称,Bitmap:具体类,位图,像素图
Bitmap img = new Bitmap("c:\\example\\123.jpg");
//使用资源 Resources.resx
Bitmap img = Properties.Resources.Img_GeNie;
//使用资源 form1.resx
ComponentResourceManager myTest = new ComponentResourceManager(typeof(Form1));
Bitmap photo=myTest.GetObject("12121212") as Bitmap;
int w = img.Width;
int h = img.Height;
//设置缩放模式
picbox.SizeMode = PictureBoxSizeMode.Zoom;
//显示图片
picbox.Image = img;
//或者加载图片
picbox.Load("c:\\example\\123.jpg");
# Button
- 去掉边框线:this.btnlogin.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
# 系统对话框
系统自带的一些对话框类:
- OpenFileDialog 打开文件对话框
- SaveFileDialog 保存文件对话框
- FolderBrowserDialog 目录选择对话框
- ColorDialog 颜色选择对话框
- FontDialog 字体选择对括框
# 普通对话框
//form1.cs
public Form1()
{
InitializeComponent();
// 初始化文本显示
string str = " 迷 离 \r\n"
+ "此 城 如 迷,\r\n"
+ "此 人 如 离。\r\n"
+ "此 生 如 戏,\r\n"
+ "此 世 如 棋。\r\n"
;
contentEdit.Text = str;
}
private void styleButton_Click(object sender, EventArgs e)
{
StyleDialog dlg = new StyleDialog();
if( dlg.ShowDialog() == DialogResult.OK)
{
string family = dlg.fontBox.SelectedItem.ToString();
int size =int.Parse(dlg.fontsizeBox.Value.ToString());
contentEdit.Font = new Font(family, (float)size);
}
dlg.Dispose();
}
}
//StyleDialog.cs
public partial class StyleDialog : Form
{
public StyleDialog()
{
InitializeComponent();
//下拉框初始值
this.fontBox.SelectedIndex = 0;
}
//Button类有一个 DialogResult属性,可用于直接设置DialogResult的值
//当按回车键时触发DialogResult.OK,当按ESC键时触发DialogResult.Cancel
//当点叉号关闭窗口时,相当于Cancel
private void okButton_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.OK;
}
private void cancelButton_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
}
}
DialogResult的作用
1、关闭对话框
2、设定返回值, 即 ShowDialog() 返回的值
# Listbox/Textbox/Richtextbox
Listbox:列表框,可以添加文本输出,只能以行输出,若想要一行数据自动换行,只能通过计算宽度来Add多次,文本不可以自动换行,但是每行可以添加颜色
//获取当前最底行,滚动到最底行
listbox.TopIndex = this.listbox.Items.Count - (int)(listbox.Height/listbox.ItemHeight);
//颜色变换
//先在前面窗体load中加入
listbox.DrawMode = DrawMode.OwnerDrawFixed;//控件中元素可以手动绘制
//在listbox的DrawItem事件中重绘当前行的字体颜色
void listbox_DrawItem(object sender, DrawItemEventArgs e)
{
e.DrawBackground();
Brush mybsh = Brushes.Black;
//如果当前行的内容中有“Error”,那么该行显示为红色
if(listbox.Items[e.Index].ToString().IndexOf("Error") != -1)
mybsh = Brushes.Red;
e.DrawFocusRectangle();
e.Graphics.DrawString(listbox.Items[e.Index].ToString(),
e.Font, mybsh, e.Bounds, StringFormat.GenericDefault);
}
Textbox:文本框,可以添加文本输出,文本支持自动换行,每行不可以添加颜色进行显示,换行符"\r\n"
textbox.SelectionStart = richtextbox.Text.Length - 1; //获取文本起始点 滚动最底行
textbox.ScrollTocaret();//滚动条滚动到当前插入符号位置
Richtextbox:Textbox的加强版,可以添加文本输出,文本支持自动换行,每行可以添加颜色显示,换行符"\n"。注意如果需要将该richtextbox的内容打到日志中,需要先将字符串中的换行符"\n"全部替换为"\r\n"
richtextbox.SelectionFont = new Font ("楷体", 12, FontStyle.Bold); //设置SelectionFont属性
richtextbox.SelectionColor = Color.Red;//设置文本起始点颜色
richtextbox.SelectionStart = richtextbox.Text.Length - 1; //获取文本起始点
richtextbox.ScrollToCaret();//滚动条滚动到当前插入符号位置
//添加文本
richtextbox.AppendText(Environment.NewLine+"["+DateTime.Now.ToString()+"]:"+message);
# 右键菜单 ContextMenuStrip
//给ListBox 添加鼠标事件 MouseUp
private void listBox1_MouseUp(object sender, MouseEventArgs e)
{
if(e.Button == MouseButtons.Right)
{
//根据鼠标点击的位置,判断点中了哪一项
int index = listBox1.IndexFromPoint(e.Location);
if(index>=0)
{
listBox1.SetSelected(index, true);
menuItem_Edit.Enabled = true;
menuItem_Del.Enabled = true;
}
else
{
listBox1.ClearSelected();
menuItem_Edit.Enabled = false;
menuItem_Del.Enabled = false;
// menuItem_Add.Visible = true;
}
this.contextMenuStrip1.Show(listBox1, e.Location);
}
}
# 列表控件 ListView
列表控件的几个特点:
- 显示模式可以切换
- 可以多字段显示
- 可以设置图标
- 标签可以编辑
- 每列可以单独排序
- 图片最大是256*256
提示
在批量添加数据时,为了避免界面频繁更新
listView1.BeginUpdate();
… 修改显示数据 …
listView1.EndUpdate();
//winform利用ImageList控件和ListView控件组合制作图片文件浏览器
//需要在窗体增加imageList和listview控件,并把ListView控件的LargeImageList设置为imageList1
//ListView控件显示图片的大小可以在imageList1控件中调整ImageSize属性
private void bindListView()
{
List<string> imageLists = new List<string>();
DirectoryInfo dir = new DirectoryInfo("E:\\bg\\");
string[] files = new string[100];
string ext = "";
foreach (FileInfo d in dir.GetFiles())
{
ext = System.IO.Path.GetExtension("E:\\bg\\" + d.Name);
if (ext == ".jpg" || ext == ".jpeg") //在此只显示Jpg
{
imageLists.Add("E:\\bg\\" + d.Name);
}
}
for (int i = 0; i < imageLists.Count; i++)
{
imageList1.Images.Add(System.Drawing.Image.FromFile(imageLists[i].ToString()));
listView1.Items.Add(System.IO.Path.GetFileName(imageLists[i].ToString()), i);
listView1.Items[i].ImageIndex = i;
listView1.Items[i].Name = imageLists[i].ToString();
}
}
# 表格视图 DataGridView
几个基本的属性:
- 列设定 [杂项] Columns
- 列标题是否可见 [外观] ColumnHeadersVisible
- 行标题是否可见 [外观] RowHeadersVisible
- 允许用户添加 [行为] AllowUserToAddRows
表格的基础操作
- 增加一行数据 grid.Rows.Add()
- 获取所有行的数据 grid[col, row] / grid.Rows[i].Cells[j]
- 选中行 grid.SelectedRows
- 删除一行 grid.Rows.RemoveAt(i) 删除选中的行 grid.Rows.Remove(row)
单元格的编辑
- 设置某列为不可编辑 grid.Columns[0].ReadOnly = true
- 编辑后的验证 grid_CellValidating事件(e.ColumnIndex第几列)
// 根据鼠标点击的位置,判断该位置所在的单位格的索引
public static Point GetCellAt(DataGridView grid, Point location)
{
int row = -1, col = -1;
// 一共显示的行数: DisplayedRowCount()
// 第一个显示的行: FirstDisplayedScrollingRowIndex
// 某行的显示区域: GetRowDisplayRectangle()
for (int i= grid.FirstDisplayedScrollingRowIndex;
i< grid.FirstDisplayedScrollingRowIndex + grid.DisplayedRowCount(true);
i++)
{
Rectangle rect = grid.GetRowDisplayRectangle(i, true);
if(location.Y > rect.Top && location.Y < rect.Bottom)
{
row = i;
break;
}
}
for (int k = grid.FirstDisplayedScrollingColumnIndex;
k < grid.FirstDisplayedScrollingColumnIndex + grid.DisplayedColumnCount(true);
k++)
{
Rectangle rect = grid.GetColumnDisplayRectangle(k, true);
if (location.X > rect.Left && location.X < rect.Right)
{
col = k;
break;
}
}
return new Point(row, col);
}
# Timer控件
Timer控件只有绑定了Tick事件,和设置Enabled=True后才会自动计时,停止计时可以用Stop()控制,通过Stop()停止之后,如果想重新计时,可以用Start()方法来启动计时器
Timer控件和它所在的Form属于同一个线程;timer1_Tick:是Timer对象的一个事件,表示在设定的时间间隔后自动触发的事件
private void Form1_Load(object sender, EventArgs e){
timer1.Interval = 1000;//毫秒为单位 默认的
}
private void timer1_Tick(object sender, EventArgs e){
//自动执行的代码
}
# Form窗体
- Size:窗口大小(含标题栏和边框)、ClientSize:仅窗口客户区的大小
- WindowState:窗体状态(放大、缩小)
- this.WindowState = FormWindowState.Maximized,会自动调用resize方法
- TransparencyKey:只支持透明或不透明
- this.BackColor =Color.White; this.TransparencyKey = Color.White;
- Opacity:窗体的透明度,Opacity制作的透明窗体盖在其他程序的窗口上,你看得到后面的窗口
# 关闭退出
this.Close() //关闭当前窗口
Application.Exit() //关闭所有窗口,退出应用程序
# 打开窗口的三种方式
- 从一个窗口打开另一个窗口
- 新窗口对象.show()
- 从一个窗口打开另一个对话框
- 新窗口对象.showDialog()
- 在一个窗口内部打开另一个窗口(MDI父窗体)
- 先设置父窗口属性IsMdiContainer 为True
- new 一个新窗口对象后,新窗口对象.MdiParent = this
- 新窗口对象.Show()
# FormClosing与FormClosed事件区别
- FormClosing事件是在关闭窗体时发生,用户可以在该事件中 取消关闭,窗体仍然保持打开状态。因此可以在该事件中提示一些状态信息,询问用户是否关闭窗口
- ormClosed事件 是在 关闭窗体后发生,可以在该事件中处理保存窗口的一些信息等操作,不能取消窗口关闭
private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
{
if (MessageBox.Show("确定要退出本系统吗?", "警告", MessageBoxButtons.OKCancel
, MessageBoxIcon.Warning) == DialogResult.OK)
{
this.Dispose();
Application.Exit();
}
else
{
e.Cancel = true; //不关闭窗口
//关闭窗口 e.Cancel = false;
}
}
# Form去掉默认的控件焦点
//注意不是load是shown
private void Form1_Shown(object sender, EventArgs e)
{
this.ActiveControl = null;
}
# 页面切换导致闪烁的解决方法
将此代码写在要解决闪烁问题的父窗体中
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000;
return cp;
}
}
# 无法接受Keydown事件
- 此时需要将窗体的 KeyPreview属性设置为true,将系统传入的键值先传递给窗体,再传递给控件。
- 覆盖默认的系统键处理方式,遇到方向键,则直接返回,系统不处理,这样键值就会被传递到窗体,触发KeyDown事件。
protected override bool ProcessDialogKey(Keys keyData){
if (keyData == Keys.Up || keyData == Keys.Down ||
keyData == Keys.Left || keyData== Keys.Right)
return false;
else
return base.ProcessDialogKey(keyData);
}
# 绝对路径的获取
//结果:D:\MyWork\MyProject\bin\Debug
string strpath=System.Windows.Forms.Application.StartupPath;
//结果: D:\MyWork\MyProject\bin\b.text
string _path=Path.GetFullPath(Path.Combine(strpath,"./Content/music.wav"));
# 嵌入的资源和资源文件
生成操作:嵌入的资源;复制到输出目录:不复制
- 获取“嵌入的资源”数据
//TestCustomForm为项目名称
//Res为项目下的文件夹
//btndown.bmp是文件名称。
Image.FromStream(Assembly.GetExecutingAssembly()
.GetManifestResourceStream(@"TestCustomForm.Res.btndown.bmp"));
- 获取项目中“资源文件”的数据
Assembly asm = Assembly.GetExecutingAssembly();
ResourceManager rm = new ResourceManager("TestCustomForm.Properties.Resources", asm);
String str = rm.GetString("ApplicationName"); //添加到资源文件的字符串
Image img = rm.GetObject("bamboo") as Image; //添加到资源文件的图片
//获取资源文件的图片到Panel
Properties.Resources res = new Properties.Resources();
PropertyInfo[] properInfos = res.GetType().GetProperties(BindingFlags.Static
| BindingFlags.NonPublic
| BindingFlags.Instance);
foreach (PropertyInfo item in properInfos.OrderBy(a => a.Name)) {
if (item.Name.StartsWith("lesson_1")) {
Panel panel = new Panel();
panel.Margin = new Padding(0);
panel.Width = 375;
panel.Height = 110;
PictureBox pic = new PictureBox();
pic.Image = (Image)TestCustomForm.Properties
.Resources
.ResourceManager
.GetObject(item.Name,
Properties.Resources.Culture);
pic.Width = 265;
pic.Height = 62;
pic.Location = new Point(55, 24);
panel.Controls.Add(pic);
panelMenu.Controls.Add(panel);
}
}
- 获取项目图片资源并转换为Image对象
System.Drawing.Image.FromFile(@"../../Resources/a.png")
# 手动添加控件
InitializeComponent();
//在它之后添加自己的初始化代码
Button testButton = new Button();
this.Controls.Add(testButton);
testButton.Location = new Point(40, 40);
testButton.Size = new Size(100, 40);
# 手工事件处理
//在Form1.cs里,添加一个回调方法
void OnTest(object sender, EventArgs e){}
//添加事件处理
testButton.Click += new EventHandler(this.OnTest);
事件委托
delegate void EventHandler(object sender, EventArgs e)
sender : 事件发送者,即点中的控件
e : 事件的参数,比如鼠标点击的位置
# 自定义控件的使用
- 工具|选项,Windows窗体设计器 | 常规 自动填充工具箱:设为True
- 添加自定义Panel或Control的类
- 生成解决方案 F7
- 重新打开Form1.cs,在工具箱界面可以看到自己的控件
# 自定义控件的方式
# 复合控件:将标准控件组合起来
class YourControl : UserControl{}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace FormApp0901
{
public partial class SearchBox : UserControl
{
public SearchBox()
{
InitializeComponent();
}
//自定义属性
[Browsable(true)]//是否显示在属性面板
[Category("Appearance")]//属性面板中显示的分类
//让设置器自动生成代码
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public override string Text
{
get
{
return edit.Text;
}
set
{
edit.Text = value;
}
}
// 添加自定义事件
public event EventHandler SearchEvent;
// 当点击按钮时,触发自定义事件
private void btn_Click(object sender, EventArgs e)
{
SearchEvent?.Invoke(this, e);
}
}
}
在Form中使用复合控件,最简单的办法:将各个子控件设为 public ,即可以直接访问
public Form1()
{
InitializeComponent();
searchBox1.edit.Text = "Java";
// 添加事件处理
searchBox1.btn.Click += new EventHandler(this.searchBox1_SearchEvent);
}
private void searchBox1_SearchEvent(object sender, EventArgs e)
{
MessageBox.Show("开始搜索..." + searchBox1.edit.Text);
}
# 扩展控件:继承于标准控件
class YourControl : Button {}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Af.Winform
{
class AfTextBox : UserControl
{
private TextBox edit;
private void InitializeComponent()
{
this.edit = new System.Windows.Forms.TextBox();
this.SuspendLayout();
//
// edit
//
this.edit.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.edit.Location = new System.Drawing.Point(62, 31);
this.edit.Name = "edit";
this.edit.Size = new System.Drawing.Size(100, 14);
this.edit.TabIndex = 0;
//
// AfTextBox
//
this.BackColor = System.Drawing.Color.White;
this.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.Controls.Add(this.edit);
this.Name = "AfTextBox";
this.Size = new System.Drawing.Size(242, 76);
this.ResumeLayout(false);
this.PerformLayout();
}
public AfTextBox()
{
InitializeComponent();
}
protected override void OnLayout(LayoutEventArgs e)
{
base.OnLayout(e);
// 获取子控件
if (this.Controls.Count == 0) return;
Control c = this.Controls[0];
// 父窗口参数
Padding p = this.Padding;
int x = 0, y = 0;
int w = this.Width, h = this.Height;
w -= (p.Left + p.Right);
x += p.Left;
// 计算文本框的高度,使其显示在中间
int h2 = c.PreferredSize.Height;
if (h2 > h) h2 = h;
y = (h - h2) / 2;
c.Location = new Point(x, y);
c.Size = new Size(w, h2);
}
// 添加一些属性
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public override string Text
{
get
{
return edit.Text;
}
set
{
edit.Text = value;
}
}
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public override Color BackColor
{
get
{
return edit.BackColor;
}
set
{
base.BackColor = value;
edit.BackColor = value;
}
}
// Font属性的设定:自己定义一个默认值
private Font font = new Font("宋体", 10f);
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public override Font Font
{
get
{
return font;
}
set
{
this.font = value;
this.edit.Font = value;
}
}
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public override Color ForeColor
{
get
{
return edit.ForeColor;
}
set
{
edit.ForeColor = value;
}
}
// 自定义事件: 回车
public event EventHandler ReturnPressed;
private void edit_KeyPress(object sender, KeyPressEventArgs e)
{
char ch = e.KeyChar;
if (ch == '\r')
{
ReturnPressed?.Invoke(this, e);
}
}
}
}
# 自定义控件:完全地自定义一个控件
class YourControl : Control {}
# 控件布局的方式
# 可视化布局 :在设计器里拖放操作
是在窗口初始化的时候,使用代码设置了每个控件的位置和大小
当窗口改变大小时,布局并不能够自动适应
button1.Location = new Point(375, 12);
button1.Size = new Size(75, 23);
# 手工布局 :用代码计算每个控件的位置
重写 OnLayout方法,当窗口大小改变时,会自动调用这个方法重新布局
protected override void OnLayout(LayoutEventArgs levent)
{
// 1 调用父类的OnLayout(),不是必需的
base.OnLayout(levent);
// 2 获取窗口大小 ClientSize (仅客户区,不含标题栏)
int w = this.ClientSize.Width;
int h = this.ClientSize.Height;
// 3 计算每一个控件的位置和大小
int yoff = 0;
yoff = 4;
this.textBox1.Location = new Point(0, yoff);
this.textBox1.Size = new Size(w - 80, 30);
this.button1.Location = new Point(w - 80, yoff);
this.button1.Size = new Size(80, 30);
yoff += 30; // 第一行的高度
yoff += 4; // 间隔
this.pictureBox1.Location = new Point(0, yoff);
this.pictureBox1.Size = new Size(w, h - yoff - 4);
}
# 使用布局器 :用布局器自动布局
Anchor:锚定,将控件固定于某个位置
Dock:停靠,将控件停靠在一侧或中央
当设置 Dock 属性时,Anchor属性无效
# 布局器
# 自定义布局器 LayoutEngine : 负责子控件的布局
默认地,一个Form 或 Panel 都自带了一个布局器
在窗口改变大小时,由窗口的布局器来负责调整布局
自定义一个Panel,并自己实现一个LayoutEngine
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.Layout;
namespace FormApp0601
{
// 自定义面板
class SimpleLayoutPanel : Panel
{
// 布局器
private LayoutEngine layoutEngine = new SimpleLayoutEngine();
public override LayoutEngine LayoutEngine
{
get
{
return layoutEngine;
}
}
}
// 自定义布局器
class SimpleLayoutEngine : LayoutEngine
{
public override bool Layout(object container,LayoutEventArgs layoutEventArgs)
{
// 容器
SimpleLayoutPanel parent = (SimpleLayoutPanel)container;
int w = parent.Width;
int h = parent.Height;
// 去除Padding
int x = parent.Padding.Left;
int y = parent.Padding.Top;
w -= (parent.Padding.Left + parent.Padding.Right);
h -= (parent.Padding.Top + parent.Padding.Bottom);
int gap = 2;
foreach (Control c in parent.Controls)
{
c.Location = new Point(x, y);
c.Size = new Size(w, c.PreferredSize.Height);
y += c.Size.Height;
y += gap;
}
return false;
}
}
}
# FlowLayoutPanel :流式布局
子控件依次排列,一行排满之后换行继续排
# TableLayoutPanel:表格布局
以表格的形式进行布局。列的宽度:
- 绝对值:固定大小
- 百分比:占据剩余空间的百分比
- 自动大小:根据所需的空间自动分配
# 线程控制
# 控件的InvokeRequired方法
C#为控件单独开辟了一个线程,当另外一个线程的方法需要修改控件或者调用控件的方法时,需要通过控件的InvokeRequired方法来进行。
比如,当另一个线程想调用控件的方法时:
//定义委托
private delegate void SendCallBack(List<byte[]> bufferList,bool feedback);
//Send方法属于某个窗口
private void Send(List<byte[]> bufferList,bool feedback) {
if (this.InvokeRequired) { //跨线程调用时的执行逻辑
try {
SendCallBack cb = new SendCallBack(Send);
this.Invoke(cb,bufferList,feedback);
} catch(Exception ex) {
MessageBox.Show(ex.Message);
}
}
else
{
//不是跨线程调用此方法时的执行逻辑
}
}
private void serialPort1_DataReceived(object sender,SerialDataReceivedEventArgs e) {
Send(bufferlist,true);
}
控件的InvokeRequired属性:bool值,为true时表示调用Send方法的是另一个线程,此时需要将Send方法传送给一个委托让委托所在的线程来代替执行Send方法;为false时表示Send的调用者没有跨线程调用Send方法,此时直接执行else中的代码即可。
串口的DataReceived事件和Send方法所属的窗口不在同一个线程,因此在serialPort1_DataReceived事件中调用Send方法时就会执行Send方法中if块中的代码。
# this.Invoke()中委托的定义
在主线程中开了一个子线程,如果要在子线程中修改主线程某个控件,会触发异常:“线程间操作无效: 从不是创建控件“button1”的线程访问它”。原来从.net framework 2.0开始,为了安全,不允许跨线程操作控件。
- 正确的写法是需要使用Invoke,Invoke方法需要创建一个委托。如下,要修改一个Button控件的文字:
Thread testThread1=new Thread(new ThreadStart(process1));//主函数中创建一个子线程
testThread1.IsBackground = true;
testThread1.Start();
void process1()
{ //使用委托
this.Invoke(new EventHandler(delegate
{
button1.Text = "测试";
}));
}
- 使用Lamda表达式,在.Net Framework3.5及以后的版本中使用Action封装方法,代码如下:
void process1()
{
this.Invoke(new Action(()=>
{
button1.Text = "测试";
}));
}
# this.Invoke和this.BeginInvoke的区别
- Invoke会阻止当前主线程的运行;BeginInvoke不会阻止当前主线程的运行,而是等当前主线程做完事情之后再执行BeginInvoke中的代码内容
- 这2个方法都是由主线程运行的,并不是异步执行,如果代码耗时过长,同样会造成界面卡死
private void button1_Click(object sender, EventArgs e)
{
this.textBox1.Text = "1";
this.Invoke(new EventHandler(delegate{
this.textBox1.Text += "2";
}));
this.textBox1.Text += "3";
}
//输出:123
private void button1_Click(object sender, EventArgs e)
{
this.textBox1.Text = "1";
this.BeginInvoke(new EventHandler(delegate{
this.textBox1.Text += "2";
}));
this.textBox1.Text += "3";
}
//输出:132
# 播放音频文件
微软提供了三种方式来播放音频文件
- 通过System.Media.SoundPlayer来播放
- 通过Com组件,添加axWindowsMediaPlayer控件播放。
- 通过ApI函数mcisendstring播放
这三种方法都没法实现amr文件的播放,可以通过第三方插件(libvlc播放器是个不错的选择)的方式实现
# 通过System.Media.SoundPlayer
- 首先,在Com类型库中添加Windows Media Player组件的引用
- 添加的引用之后,就可以直接使用了
private void button2_Click(object sender, EventArgs e)
{
string path=@"D:/test.wav";
System.Media.SoundPlayer player = new System.Media.SoundPlayer();
player.SoundLocation = path;
//或者加载资源中的文件
//player.Stream = Properties.Resources.musicbg;.
player.LoadAsync(); //异步加载
//player.PlayLooping(); //循环播放
player.PlaySync();
}
注意
- 只支持wav格式的文件,其余的格式文件都不支持
- SoundPlayer非常有限,不支持暂停和恢复
# 通过Com组件,添加axWindowsMediaPlayer控件播放
- 首先,需要在工具箱中添加Com组件,具体步骤,找到工具箱,在工具箱的空白位置,右键,选择“选择项”,点击“Com”组件,选择 Windows Media Player
- 选择之后,在工具箱中就会显示Windows Media Player
- 将“Windows Media Player”控件拖到Form表单中,就显示了Windows Media Player播放器,如果不想显示可以使用visible属性隐藏
private void button1_Click(object sender, EventArgs e)
{
axWindowsMediaPlayer1.URL = @"D:/Test.mp3";
axWindowsMediaPlayer1.settings.autoStart = true;
this.axWindowsMediaPlayer1.settings.playCount = 2;//播放次数;
if (this.axWindowsMediaPlayer1.Ctlcontrols.currentPosition > 0)
{
this.axWindowsMediaPlayer1.Ctlcontrols.pause();//暂停播放文件
}
else {
this.axWindowsMediaPlayer1.Ctlcontrols.play();//播放文件
}
}
注意
- 这种方式支持的文件格式比较多,主要包括:mp3、wav、mp4,wmv格式
- url参数可以直接访问服务器端的文件,本地文件时必须是绝对路径
# 通过ApI函数mcisendstring播放
//封装Play类
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.IO;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
class AudioPlay
{
//定义API函数使用的字符串变量
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
private string Name = "";
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
private string durLength = "";
[MarshalAs(UnmanagedType.LPTStr, SizeConst = 128)]
private string TemStr = "";
int ilong;
//定义播放状态枚举变量
public enum State
{
mPlaying = 1,
mPuase = 2,
mStop = 3
};
//结构变量
public struct structMCI
{
public bool bMut;
public int iDur;
public int iPos;
public int iVol;
public int iBal;
public string iName;
public State state;
};
public structMCI mc = new structMCI();
//取得播放文件属性
public string FileName
{
get
{
return mc.iName;
}
set
{
//ASCIIEncoding asc = new ASCIIEncoding();
try
{
TemStr = "";
TemStr = TemStr.PadLeft(127, Convert.ToChar(" "));
Name = Name.PadLeft(260, Convert.ToChar(" "));
mc.iName = value;
ilong = APIClass.GetShortPathName(mc.iName, Name, Name.Length);
Name = GetCurrPath(Name);
Name = "open " + Convert.ToChar(34) + Name
+ Convert.ToChar(34) + " alias media";
ilong = APIClass.mciSendString("close all", TemStr, TemStr.Length, 0);
ilong = APIClass.mciSendString(Name, TemStr, TemStr.Length, 0);
ilong = APIClass.mciSendString("set media time format milliseconds",
TemStr, TemStr.Length, 0);
mc.state = State.mStop;
}
catch
{
}
}
}
//播放
public void play()
{
TemStr = "";
TemStr = TemStr.PadLeft(127, Convert.ToChar(" "));
APIClass.mciSendString("play media", TemStr, TemStr.Length, 0);
mc.state = State.mPlaying;
}
//停止
public void StopT()
{
TemStr = "";
TemStr = TemStr.PadLeft(128, Convert.ToChar(" "));
ilong = APIClass.mciSendString("close media", TemStr, 128, 0);
ilong = APIClass.mciSendString("close all", TemStr, 128, 0);
mc.state = State.mStop;
}
public void Puase()
{
TemStr = "";
TemStr = TemStr.PadLeft(128, Convert.ToChar(" "));
ilong = APIClass.mciSendString("pause media", TemStr, TemStr.Length, 0);
mc.state = State.mPuase;
}
private string GetCurrPath(string name)
{
if (name.Length < 1) return "";
name = name.Trim();
name = name.Substring(0, name.Length - 1);
return name;
}
//总时间
public int Duration
{
get
{
durLength = "";
durLength = durLength.PadLeft(128, Convert.ToChar(" "));
APIClass.mciSendString("status media length", durLength,
durLength.Length, 0);
durLength = durLength.Trim();
if (durLength == "") return 0;
return (int)(Convert.ToDouble(durLength) / 1000f);
}
}
//当前时间
public int CurrentPosition
{
get
{
durLength = "";
durLength = durLength.PadLeft(128, Convert.ToChar(" "));
APIClass.mciSendString("status media position", durLength,
durLength.Length, 0);
mc.iPos = (int)(Convert.ToDouble(durLength) / 1000f);
return mc.iPos;
}
}
}
public class APIClass
{
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern int GetShortPathName(
string lpszLongPath,
string shortFile,
int cchBuffer
);
[DllImport("winmm.dll", EntryPoint = "mciSendString", CharSet = CharSet.Auto)]
public static extern int mciSendString(
string lpstrCommand,
string lpstrReturnString,
int uReturnLength,
int hwndCallback
);
}
}
//调用也非常简单
private void button3_Click(object sender, EventArgs e)
{
AudioPlayer cm = new AudioPlayer ();
cm.FileName =@"D:/mp3222.mp3";
cm.play();
}
注意
- 这种方式支持mp3格式,其余的格式没有测试
- 在AudioPlayer类中,通过APIClass.GetShortPathName(mc.iName, Name, Name.Length)方法根据长路径获得短路径,但这种方法不能获得服务器的短路径,所以,目前不知道根据这种方法播放服务器端的文件。但是可以通过将服务器端的文件加载的本地,在通知这种方式来播放本地文件
# 基于VLC的视频播放
- 安装Vlc.DotNet.Forms
- 下载VLC播放器安装包 (opens new window) 获取组件plugin、libvlc.dll、libvlccore.dll
- VLC类库安装成功后,即可使用封装好的VlcControl控件,在工具箱中可以看到此控件,可以直接拖拽到界面使用
- 必须设置VLC类库目录到VlcLibDirectory,这里设置为程序所在目录下的“vlclib”文件夹
private void vlcControl1_VlcLibDirectoryNeeded(object sender, Vlc.DotNet.Forms.VlcLibDirectoryNeededEventArgs e)
{
var currentAssembly = Assembly.GetEntryAssembly();
var currentDirectory = new FileInfo(currentAssembly.Location).DirectoryName;
// Default installation path of VideoLAN.LibVLC.Windows
e.VlcLibDirectory = new DirectoryInfo(Path.Combine(currentDirectory, "libvlc", IntPtr.Size == 4 ? "win-x86" : "win-x64"));
if (!e.VlcLibDirectory.Exists)
{
var folderBrowserDialog = new FolderBrowserDialog();
folderBrowserDialog.Description = "Select Vlc libraries folder.";
folderBrowserDialog.RootFolder = Environment.SpecialFolder.Desktop;
folderBrowserDialog.ShowNewFolderButton = true;
if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
{
e.VlcLibDirectory = new DirectoryInfo(folderBrowserDialog.SelectedPath);
}
}
}
- 常用方法
// 播放本地文件
vlcControl.SetMedia(new Uri(videoFilePath));
vlcControl.Play();
//重复播放
var vlcOption = new string[] { "input-repeat=1000"};
vlcVideo.Play(fileStream, vlcOption);
// 暂停
vlcControl.Pause();
// 停止
vlcControl.Stop();
// 截图保存到指定文件
vlcControl.TakeSnapshot(imageSavePath);
// 设置进度,position为进度百分比,等于1时会停止播放
vlcControl.Position = position;
//vlcControl默认屏蔽了鼠标事件响应(当视频在播放时,vlcControl的鼠标事件无效),我们可以设置
vlcControl1.Video.IsMouseInputEnabled = false;
//设置全屏核心代码 去除黑边
this.vlcVideo.Video.AspectRatio = this.vlcVideo.Width + ":" + this.vlcVideo.Height;
常用方法 →