支持预览的文件选择对话框
来源:互联网 发布:数据库里能放图片吗 编辑:程序博客网 时间: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;
}
}
}
- 支持预览的文件选择对话框
- 支持图片预览的文件选择对话框
- 文件预览对话框的实现
- 选择文件的对话框
- 支持选择文件和目录(可多选)的文件对话框CSelectDialog
- 选择文件夹和选择文件的对话框
- 文件选择对话框、路径选择对话框的函数实现
- 弹出文件选择对话框(支持多选)
- 实现文本预览文件对话框
- XImageDialog,带图像预览的打开文件对话框
- VC 实现文件对话框的图像预览功能
- VC 实现文件对话框的图像预览功能
- CFileDialog文件选择对话框的使用
- CFileDialog文件选择对话框的使用
- CFileDialog文件选择对话框的使用
- 文件选择对话框的使用(CFileDialog)
- CFileDialog文件选择对话框的使用
- CFileDialog文件选择对话框的使用
- C++中的友元关系
- 20个2013年最值得关注的网页设计趋势
- Android Contacts 联系人源码分析
- 近期的规划安排
- ant编译脚本详解
- 支持预览的文件选择对话框
- [LeetCode] Longest Common Prefix
- node.js下的canvas,可加水印,可验证码,可裁剪图片
- 设计模式--单例模式
- ios 应用中直接跳转到指定app页面 或者 app 评价页面 的方法
- Android 内存剖析 – 发现潜在问题
- Windows下的Memcached安装
- Jsp中Java脚本获取EL表达式的值
- myeclipse生成注册码的地址