zoukankan      html  css  js  c++  java
  • 支持预览的文件选择对话框

    最近想实现这么一个功能:通过OpenFileDialog对话框选择文件时,每选中一个文件,能够预览该文件的内容。正好园子里有位朋友分享了这样的代码:http://www.cnblogs.com/xiaozhi_5638/archive/2012/12/21/2828376.html

    这位朋友确实厉害啊,谢谢他的分享!

    我对他的程序进行了一些完善,主要改进如下(文章最后有源代码下载):

    1、OpenFileDialog的窗口宽度会非常大,有1600多像素,我限制了一下宽度。
    2、响应WM_ACTIVATE消息时,NativeWindow会重复创建多次,因为主窗体的消息也进入这个方法了,我把多余的窗口Handle排除了。否则,关闭OpenFileDialog之后,每次点击主窗口的边框,窗口宽度都会发生变化。
    3、关闭OpenFileDialog之后,主窗口会被其它窗口盖住。
    4、封装成了一个支持文件预览的通用对话框,并继承Component,可以直接拖放到窗体上。

    最后的运行效果如下图所示:

    源代码如下:

    View Code
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.ComponentModel;
    using System.Runtime.InteropServices;
    using System.Drawing;
    
    namespace WindowsFormsApplication1
    {
        /// <summary>
        /// 扩展文件打开对话框。不可继承该类。
        /// 支持自定义的文件预览功能。
        /// </summary>
        [DefaultEvent("FileSelecting")]
        public sealed class OpenFileDialogEx : Component
        {
            #region 字段区域
    
            private string m_fileName = string.Empty;
            private string m_filer = string.Empty;
            private Control m_previewControl;
    
            #endregion
    
            #region 属性区域
    
            /// <summary>
            /// 获取或设置当前选择的文件名。
            /// </summary>
            [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
            public string FileName
            {
                get { return m_fileName; }
                set { m_fileName = value ?? string.Empty; }
            }
    
            /// <summary>
            /// 获取或设置文件筛选条件。
            /// </summary>
            [Description("文件筛选条件。")]
            public string Filer
            {
                get { return m_filer; }
                set { m_filer = value ?? string.Empty; }
            }
    
            /// <summary>
            /// 获取或设置文件预览控件。
            /// </summary>
            [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
            public Control PreviewControl
            {
                get { return m_previewControl; }
                set { m_previewControl = value; }
            }
    
            #endregion
    
            #region 方法区域
    
            /// <summary>
            /// 显示模式对话框。
            /// </summary>
            /// <returns></returns>
            public DialogResult ShowDialog()
            {
                return ShowDialog(null);
            }
    
            /// <summary>
            /// 显示模式对话框。
            /// </summary>
            /// <param name="owner">宿主控件。</param>
            /// <returns></returns>
            public DialogResult ShowDialog(IWin32Window owner)
            {
                using (OpenFileDialog dialog = new OpenFileDialog() { FileName = m_fileName, Filter = m_filer })
                {
                    //在Vista、WIN7、WIN8上按XP风格显示对话框
                    dialog.AutoUpgradeEnabled = false;
    
                    OpenFileDialogHostForm hostForm = new OpenFileDialogHostForm(this, dialog);
                    if (owner != null)
                        hostForm.Show(owner);
                    else hostForm.Show(Application.OpenForms[0]);
    
                    //隐藏中间窗体
                    Win32.SetWindowPos(hostForm.Handle, IntPtr.Zero, 0, 0, 0, 0,
                        SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_NOOWNERZORDER | SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_HIDEWINDOW);
    
                    //将median作为openfileDialog的owner
                    DialogResult result = dialog.ShowDialog(hostForm);
                    if (result == DialogResult.OK)
                    {
                        m_fileName = dialog.FileName;
                    }
    
                    hostForm.Close();
                    hostForm.Dispose();
    
                    return result;
                }
            }
    
            #endregion
    
            #region 事件委托
    
            /// <summary>
            /// 选择文件时引发该事件。
            /// </summary>
            public event EventHandler<OpenFileDialogExPathEventArgs> FileSelecting;
    
            /// <summary>
            /// 打开路径时引发该事件。
            /// </summary>
            public event EventHandler<OpenFileDialogExPathEventArgs> PathOpened;
    
            /// <summary>
            /// 选择文件时调用该方法。
            /// </summary>
            /// <param name="fileName"></param>
            public void OnFileSelecting(string fileName)
            {
                if (FileSelecting != null && !string.IsNullOrEmpty(fileName)
                    && !string.IsNullOrEmpty(System.IO.Path.GetExtension(fileName)))
                {
                    FileSelecting(this, new OpenFileDialogExPathEventArgs(fileName));
                }
            }
    
            /// <summary>
            /// 打开路径时调用该方法。
            /// </summary>
            /// <param name="path"></param>
            public void OnPathOpened(string path)
            {
                if (PathOpened != null && !string.IsNullOrEmpty(path))
                {
                    PathOpened(this, new OpenFileDialogExPathEventArgs(path));
                }
            }
    
            #endregion
    
            #region 内部类型
    
            /// <summary>
            /// OpenFileDialog宿主窗体。
            /// </summary>
            class OpenFileDialogHostForm : Form
            {
                #region 构造区域
    
                /// <summary>
                /// 构造函数。
                /// </summary>
                /// <param name="dialogEx"></param>
                /// <param name="dialog"></param>
                public OpenFileDialogHostForm(OpenFileDialogEx dialogEx, OpenFileDialog dialog)
                {
                    m_dialogEx = dialogEx;
                    m_dialog = dialog;
    
                    this.StartPosition = FormStartPosition.Manual;
                    this.Location = new System.Drawing.Point(-1000, -1000); //隐藏窗口,避免界面闪烁
                }
    
                #endregion
    
                #region 字段区域
    
                private OpenFileDialogEx m_dialogEx;
                private OpenFileDialog m_dialog = null;
                private DialogNativeWindow m_nativeWindow;
    
                #endregion
    
                #region 方法区域
    
                /// <summary>
                /// 窗口关闭前。
                /// </summary>
                /// <param name="e"></param>
                protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
                {
                    if (m_nativeWindow != null)
                        m_nativeWindow.Dispose();
                    base.OnClosing(e);
                }
    
                /// <summary>
                /// 处理窗口消息。
                /// </summary>
                /// <param name="m"></param>
                protected override void WndProc(ref Message m)
                {
                    //m.LParam为要打开的窗口句柄,开始监听OpenFileDialog的Windows消息
                    if (m.Msg == (int)Msg.WM_ACTIVATE)
                    {
                        //跳过不需要监听的窗口
                        bool needInitNative = true;
                        if (Application.OpenForms != null && Application.OpenForms.Count > 0)
                        {
                            foreach (Form frm in Application.OpenForms)
                            {
                                if (m.LParam == frm.Handle && frm.Handle != this.Handle)
                                    needInitNative = false;
                            }
                        }
                        if (m_nativeWindow == null && needInitNative)
                            m_nativeWindow = new DialogNativeWindow(m_dialogEx, m.LParam, m_dialog);
                    }
                    base.WndProc(ref m);
                }
    
                #endregion
            }
    
            /// <summary>
            /// OpenFileDialog钩子窗口。
            /// </summary>
            class DialogNativeWindow : NativeWindow, IDisposable
            {
                #region 构造区域
    
                /// <summary>
                /// 构造函数。
                /// </summary>
                /// <param name="dialogEx"></param>
                /// <param name="handle">要监视的窗口句柄。</param>
                /// <param name="dialog">打开文件的对话框。</param>
                public DialogNativeWindow(OpenFileDialogEx dialogEx, IntPtr handle, OpenFileDialog dialog)
                {
                    m_dialogEx = dialogEx;
                    m_dialog = dialog;
                    AssignHandle(handle);
                }
    
                #endregion
    
                #region 字段区域
    
                private OpenFileDialogEx m_dialogEx;
                private OpenFileDialog m_dialog; //待扩展OpenFileDialog
                private ChildControlNativeWindow m_childNative;
                private bool m_isInited;//自定义控件是否已初始化
                private bool m_isDisposed;
    
                #endregion
    
                #region 属性区域
    
                /// <summary>
                /// 获取一个值,该值指示当前资源是否已被释放。
                /// </summary>
                public bool IsDisposed
                {
                    get { return m_isDisposed; }
                }
    
                #endregion
    
                #region 方法区域
    
                /// <summary>
                /// 处理窗口消息。
                /// </summary>
                /// <param name="m"></param>
                protected override void WndProc(ref Message m)
                {
                    switch (m.Msg)
                    {
                        case (int)Msg.WM_SHOWWINDOW:
                            InitChildNative();
                            InitCustomControl();
                            break;
                        case (int)Msg.WM_SIZING:
                            UpdateSize();
                            break;
                        case (int)Msg.WM_WINDOWPOSCHANGING:
                            UpdateLocation(m);
                            break;
                    }
                    base.WndProc(ref m);
                }
    
                /// <summary>
                /// 初始化子控件的NativeWindow。
                /// </summary>
                private void InitChildNative()
                {
                    //查找openfileDialog中的子控件
                    Win32.EnumChildWindows(this.Handle, new Win32.EnumWindowsCallBack((IntPtr handle, int lparam) =>
                            {
                                StringBuilder sb = new StringBuilder(256);
                                Win32.GetClassName(handle, sb, sb.Capacity);//获取控件类名
    
                                if (sb.ToString().StartsWith("#32770")) //找到目标控件
                                {
                                    m_childNative = new ChildControlNativeWindow(handle);
                                    m_childNative.SelectFileChanged += new ChildControlNativeWindow.SelectFileChangedEventHandler(childNative_SelectFileChanged);
                                    m_childNative.SelectPathChanged += new ChildControlNativeWindow.SelectPathChangedEventHandler(childNative_SelectPathChanged);
                                    return true;
                                }
                                return true;
                            }),
                        0);
                }
    
                /// <summary>
                /// 初始化自定义控件。
                /// </summary>
                private void InitCustomControl()
                {
                    if (m_dialogEx.PreviewControl != null && !m_dialogEx.PreviewControl.IsDisposed)
                    {
                        //添加控件到OpenFileDialog界面
                        Win32.SetParent(m_dialogEx.PreviewControl.Handle, this.Handle);
    
                        //调整对话框的宽度
                        WINDOWINFO info = new WINDOWINFO();
                        Win32.GetWindowInfo(this.Handle, out info);
                        Win32.SetWindowPos(this.Handle, IntPtr.Zero, (int)info.rcWindow.left, (int)info.rcWindow.top, 500, (int)info.rcWindow.Height, SetWindowPosFlags.SWP_SHOWWINDOW);
    
                        //计算自定义控件的位置和尺寸
                        RECT rc = new RECT();
                        Win32.GetClientRect(this.Handle, ref rc);
                        m_dialogEx.PreviewControl.Height = (int)rc.Height;
                        m_dialogEx.PreviewControl.Location = new Point((int)(rc.Width - m_dialogEx.PreviewControl.Width), 0);
                    }
                    m_isInited = true;
                }
    
                /// <summary>
                /// 更新自定义控件的位置。
                /// </summary>
                /// <param name="m"></param>
                private void UpdateLocation(Message m)
                {
                    if (m_dialogEx.PreviewControl != null && !m_dialogEx.PreviewControl.IsDisposed)
                    {
                        if (!m_isInited && !this.IsDisposed)
                        {
                            WINDOWPOS pos = (WINDOWPOS)Marshal.PtrToStructure(m.LParam, typeof(WINDOWPOS));
                            if (pos.flags != 0 && ((pos.flags & (int)SWP_Flags.SWP_NOSIZE) != (int)SWP_Flags.SWP_NOSIZE))
                            {
                                pos.cx += m_dialogEx.PreviewControl.Width; //修改OpenfileDialog的宽度
                                Marshal.StructureToPtr(pos, m.LParam, true);
    
                                RECT rc = new RECT();
                                Win32.GetClientRect(this.Handle, ref rc);
                                m_dialogEx.PreviewControl.Height = (int)rc.Height;
                            }
                        }
                    }
                }
    
                /// <summary>
                /// 更新自定义控件的尺寸。
                /// </summary>
                private void UpdateSize()
                {
                    if (m_dialogEx.PreviewControl != null && !m_dialogEx.PreviewControl.IsDisposed)
                    {
                        if (!this.IsDisposed)
                        {
                            //新添加的控件与openfileDialog大小一致
                            RECT rc = new RECT();
                            Win32.GetClientRect(this.Handle, ref rc);
                            Win32.SetWindowPos(m_dialogEx.PreviewControl.Handle, (IntPtr)ZOrderPos.HWND_BOTTOM, 0, 0, (int)m_dialogEx.PreviewControl.Width, (int)rc.Height,
                                SetWindowPosFlags.SWP_NOACTIVATE |
                                SetWindowPosFlags.SWP_NOOWNERZORDER |
                                SetWindowPosFlags.SWP_NOMOVE |
                                SetWindowPosFlags.SWP_ASYNCWINDOWPOS |
                                SetWindowPosFlags.SWP_DEFERERASE);
                        }
                    }
                }
    
                /// <summary>
                /// 释放资源。
                /// </summary>
                public void Dispose()
                {
                    ReleaseHandle();
    
                    if (m_childNative != null)
                    {
                        m_childNative.SelectFileChanged -= new ChildControlNativeWindow.SelectFileChangedEventHandler(childNative_SelectFileChanged);
                        m_childNative.SelectPathChanged -= new ChildControlNativeWindow.SelectPathChangedEventHandler(childNative_SelectPathChanged);
                        m_childNative.Dispose();
                    }
    
                    m_isDisposed = true;
                }
    
                /// <summary>
                /// 选择目录发生变化。
                /// </summary>
                /// <param name="path"></param>
                void childNative_SelectPathChanged(string path)
                {
                    m_dialogEx.OnPathOpened(path);
                }
    
                /// <summary>
                /// 选择文件发生变化。
                /// </summary>
                /// <param name="fileName"></param>
                void childNative_SelectFileChanged(string fileName)
                {
                    m_dialogEx.OnFileSelecting(fileName);
                }
    
                #endregion
            }
    
            /// <summary>
            /// 子控件钩子窗口。
            /// </summary>
            class ChildControlNativeWindow : NativeWindow, IDisposable
            {
                #region 构造区域
    
                /// <summary>
                /// 构造函数。
                /// </summary>
                /// <param name="handle"></param>
                public ChildControlNativeWindow(IntPtr handle)
                {
                    AssignHandle(handle);
                }
    
                #endregion
    
                #region 方法区域
    
                /// <summary>
                /// 处理窗口消息。
                /// </summary>
                /// <param name="m"></param>
                protected override void WndProc(ref Message m)
                {
                    switch (m.Msg)
                    {
                        case (int)Msg.WM_NOTIFY:
                            OFNOTIFY ofNotify = (OFNOTIFY)Marshal.PtrToStructure(m.LParam, typeof(OFNOTIFY));
                            if (ofNotify.hdr.code == (uint)DialogChangeStatus.CDN_SELCHANGE) //openfileDialog选择文件发生变化
                            {
                                StringBuilder sb = new StringBuilder(256);
                                Win32.SendMessage(Win32.GetParent(this.Handle), (int)DialogChangeProperties.CDM_GETFILEPATH, (int)256, sb);
                                if (SelectFileChanged != null)
                                    SelectFileChanged(sb.ToString()); //通知注册者
                            }
                            else if (ofNotify.hdr.code == (uint)DialogChangeStatus.CDN_FOLDERCHANGE) //openfileDialog选择目录发生变化
                            {
                                StringBuilder sb = new StringBuilder(256);
                                Win32.SendMessage(Win32.GetParent(this.Handle), (int)DialogChangeProperties.CDM_GETFOLDERPATH, (int)256, sb);
                                if (SelectPathChanged != null)
                                    SelectPathChanged(sb.ToString()); //通知注册者
                            }
                            break;
                    }
                    base.WndProc(ref m);
                }
    
                /// <summary>
                /// 释放资源。
                /// </summary>
                public void Dispose()
                {
                    ReleaseHandle();
                }
    
                #endregion
    
                #region 事件委托
    
                //当openfileDialog的选择文件发生变化时发生
                public delegate void SelectFileChangedEventHandler(string fileName);
                public event SelectFileChangedEventHandler SelectFileChanged;
    
                //当openfileDialog的选择目录发生变化时发生
                public delegate void SelectPathChangedEventHandler(string path);
                public event SelectPathChangedEventHandler SelectPathChanged;
    
                #endregion
            }
    
            #endregion
        }
    
        /// <summary>
        /// 路径事件参数。
        /// </summary>
        [Serializable]
        public class OpenFileDialogExPathEventArgs : EventArgs
        {
            #region 构造区域
    
            /// <summary>
            /// 构造函数。
            /// </summary>
            /// <param name="path">与事件相关的路径名(文件名或文件夹名)。</param>
            public OpenFileDialogExPathEventArgs(string path)
            {
                m_path = path;
            }
    
            #endregion
    
            #region 字段区域
    
            private string m_path = string.Empty;
    
            #endregion
    
            #region 属性区域
    
            /// <summary>
            /// 获取与事件相关的路径名(文件名或文件夹名)。
            /// </summary>
            public string Path
            {
                get { return m_path; }
            }
    
            #endregion
        }
    }

    使用范例:

    往窗体上拖放一个OpenFileDialogEx组件,双击该组件,注册FileSelecting事件。具体代码如下:

    View Code
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    
    namespace WindowsFormsApplication1
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
    
                pictureBox1 = new PictureBox();
                pictureBox1.Width = 300;
                pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;
                pictureBox1.BorderStyle = BorderStyle.FixedSingle;
    
                openFileDialogEx1.PreviewControl = pictureBox1;
            }
    
            private PictureBox pictureBox1;
    
            private void button1_Click(object sender, EventArgs e)
            {
                if (openFileDialogEx1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                }
            }
    
            private void openFileDialogEx1_FileSelecting(object sender, OpenFileDialogExPathEventArgs e)
            {
                //预览图片
                pictureBox1.ImageLocation = e.Path;
            }
        }
    }

     下载完整的源代码(解压密码:cnblogs)

  • 相关阅读:
    ubuntu下wget的配置文件在哪里
    ubuntu下apt-get的配置文件是哪个
    Jar包方式运行web项目
    深入浅说服务如何以Jar包的方式发布
    maven 私服的setting.xml配置
    maven 配置私服 连接
    一、Spring MVC起步——IntelliJ IDEA 搭建Spring MVC环境(手把手搭建)
    intellij IDEA配置tomcat
    maven工程项目与项目之间的依赖方式
    手动创建Maven项目并建立两个项目之间的依赖关系
  • 原文地址:https://www.cnblogs.com/lavezhang/p/2879751.html
Copyright © 2011-2022 走看看