zoukankan      html  css  js  c++  java
  • 将多个外部窗体移动到多个Winform Panel中时上方窗体位置异常

    问题描述

    用matlab生成了多个图,每个图一个窗口,想要将这些图嵌入到我的程序中;
    我的程序是wpf的程序,使用了windowsFormsHost内部嵌套了一个Panel来装图像窗口。
    采用了边生成图像,边找图像窗口,边嵌套进Panel的方法,假定一共有四个图像窗口,存在如下异常情况:

    1. 刚找到第一个图像窗口并移动到Panel中的时候位置是正确的,但是生成第二个图像的时候,第一个窗口的位置总是发生了向下的位移;
    2. 有时会出现生成下一个窗口时,上一个窗口发生向下位移的情况(从第三个窗口生成计);
    3. 有时会出现生成下一个窗口时,第一个窗口继续发生向下位移的情况(从第三个窗口生成计);
    4. 有时会出现在生成第四个窗口时,第三个窗口消失了的情况。

    放张图吧:

    红色的部分是Panel所在的位置,可以看到第一个图的位置明显向下移动了。
    多次测试发现,每个图会发生什么样的位移变化,是一件看上去随机的事件,
    之前有出现过如果在图片窗口全都生成之后依次将窗口移入到Panel中时,不出问题的情况,然而多次测试之后,发现又位移了,
    好像有点玄学。

    测试代码

    添加Panel:

    for (int i = 1; i < 5; i++)
    {
        WindowsFormsHost wfh = new WindowsFormsHost();
        wfh.Child = new System.Windows.Forms.Control();
        wfh.Width = 350;
        wfh.Height = 350;
        wfh.VerticalAlignment = VerticalAlignment.Top;
        wfh.HorizontalAlignment = HorizontalAlignment.Left;
        wfh.Margin = new Thickness(0, (i - 1) * 400, 0, 0);
        wfh.Background = Brushes.Blue;
        System.Windows.Forms.Panel p = new System.Windows.Forms.Panel();
        p.Name = string.Format("{0}", i);
        p.BackColor = System.Drawing.Color.Red;
        p.Width = (int)wfh.Width;
        p.Height = (int)wfh.Height;
        p.Location = new System.Drawing.Point(0, 0);
        wfh.Child.Controls.Add(p);
        testGrid.Children.Add(wfh);
        ps.Add(p);
    }
    Thread t = new Thread(WorkThread);
    t.IsBackground = true;
    t.Start();
    

    生成并移动窗口:
    这样写是会出现上面的问题的

    private void WorkThread()
    {
        for (int i = 1; i < 5; i++)
        {
            // d1是matlab导出的dll,用来生成一张图
            d1.Class1 class1 = new Class1();
            class1.d1(string.Format("{0}", i));
            IntPtr figure = IntPtr.Zero;
            while (figure == IntPtr.Zero)
            {
                figure = FindWindow("SunAwtFrame", string.Format("{0}", i));
                if (figure != IntPtr.Zero)
                {
                    new Thread(() => {
                        Dispatcher.Invoke(() =>
                        {
                            SetParent(figure, ps[i - 1].Handle);
                        });
                    }).Start();
                    var style = GetWindowLong(figure, GWL_STYLE);
                    // 设置窗体风格
                    SetWindowLong(figure, GWL_STYLE, style & ~WS_CAPTION & ~WS_THICKFRAME);
                    MoveWindow(figure, 0, 0, ps[i - 1].Width, ps[i - 1].Height, true);
                }
                Thread.Sleep(1300);
            }
        }
    }
    

    解决方案

    将窗口全部生成出来之后,再倒序移动到Panel中,并且移动要稍有一定的时间检测,经测试,100ms能够达到要求,
    如果不留有时间检测,会出现最后一个窗口失踪的问题。
    这种方案我做过多次测试,包括在我的程序界面移动的情况下,或者滚动条滚动的情况下,都有稳定的表现效果,
    这是我希望的模样:

    修正代码:

    private void WorkThread()
    {
        for (int i = 1; i < 5; i++)
        {
            d1.Class1 class1 = new Class1();
            class1.d1(string.Format("{0}", i));
        }
    
        for(int i = 4; i > 0; i --)
        { 
            IntPtr figure = IntPtr.Zero;
            while (figure == IntPtr.Zero)
            {
                figure = FindWindow("SunAwtFrame", string.Format("{0}", i));
                if (figure != IntPtr.Zero)
                {
                    new Thread(() => {
                        Dispatcher.Invoke(() =>
                        {
                            SetParent(figure, ps[i - 1].Handle);
                        });
                    }).Start();
                    var style = GetWindowLong(figure, GWL_STYLE);
                    // 设置窗体风格
                    SetWindowLong(figure, GWL_STYLE, style & ~WS_CAPTION & ~WS_THICKFRAME);
                    MoveWindow(figure, 0, 0, ps[i - 1].Width, ps[i - 1].Height, true);
                }
                Thread.Sleep(100);
            }
        }
    }
    

    windowsAPI:

    [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);
    
    private const int GWL_STYLE = -16;
    private const int WS_CAPTION = 0x00C00000;
    private const int WS_THICKFRAME = 0x00040000;
    private 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);
    

    我做了这么多努力,要把这个功能做出来的目的,还是希望能够调用matlab画三维坐标图,
    如果有其他可行的在wpf中画三维坐标点的方法,我一定立即抛弃这个方案。
    看上去viewport3D,OpenGl还有Direct X实在是有点学习成本了,
    毕竟我只是想画个三维坐标系,搞这些3D的东西,我还得考虑缩放,移动,旋转,刷新的时候是不是会卡顿,不单单只是显示出来的问题,
    matlab画图可真是太漂亮了。
    这个方案还有个很大的弊端,就是wpf中使用winform控件,必然会导致winform控件置于顶层,这其实是个很大的麻烦。

    嵌套窗口的这部分代码来自网络

  • 相关阅读:
    盲山有感
    一个用Regex的完成sql语句中字段替换的demo
    月夜奔跑
    乱弹
    《勇敢抉择》摘录一
    梦想高歌
    从今天起
    php性能优化
    unity3d shader中RenderType的所有类型
    Unity打包ipa图文教程
  • 原文地址:https://www.cnblogs.com/yutou2016/p/14792848.html
Copyright © 2011-2022 走看看