none
使用Windows API模拟WPF MDI时子窗口花屏问题 RRS feed

  • 问题

  • 为了在WPF中实现MDI窗口,使用了最简单的实现方式
    using System;
    using System.Runtime.InteropServices;
    
    namespace WPFMdi {
    	public class Win32Native {
    		[DllImport("user32.dll", EntryPoint = "SetParent")]
    		public extern static IntPtr SetParent(IntPtr childPtr, IntPtr parentPtr);
    	}
    }
    
    using System.Windows;
    using System.Windows.Interop;
    
    namespace WPFMdi {
    	/// <summary>
    	/// MainWindow.xaml 的交互逻辑
    	/// </summary>
    	public partial class MainWindow : Window {
    		public MainWindow() {
    			InitializeComponent();
    		}
    
    		private void button1_Click(object sender, RoutedEventArgs e) {
    			ChildWindow win = new ChildWindow();
    			win.Show();
    
    			//创建MDI窗体
    			WindowInteropHelper parentHelper = new WindowInteropHelper(this);
    			WindowInteropHelper childHelper = new WindowInteropHelper(win);
    			Win32Native.SetParent(childHelper.Handle, parentHelper.Handle);
    		}
    	}
    }
    

    这样可以实现MDI窗口,但是问题来了。先设置好主窗口背景图片(为了能演示出效果),然后创建一个子窗口,当移动子窗口时,子窗口的边缘产生了花屏效果。
    分析了原因,应该是当窗口移动时,渲染产生了问题。所以我迫切希望在子窗口的LocationChanged事件中重绘,WPF中没有Invalidate方法,试了很多方法也没有成功。请教高手如何解决这个问题,或者如何重绘子窗口,使其边缘不产生花屏效果。

    我的临时解决方案:我发现当窗口大小发生变化后,窗口会自动重绘,边缘的花屏也就没了,正常了。于是我在LocationChanged事件中更改窗口大小

    using System;
    using System.Windows;
    
    namespace WPFMdi {
    	/// <summary>
    	/// ChildWindow.xaml 的交互逻辑
    	/// </summary>
    	public partial class ChildWindow : Window {
    		public ChildWindow() {
    			InitializeComponent();
    		}
    
    		#region Window重绘处理
    		int count = 0;
    		private void Window_LocationChanged(object sender, EventArgs e) {
    			if (count < 10) {
    				this.Width += 0.101;
    			} else if (count < 20) {
    				this.Width -= 0.102;
    			} 	
    			count = ++count % 20;
    		}
    		#endregion
    	}
    }
    
    

    临时克服了问题,但是不是最终方案,请高手们帮忙解决。
    项目下载



    Sonny.Lin
    2011年5月20日 14:23

答案

  • 很遗憾的是,WPF本身没有实现MDI,所以你会想到用Host的方式,将一个窗体的句柄设置为另一窗体的子窗体 (调用  SetParent API)

    这一点就会引起问题,WPF不同于一般的Win32程序,本身内容是没有句柄的,因为它通过DX去实现绘制,所以消息循环就不同于Win32的窗体了,不能有一个消息队列来对应每一个组件,来各自处理不同的消息,来处理每一次的重绘消息。何况,WPF使用DX后,为了提高性能,缓存了大量的UI信息,使并不是每次窗体区域被遮住或者改变都要进行重绘的,所以上述两点就很显然会导致大小改变的时候边缘有无效区域或者花屏产生。

     

    你的临时方案是可行的,但是我还是要推荐你使用下面的WPF  MDI解决方案:http://wpfmdi.codeplex.com/

    他已经很好的实现了父子窗体间的消息传递了。

     

    Sincerely,


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    2011年5月23日 0:47
    版主