unity多种方式实现文件选择和保存窗口

在用unity做项目开发的过程中,经常会用到文件选择框,一般有以下三种方式实现:

一、编辑器模式下

编辑器的模式,可以利用EditorUtility类里面的方式实现,需要引用UnityEditor命名空间。

  //选择文件
   var filePath = EditorUtility.OpenFilePanel("选择打开文件", UnityEngine.Application.streamingAssetsPath, "json");
  //保存文件
   var fileName= EditorUtility.SaveFilePanel("选择保存路径", Application.streamingAssetsPath, "", "json");
  //选择文件夹
   var filePath = EditorUtility.OpenFolderPanel("选择打开的文件夹", UnityEngine.Application.streamingAssetsPath, "默认文件夹名");

此种方式只支持在编辑器模式下运行,因为带有UnityEditor命名空间,故发布成exe之后不能使用。

二、发布模式下

首先需要引用System.Windows.Forms命名空间。

引用System.Windows.Forms报错

报错的是原因是缺少System.Windows.Forms.dll文件,需要手动导入该文件。

  1. 找到unity所在的安装路径下的EditorDataMonoBleedingEdgelibmono2.0-api,注意不要复制错了,其他路径也会有同名的库,但是打包之后会出现报错的情况。

  2. 将其导入到unity项目根目录的Plugins文件夹中,这样项目就会自动引用该Dll了。

文件弹窗调用方法如下:

选择文件夹弹窗

  		/// <summary>
        /// 保存文件
        /// </summary>
        /// <param name="title">标题</param>
        /// <param name="onComplete">回调</param>
        /// <param name="fileExts">文件保存的类型</param>
         public static void SaveFile(string title, Action<bool, string> onComplete = null,
            params string[] fileExts)
        {
            var dialog = new SaveFileDialog
            {
                Title = title
            };
            string filter = "";
            foreach (var item in fileExts)
            {
                filter += $"{item}|";
            }

            filter = filter.Remove(filter.Length - 1);

            dialog.Filter = filter;
            DialogResult result = dialog.ShowDialog();

            if (result == DialogResult.OK)
            {
                string filepath = dialog.FileName;
               

                onComplete?.Invoke(true, filepath);
            }
            else
            {
                onComplete?.Invoke(false, "");
            }

        }
        /// <summary>
        /// 选择文件窗口
        /// </summary>
        /// <param name="title">弹窗标题</param>
        /// <param name="onComplete">完成回调</param>
        /// <param name="fileExts">文件类型</param>
        static void ChooseFile(string title, Action<bool, string> onComplete = null, params string[] fileExts)
        {

            var dialog = new OpenFileDialog();
            dialog.Title = title;
            string filter = "";
            foreach (var item in fileExts)
            {
                filter += $"{item}|";
            }
            filter = filter.Remove(filter.Length - 1);

            dialog.Filter = filter;
            DialogResult result = dialog.ShowDialog();

            if (result == DialogResult.OK)
            {
                onComplete?.Invoke(true,  dialog.FileName);
            }
            else
            {
                onComplete?.Invoke(false, "");
            }

        }

 		/// <summary>
        /// 选择文件夹
        /// </summary>
        /// <param name="title">选择框标题</param>
        /// <param name="onComplete">回调</param>
        public static void ChooseDictionaryOld(string title, Action<bool, string> onComplete = null)
        {
            var dialog = new FolderBrowserDialog();
            dialog.Description = title;
            dialog.ShowNewFolderButton = true;
            dialog.RootFolder = Environment.SpecialFolder.ApplicationData;
            DialogResult result = dialog.ShowDialog();

            if (result == DialogResult.OK)
            {
                onComplete?.Invoke(true, dialog.SelectedPath);
            }
            else
            {
                onComplete?.Invoke(false, "");
            }
        }
        
		/// <summary>
        /// 文件类型的格式
        /// </summary>
  		public class FileType
        {
            public const string doc = "doc文件(.doc)|*.doc";
            public const string txt = "文本文件(.txt)|*.txt";
            public const string json = "json文件(.json)|*.json";
            public const string xls = "xls表格(.xls)|*.xls";
            public const string ppt = "ppt文件(.ppt)|*.ppt";
        }

