C#图形之与matlab混编思路

参考文章:
1、https://www.cnblogs.com/asxinyu/archive/2013/04/14/3020813.html
2、https://blog.csdn.net/yxy244/article/details/79305757

一、实现原理:

需要用到Windows API来设置窗体,用FindWindow查找图像窗体Figure1的句柄,使用SetParent设置Figure1父窗体为Winform的控件Panel,这样就把figure放进Winform里了,之所以放到panel控件里,而不是作为子窗体在winform里,是为了把它当做Winform的一个控件,便于布局。再使用MoveWindow移动到合适的位置,SetWindowLong去掉标题,不能通过边框改变大小,在Winform窗体SizeChanged事件里用MoveWindow改变Figure的大小,就能使Figure的尺寸和窗体同步改变。

二、关键代码:

1.使用到的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);
        [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);
        [DllImport("user32")]
        private static extern int InvalidateRect(System.IntPtr hwnd, object rect, bool bErase);
        /// <summary>最大化窗口,最小化窗口,正常大小窗口
        /// nCmdShow:0隐藏,3最大化,6最小化,5正常显示
        /// </summary>
        [DllImport("user32.dll", EntryPoint = "ShowWindow")]
        public static extern int ShowWindow(IntPtr hwnd, int nCmdShow);
        #endregion

2.定义全局变量,窗体载入事件

       public delegate void UpdateUI();//委托用于更新UI
        Thread startload;//线程用于matlab窗体处理
        MatlabFunction matlabFunction;//matlab编译的类
        IntPtr figure1;//图像句柄
        private void Form1_Load(object sender, EventArgs e)
        {
            //实例化线程,用来初次调用matlab,并把图像窗体放到winform
            startload = new Thread(new ThreadStart(startload_run));
            //开始线程
            startload.Start();
        }

3.线程执行的方法,每隔50ms查找一下figure窗体,找到嵌入到winform的panel控件里。

        void startload_run()
        {
            int count50ms = 0;
            //实例化matlab对象
            matlabFunction = new MatlabFunction();
            //调用方法画高斯分布函数图
            matlabFunction.mysurf();//高斯分布函数
 
            //循环查找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, panel1.Width + 20, panel1.Height + 40, true);
                //调用显示窗体函数,隐藏再显示相当于刷新一下窗体
                //radiobutton按钮使能
                radioButton1.Enabled = true;
                radioButton2.Enabled = true;
                radioButton3.Enabled = true;
                radioButton4.Enabled = true;
                radioButton5.Enabled = true;
                radioButton6.Enabled = true;
               
            };
            panel1.Invoke(update);
            //再移动一次,防止显示错误
            Thread.Sleep(100);
            MoveWindow(figure1, 0, 0, panel1.Width + 20, panel1.Height + 40, true);
        }

三、注意事项

1.代码中的MatlabFunction 类是我自己封装的一些matlab演示函数,关于C#调用matlab这方面的网上很多,这里不作介绍。
2.工程使用vs2017和matlab2010b编译,程序运行需要 .net4.0和matlab2010或者matlab运行环境MCRInstaller。

以上原文链接:https://blog.csdn.net/yxy244/article/details/79305757

四、 Matlab.NET混合编程思路

这方面就不仔细阐述了,主要是利用Matlab的Deploytool工具将m函数编译为.NET程序集,然后在C#中调用的过程。然后程序可以在安装MCR的机器上运行,这个过程如果懂Matalb和C#的人,其实看看帮助很容易理解,也有例子。在这里不仔细讲解。2012年,我录制过国内第一套Matlab.NET混合编程视频教程,与ILoveMatab论坛的管理员Math商量之后,已经免费开放,大家去论坛下载即可。
混合编程最重要的是利用matlab的科学计算功能和强大的工具箱函数。但目前很多学生做这个,都只是为了简单的绘图,.NET绘图其实也很强大。很多人混编,就想把Matlab绘图产生的Figure在WinForm窗体中显示,但是Matlab并没有提供直接的解决方法。只能找另外的方法,我目前想到的有2种:
1.先把Figure保存为图片,然后WinForm窗体去读取图片;
2.利用Windows API 技术,动态的获取窗体句柄,然后嵌入到WinForm中。
第一种方法很容易想到,也很容易解决,就不仔细讲了。
第二中方法懂开发的人一般能想得到,但是学生一般不是很容易想到,即使想到,做出来也很难。
下面我就把第二种方法的实现过程讲解一下。

五、MatlabFigure控件的封装过程

控件的设计思想:
1.能够在WinForm桌面程序调用Matlab混编产生的Figure,主要是根据Figure的标题文字
2.Figure嵌入在控件C中,C的大小应该和Figure的大小一致,程序只需要关心控件的大小状态就可以
3.初始化的时候,Figure的大小根据控件C的大小进行调整
4.Figure的大小将随着控件的大小改变而改变
其实根据上面阐述的原理,精通Windows API的人很快就可以解决,主要就这么几个函数,我贴一下代码:

DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

[DllImport("user32.dll")]
public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);

[DllImport("user32.dll")]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
    public int left;
    public int top;
    public int right;
    public int bottom;
}

[DllImport("user32.dll")]
public static extern int GetClientRect(IntPtr hwnd, ref RECT rc);

[DllImport("user32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);

/// <summary>最大化窗口,最小化窗口,正常大小窗口
/// nCmdShow:0隐藏,3最大化,6最小化,5正常显示
/// </summary>
[DllImport("user32.dll", EntryPoint = "ShowWindow")]
public static extern int ShowWindow(IntPtr hwnd, int nCmdShow);

[DllImport("user32.dll")]
public static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);


public class _SW
{
    public const int SW_HIDE = 0;
    public const int SW_SHOWNORMAL = 1;
    public const int SW_SHOWMINIMIZED = 2;
    public const int SW_SHOWMAXIMIZED = 3;
    public const int SW_MAXIMIZE = 3;
    public const int SW_SHOWNOACTIVATE = 4;
    public const int SW_SHOW = 5;
    public const int SW_MINIMIZE = 6;
    public const int SW_SHOWMINNOACTIVE = 7;
    public const int SW_SHOWNA = 8;
    public const int SW_RESTORE = 9;
}

在Figure窗体显示后,通过FindWindow(“SunAwtFrame”, figureTitleName);来动态获取窗体的句柄,注意Figure的类型都是”SunAwtFrame”,这个要专业点的软件查出来。获取窗体之后,然后通过 SetParent和MoveWindow来设置子窗体,并改变窗体的大小。控件的详细代码就不贴了,主要是上面的封装过程吧,把逻辑搞懂了,很容易。由于源代码是给商业项目使用,暂时不开放。贴一段窗体寻找的代码,为了防止Figure还未显示,程序已经在寻找,特意加了一个延时:

#region 寻找窗体
int num = 0 ;
while (num <5 )
{
    num++;
    //若找不到窗体,循环5次,每次100ms
    if (wf == IntPtr.Zero)
    {
        wf = FindWindow("SunAwtFrame", figureTitleName);                    
        Thread.Sleep(timeSpan);
    }
    else
    {   //找到隐藏起来
        ShowWindow(wf, _SW.SW_HIDE);break;
    }
}           
if (wf == IntPtr.Zero)
{
    MessageBox.Show("无法获取Figure窗体,请确认信息是否正确");
    return;
}
#endregion

看看效果图:
在这里插入图片描述
特

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值