Unity implements file selection and saving windows in multiple ways

In the process of project development with Unity, the file selection box is often used. There are generally three ways to implement it:

1. In editor mode

The editor mode can be implemented using the method in the EditorUtility class and needs to reference the UnityEditor namespace.

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

This method only supports running in editor mode. Because it has the UnityEditor namespace, it cannot be used after it is published as an exe.

2. In release mode

First you need to quoteSystem.Windows.FormsNamespaces.

Error reported when referencing System.Windows.Forms

The reason for the error is that the System.Windows.Forms.dll file is missing and the file needs to be imported manually.

  1. Find Editor\Data\MonoBleedingEdge\lib\mono\2.0-api\ under the installation path where unity is located. Be careful not to copy it wrong. There will be libraries with the same name in other paths, but an error will appear after packaging.

  2. Import it into the Plugins folder in the root directory of the unity project, so that the project will automatically reference the Dll.

The method of calling the file pop-up window is as follows:

Select folder pop-up window

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

Using the above method to call the file selection pop-up window, sometimes the following pop-up window will appear:
Insert image description here
This seriously affects the user experience. For this reason, I found a solution on the Internet. Use the handle to detect the pop-up window and close it when it appears. Implement the code as follows:

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

        }

However, this method ultimately treats the symptoms rather than the root cause, and the function is slightly awkward to implement, so a third implementation method emerged.

3. Use C++ dynamic library to implement pop-up windows

Since the method is also learned and referenced from many bloggers on the Internet, I don’t know much about some parameters. If you are interested, you can study the official Chinese instructions for Windows application development on your own . I won’t introduce it here. I will just paste the code:

	/// <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] = '\0';
            if (DialogShow.SHGetPathFromIDList(pidl, path))
            {
    
    
                string str = new string(path);
                string DirPath = str.Substring(0, str.IndexOf('\0'));
                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文档)\0*.doc\0xls Files(*xls表格)\0*.xls\0ppt Files(*ppt演示文档)\0*.ppt\0";
                    break;
                case Constant.EFileType.json:
                    filter = "Json Files(*json文件)\0*.json\0";
                    break;
                case Constant.EFileType.txt:
                    filter = "Text Files(*文本文件)\0*.txt\0";
                    break;
                case Constant.EFileType.all:
                    filter = "All Files(*.*)\0*.*\0\0";
                    break;
                case Constant.EFileType.Texture:
                    filter = "Png Files(*图片文件)\0*.png\0Jpg Files(*图片文件)\0*.jpg\0";
                    break;
                case Constant.EFileType.Video:
                    filter = "Video Files(*视频文件)\0*.mp4;*.mov\0";
                    break;
                case Constant.EFileType.Music:
                    filter = "wav Files(*音频文件)\0*.wav\0 mp3 Files(*音频文件)\0*.mp3\0";
                    break;

            }

            return filter;
        }

        #endregion
    }

Guess you like

Origin blog.csdn.net/qq_42914882/article/details/131456560