用上面的方法调用文件选择弹窗,有时会出现以下弹窗:
在这里插入图片描述
这里严重影响使用体验,为此我在网上找到了一个解决办法,利用句柄检测弹窗,在弹窗出现的时候关闭,实现代码如下:

 		[DllImport("user32.dll")]
        public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
        [DllImport("user32.dll")]
        public static extern int SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);

        public const int WM_SYSCOMMAND = 0x0112;
        public const int SC_CLOSE = 0xF060;

        static FileTool()
        {
           //在类初始化的时候,调用此方法
            FindAndCloseWindow();
        }

        /// <summary>
        /// 关闭对应弹窗
        /// </summary>
        static void FindAndCloseWindow()
        {
            IntPtr lHwnd = FindWindow("ClearOOPs", "Oops");
            if (lHwnd != IntPtr.Zero)
            {
                SendMessage(lHwnd, WM_SYSCOMMAND, SC_CLOSE, 0);
            }

        }

但此种办法终究是治标不治本,功能实现起来略微别扭,于是出现了第三种实现方法。

三、利用C++动态库实现弹窗

由于方法也是从网上多个博主学习参考过来的,有些参数我也不太了解,感兴趣的可以自己研究一下Windows应用开发中文说明官网,这里也就不多做介绍了,直接贴代码:

	/// <summary>
    /// 文件类
    /// </summary>
 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public class FileOpenDialog
    {
        public int structSize = 0;
        public IntPtr dlgOwner = IntPtr.Zero;
        public IntPtr instance = IntPtr.Zero;
        public String filter = null;
        public String customFilter = null;
        public int maxCustFilter = 0;
        public int filterIndex = 0;
        public String file = null;
        public int maxFile = 0;
        public String fileTitle = null;
        public int maxFileTitle = 0;
        public String initialDir = null;
        public String title = null;
        public int flags = 0;
        public short fileOffset = 0;
        public short fileExtension = 0;
        public String defExt = null;
        public IntPtr custData = IntPtr.Zero;
        public IntPtr hook = IntPtr.Zero;
        public String templateName = null;
        public IntPtr reservedPtr = IntPtr.Zero;
        public int reservedInt = 0;
        public int flagsEx = 0;
    }

	/// <summary>
    /// 文件夹类
    /// </summary>
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public class OpenDialogDir
    {
        public IntPtr hwndOwner = IntPtr.Zero;
        public IntPtr pidlRoot = IntPtr.Zero;
        public String pszDisplayName = "123";
        public String lpszTitle = null;
        public UInt32 ulFlags = 0;
        public IntPtr lpfn = IntPtr.Zero;
        public IntPtr lParam = IntPtr.Zero;
        public int iImage = 0;
    }

    public class DialogShow
    {
        [DllImport("Comdlg32.dll", SetLastError = true, ThrowOnUnmappableChar = true, CharSet = CharSet.Auto)]
        public static extern bool GetOpenFileName([In, Out] FileOpenDialog dialog); 
        
        [DllImport("Comdlg32.dll", SetLastError = true, ThrowOnUnmappableChar = true, CharSet = CharSet.Auto)]
        public static extern bool GetSaveFileName([In, Out] FileOpenDialog dialog);

        [DllImport("shell32.dll", SetLastError = true, ThrowOnUnmappableChar = true, CharSet = CharSet.Auto)]
        public static extern IntPtr SHBrowseForFolder([In, Out] OpenDialogDir ofn);

        [DllImport("shell32.dll", SetLastError = true, ThrowOnUnmappableChar = true, CharSet = CharSet.Auto)]
        public static extern bool SHGetPathFromIDList([In] IntPtr pidl, [In, Out] char[] fileName);
    }

 static public class OpenFileByWin32
    {
        public static string OpenFile(Constant.EFileType filter, string title, string openPath = "")
        {
            FileOpenDialog dialog = new FileOpenDialog();
            dialog.structSize = Marshal.SizeOf(dialog);
            dialog.filter = Getfilter(filter);
            dialog.file = new string(new char[256]);
            dialog.maxFile = dialog.file.Length;
            dialog.fileTitle = new string(new char[64]);
            dialog.maxFileTitle = dialog.fileTitle.Length;
            dialog.initialDir = openPath==""?UnityEngine.Application.dataPath: openPath; //默认路径
            dialog.title = title;
            dialog.defExt = null; //显示文件的类型
            //注意一下项目不一定要全选 但是0x00000008项不要缺少
            dialog.flags =
                0x00080000 | 0x00001000 | 0x00000800 | 0x00000200 |
                0x00000008; //OFN_EXPLORER|OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST| OFN_ALLOWMULTISELECT|OFN_NOCHANGEDIR

            if (DialogShow.GetOpenFileName(dialog))
            {
                return (dialog.file);
            }
            return "";
        }

        public static string SaveFile(Constant.EFileType filter, string title, string openPath = "")
        {
            FileOpenDialog dialog = new FileOpenDialog();
            dialog.structSize = Marshal.SizeOf(dialog);
            dialog.filter = Getfilter(filter);
            dialog.file = new string(new char[256]);
            dialog.maxFile = dialog.file.Length;
            dialog.fileTitle = new string(new char[64]);
            dialog.maxFileTitle = dialog.fileTitle.Length;
            dialog.initialDir = openPath == "" ? UnityEngine.Application.dataPath : openPath; //默认路径
            dialog.title = title;
            dialog.defExt = ""; //保存文件的类型
            //注意一下项目不一定要全选 但是0x00000008项不要缺少
            dialog.flags =
                0x00080000 | 0x00001000 | 0x00000800 | 0x00000200 |
                0x00000008; //OFN_EXPLORER|OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST| OFN_ALLOWMULTISELECT|OFN_NOCHANGEDIR

            if (DialogShow.GetSaveFileName(dialog))
            {
                return (dialog.file);
            }
            return "";
        }

        /// <summary>
        /// 选择文件夹
        /// </summary>
        /// <param name="title">弹窗口标题</param>
        /// <param name="openPath"></param>
        /// <returns></returns>
        public static string ChooseDictionary( string title, string openPath = "")
        {
            OpenDialogDir openDir = new OpenDialogDir();
            openDir.pszDisplayName = new string(new char[2000]);
            openDir.lpszTitle = title;
            openDir.ulFlags = 1;// BIF_NEWDIALOGSTYLE | BIF_EDITBOX;
            IntPtr pidl = DialogShow.SHBrowseForFolder(openDir);

            char[] path = new char[2000];
            for (int i = 0; i < 2000; i++)
                path[i] = '';
            if (DialogShow.SHGetPathFromIDList(pidl, path))
            {
                string str = new string(path);
                string DirPath = str.Substring(0, str.IndexOf(''));
                Debug.LogError("路径" + DirPath);
                return DirPath;
            }

            return "";
        }

        #region 内部方法
        static string Getfilter(Constant.EFileType fileType)
        {
            string filter = "";
            switch (fileType)
            {
                case Constant.EFileType.word:
                    filter = "word Files(*word文档)*.docxls Files(*xls表格)*.xlsppt Files(*ppt演示文档)*.ppt";
                    break;
                case Constant.EFileType.json:
                    filter = "Json Files(*json文件)*.json";
                    break;
                case Constant.EFileType.txt:
                    filter = "Text Files(*文本文件)*.txt";
                    break;
                case Constant.EFileType.all:
                    filter = "All Files(*.*)*.*";
                    break;
                case Constant.EFileType.Texture:
                    filter = "Png Files(*图片文件)*.pngJpg Files(*图片文件)*.jpg";
                    break;
                case Constant.EFileType.Video:
                    filter = "Video Files(*视频文件)*.mp4;*.mov";
                    break;
                case Constant.EFileType.Music:
                    filter = "wav Files(*音频文件)*.wav mp3 Files(*音频文件)*.mp3";
                    break;

            }

            return filter;
        }

        #endregion
    }

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>