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)

  • 相关阅读:
    linux 解压tgz 文件指令
    shell 脚本没有执行权限 报错 bash: ./myshell.sh: Permission denied
    linux 启动solr 报错 Your Max Processes Limit is currently 31202. It should be set to 65000 to avoid operational disruption.
    远程查询批量导入数据
    修改 MZTreeView 赋权节点父节点选中子节点自动选中的问题
    关于乱码的问题解决记录
    我的网站优化之路
    对设计及重构的一点反思
    我的五年岁月
    奔三的路上
  • 原文地址:https://www.cnblogs.com/lavezhang/p/2879751.html
Copyright © 2011-2022 走看看