支持预览的文件选择对话框

来源:互联网 发布:数据库里能放图片吗 编辑:程序博客网 时间:2024/05/10 12:06

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

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

源代码如下:

 

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事件。具体代码如下:

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;
        }
    }
}

 

原创粉丝点击