C#调⽤matlab,matlab图形Figure嵌⼊Winform窗体,并完美
解决只能捕。。。
本⽂禁⽌转载,需提前获得允许,侵权必究
(本⽂实现的前提,你要安装好matlab。本⽂⽤的是vs2015+matlab2017b)
1.⾸先第⼀步建⽴⼀个⼯程⽂件
2.添加Matlab Application(Version 9.3)Type libaray
在References这⾥右键点击——Add References,然后会弹出如图所⽰的对话框,按照图⽚所⽰的 顺序操作,(1) 选择
COM(2)输⼊matlab进⾏搜索(3) 选择(注意这⾥要和你的matlab版本对应,我是matlab2017b,对应的是9.3)
添加引⽤后,引⽤(References)⽬录下多了⼀个MLApp⽂件,如果在编程是采⽤MLAppClass的话还需要将该⽂件的属性Embed Interop Types的True值改为False,不然会报错。
如果采⽤MLApp.MLApp则不会报错。
3.添加Properties
⽹上有很多调⽤matlab的⽅法,⽐如⽣成DLL⽂件,然后引⽤,在我这⾥⽼是 出现“类型初始值设定项引发异常”的错误,⽽且安装⽹上的⽅法也改不好,因此摸索出了另外⼀个⽅法。当然这个⽅法的前提是:你的电脑要事先安装好MATLAB。
第⼀步,就是在你的项⽬下⾯新建⼀个⽂本⽂件,改名为⼀个你想的名字,如“”,然后把你需要的matlab代码复制进去,如图:
第⼆步,在你的⼯程名那⾥,右键选择“Properties”,然后选择Resources——Add Existing File,弹出的对话框之后,选择刚刚你添加的txt⽂件。
这样,在你的资源⽂件夹下⾯会多出来⼀个的⽂件
这样做的⽬的是:在我们需要调⽤matlab代码的时候,我们⽤这个资源⽂件去⽣成⼀个.m⽂件,然后再执⾏这个.m⽂件,结束了再删除.m⽂件。为什么要这样做?因为如果直接在⼯程下⾯粘贴⼀个.m⽂件,使⽤VS⾃带的打包⼯具打包发布的时候, 这个⽂件是不会⼀起打包的,就造成了发布后调⽤不成功。当然,你可以使⽤其他的打包的⼯具。⽐如【Inno setup】。
好了, 本⽂的⽅法,⾸先要⽣成⼀个.m⽂件,很简单了,直接贴代码:
FileStream fs = new FileStream("ttt.m", FileMode.OpenOrCreate, FileAccess.ReadWrite);
StreamWriter sw = new StreamWriter(fs); // 创建写⼊流
sw.WriteLine(); // 写⼊Hello World
sw.Close(); //关闭⽂件
fs = null;
4.这⾥是关键的⼀步,调⽤matlab,
MLApp.MLApp matlab = null;
Type matlabAppType = System.Type.GetTypeFromProgID("Matlab.Application");
matlab = System.Activator.CreateInstance(matlabAppType) as MLApp.MLApp;
string path_project = Directory.GetCurrentDirectory(); //⼯程⽂件的路径,如bin下⾯的debug
string path_matlab = "cd('" + path_project + "')"; //⾃定义matlab⼯作路径
matlab.Execute(path_matlab);
matlab.Execute("clear all");
matlab.Execute("close all");
string command;
command = @"ttt(10,4)";
matlab.Execute(command);
这⾥⾯⽐较关键的⼏句:第4、5句,⽬的是让matlab的⼯作路径在当前路径,不然可能你这次调⽤和
下次调⽤的路径不同,会导致错误。clear all是为了清除⼯作区,不然可能上次的结果会对下次造成影响。
5.这⾥的是画了⼀个图,第5步是将弹出的Figure对话框嵌⼊到窗体内,也很关键
⾸先在窗体内定义使⽤的windows API
#region //Windows API
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);//
[DllImport("user32.dll")]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);writeline方法属于类
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int MoveWindow(IntPtr hWnd, int x, int y, int nWidth, int nHeight, bool BRePaint);
const int GWL_STYLE = -16;
const int WS_CAPTION = 0x00C00000;
const int WS_THICKFRAME = 0x00040000;
const int WS_SYSMENU = 0X00080000;
[DllImport("user32")]
private static extern int GetWindowLong(System.IntPtr hwnd, int nIndex);
[DllImport("user32")]
private static extern int SetWindowLong(System.IntPtr hwnd, int index, int newLong);
下⼀步,定义全局变量,在触发事件内实例化线程,我这⾥因为是点击按钮,所以后⾯的实例化写在了private void
button1_Click(object sender, EventArgs e)⾥⾯
//定义在窗体内
public delegate void UpdateUI();//委托⽤于更新UI
Thread startload;//线程⽤于matlab窗体处理
IntPtr figure1;//图像句柄
//写在触发事件内,如Form_Load,这⾥我写在了Button_Click内
//实例化线程,⽤来初次调⽤matlab,并把图像窗体放到winform
startload = new Thread(new ThreadStart(startload_run));
//运⾏线程⽅法
startload.Start();
线程⾥⾯的startload_run⽅法:
void startload_run()
{
int count50ms = 0;
/
/实例化matlab对象
//循环查figure1窗体
while (figure1 == IntPtr.Zero)
{
//查matlab的Figure 1窗体
figure1 = FindWindow("SunAwtFrame", "Figure 1");
//延时50ms
Thread.Sleep(50);
count50ms++;
//20s超时设置
if (count50ms >= 400)
{
label1.Text = "matlab资源加载时间过长!";
return;
}
}
//跨线程,⽤委托⽅式执⾏
UpdateUI update = delegate
{
//隐藏标签
label1.Visible = false;
//设置matlab图像窗体的⽗窗体为panel
SetParent(figure1, panel1.Handle);
//获取窗体原来的风格
var style = GetWindowLong(figure1, GWL_STYLE);
//设置新风格,去掉标题,不能通过边框改变尺⼨
SetWindowLong(figure1, GWL_STYLE, style & ~WS_CAPTION & ~WS_THICKFRAME);
//移动到panel⾥合适的位置并重绘
MoveWindow(figure1, 0, 0, 400 + 0, 300 + 0, true);
};
panel1.Invoke(update);
//再移动⼀次,防⽌显⽰错误
Thread.Sleep(100);
MoveWindow(figure1, 0, 0, 400 + 0, 300 + 0, true);
}
这⾥⽐较关键的是,MoveWindow(figure1, 0, 0, 400 + 0, 300 + 0, true)这⼀句,⾥⾯的参数,第四、第五个代表的是在panel⾥⾯的位置,很多的博客⾥⾯写的是MoveWindow(figure1, 0, 0, panel1.Width + 20, panel1.Height + 40, true),这样⽐较坑的是,万⼀你的图⽚⽐较⼤,⽽panel⽐较⼩,会使得图⽚显⽰不全,所以,要改成你图⽚实际的⼤⼩就好了。
6.解决只能捕捉⼀次窗体的问题
上述的⽅法,只能实现在第⼀次弹出Figure的时候能够捕捉嵌⼊,当再次运⾏的时候就会弹出来。我摸索了很久,⼤概3个⼩时候发现了解决⽅案,很简单。在你触发线程实例化之前加上这句话:
figure1 = IntPtr.Zero;
这样就可以把之前的figure1给释放了,
就能再次捕捉了。哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈,开森。
7.执⾏完删除.m⽂件
定义删除的函数:
public void DeleteFile(string path)
{
try
{
FileAttributes attr = File.GetAttributes(path);
if (attr == FileAttributes.Directory)
{
Directory.Delete(path, true);
}
else
{
File.Delete(path);
}
}
catch
{
FileNotFoundException ex;
}
}
然后在结束的时候,⽐如Form_Closing的⽅法⾥⾯写上:DeleteFile("ttt.m");即可
全部项⽬代码下载链接:
好了,全⽂结束。贴上参考⽂献,感谢以下的博客及作者:
【1】
【2】
【3】
本⽂禁⽌转载,需提前获得允许,侵权必究
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论