سؤال

في VS .NET، عندما تقوم بتحديد مجلد لمشروع ما، يتم عرض مربع حوار يشبه OpenFileDialog أو SaveFileDialog، ولكن تم إعداده لقبول المجلدات فقط.منذ أن رأيت هذا أردت أن أعرف كيف يتم ذلك.أنا على دراية بـ FolderBrowserDialog، لكني لم أحب هذا الحوار أبدًا.يبدأ الأمر صغيرًا جدًا ولا يسمح لي بالاستفادة من القدرة على كتابة المسار.

أنا على يقين تقريبًا الآن من عدم وجود طريقة للقيام بذلك من .NET، ولكنني أشعر بالفضول أيضًا حول كيفية القيام بذلك من خلال تعليمات برمجية غير مُدارة أيضًا.في حالة عدم إعادة تنفيذ مربع الحوار بالكامل من البداية، كيف يمكنك تعديل مربع الحوار ليكون له هذا السلوك؟

أود أيضًا أن أؤكد مرة أخرى أنني على دراية بـ FolderBrowserDialog ولكن في بعض الأحيان لا أرغب في استخدامه، بالإضافة إلى أنني أشعر بالفضول حقًا حول كيفية تكوين مربع حوار بهذه الطريقة.إن إخباري باستخدام FolderBrowserDialog فقط يساعدني في الحفاظ على تجربة واجهة مستخدم متسقة ولكنه لا يرضي فضولي لذلك لن يتم احتسابه كإجابة.

إنه ليس شيئًا خاصًا بنظام التشغيل Vista أيضًا؛لقد رأيت مربع الحوار هذا منذ VS .NET 2003، لذا فهو قابل للتنفيذ في Win2k وWinXP.هذا أقل من سؤال "أريد أن أعرف الطريقة الصحيحة للقيام بذلك" وأكثر من سؤال "لقد كنت أشعر بالفضول بشأن هذا منذ أن أردت القيام بذلك لأول مرة في VS 2003".أدرك أن مربع حوار ملفات نظام التشغيل Vista يحتوي على خيار للقيام بذلك، لكنه كان يعمل في نظام التشغيل XP لذا أعلم أنهم فعلوا ذلك شئ ما للحصول عليه للعمل.الإجابات الخاصة بنظام التشغيل Vista ليست إجابات، لأن نظام التشغيل Vista غير موجود في سياق السؤال.

تحديث:أنا أقبل إجابة Scott Wisniewski لأنها تأتي مع عينة عمل، ولكن أعتقد أن Serge يستحق الفضل في الإشارة إلى تخصيص مربع الحوار (وهو أمر سيئ باعتراف الجميع من .NET ولكنه يفعل العمل) ومارك رانسوم لاكتشاف أن MS ربما قام بإنشاء مربع حوار مخصص لهذه المهمة.

هل كانت مفيدة؟

المحلول

لدي مربع حوار كتبته يسمى مربع حوار OpenFileOrFolder الذي يسمح لك بفتح مجلد أو ملف.

إذا قمت بتعيين قيمة AcceptFiles على false، فإنه يعمل في وضع قبول المجلد فقط.

يمكنك تنزيل المصدر من GitHub هنا

نصائح أخرى

هناك حزمة كود Windows API.إنه يحتوي على الكثير من الأشياء المتعلقة بالصدفة، بما في ذلك CommonOpenFileDialog الصف (في Microsoft.WindowsAPICodePack.Dialogs مساحة الاسم).هذا هو الحل الأمثل - مربع الحوار المفتوح المعتاد الذي يعرض المجلدات فقط.

فيما يلي مثال لكيفية استخدامه:

CommonOpenFileDialog cofd = new CommonOpenFileDialog();
cofd.IsFolderPicker = true;
cofd.ShowDialog();

لسوء الحظ، لم تعد Microsoft تشحن هذه الحزمة، ولكن قام العديد من الأشخاص بتحميل الثنائيات بشكل غير رسمي إلى NuGet.يمكن العثور على مثال واحد هنا.هذه الحزمة هي مجرد أشياء خاصة بالصدفة.إذا كنت في حاجة إليها، فإن نفس المستخدم لديه العديد من الحزم الأخرى التي توفر المزيد من الوظائف الموجودة في الحزمة الأصلية.

يمكنك استخدام FolderBrowserDialogEx -مشتق قابل لإعادة الاستخدام من المجلد المدمج.يتيح لك هذا الخيار كتابة مسار، حتى مسار UNC.يمكنك أيضًا البحث عن أجهزة الكمبيوتر أو الطابعات باستخدامه.يعمل تمامًا مثل FBD المدمج، ولكن ...أحسن.

(يحرر:كان يجب أن أشير إلى أنه يمكن ضبط مربع الحوار هذا لتحديد الملفات أو المجلدات.)

كود المصدر الكامل (وحدة C# قصيرة واحدة).حر.رخصة MS العامة.

الكود لاستخدامه:

var dlg1 = new Ionic.Utils.FolderBrowserDialogEx();
dlg1.Description = "Select a folder to extract to:";
dlg1.ShowNewFolderButton = true;
dlg1.ShowEditBox = true;
//dlg1.NewStyle = false;
dlg1.SelectedPath = txtExtractDirectory.Text;
dlg1.ShowFullPathInEditBox = true;
dlg1.RootFolder = System.Environment.SpecialFolder.MyComputer;

// Show the FolderBrowserDialog.
DialogResult result = dlg1.ShowDialog();
if (result == DialogResult.OK)
{
    txtExtractDirectory.Text = dlg1.SelectedPath;
}

ال Ookii.Dialogs تحتوي الحزمة على غلاف مُدار حول مربع حوار متصفح المجلد الجديد (نمط Vista).كما أنه يتحلل بأمان على أنظمة التشغيل الأقدم.

من الأفضل استخدام FolderBrowserDialog لذلك.

using (FolderBrowserDialog dlg = new FolderBrowserDialog())
{
    dlg.Description = "Select a folder";
    if (dlg.ShowDialog() == DialogResult.OK)
    {
        MessageBox.Show("You selected: " + dlg.SelectedPath);
    }
}

وبعد ساعات من البحث وجدت هذه الإجابة بواسطة com.leetNightShade ل حل عملي.

هناك ثلاثة أشياء أعتقد أنها تجعل هذا الحل أفضل بكثير من جميع الحلول الأخرى.

  1. انها سهلة الاستخدام. يتطلب الأمر فقط تضمين ملفين (يمكن دمجهما في ملف واحد على أي حال) في مشروعك.
  2. فإنه يعود إلى المعيار FolderBrowserDialog عند استخدامه على أنظمة XP أو الأنظمة الأقدم.
  3. يمنح المؤلف الإذن باستخدام الكود لأي غرض تراه مناسبًا.

    لا يوجد ترخيص من هذا القبيل، فأنت حر في أن تأخذ الكود وتفعل به ما تريد.

قم بتنزيل الكود هنا.

نسخة صوتية دقيقة يعمل بهذه الطريقة على نظام التشغيل Windows XP.يتم عرض مربع حوار فتح الملف القياسي، لكن حقل اسم الملف يحتوي على النص "سيتم تجاهل اسم الملف".

مجرد تخمين هنا، ولكن أظن أنه يتم إدخال السلسلة في عنصر التحكم في تحرير مربع التحرير والسرد في كل مرة يتم فيها إجراء تغيير مهم في مربع الحوار.طالما أن الحقل ليس فارغًا، وتم تعيين علامات الحوار على عدم التحقق من وجود الملف، فيمكن إغلاق مربع الحوار بشكل طبيعي.

يحرر: هذا أسهل بكثير مما كنت أعتقد.إليك الكود الموجود في C++/MFC، ويمكنك ترجمته إلى البيئة التي تختارها.

CFileDialog dlg(true, NULL, "Filename will be ignored", OFN_HIDEREADONLY | OFN_NOVALIDATE | OFN_PATHMUSTEXIST | OFN_READONLY, NULL, this);
dlg.DoModal();

تحرير 2: يجب أن تكون هذه هي الترجمة إلى لغة C#، لكنني لا أتقن لغة C# لذا لا تطلق النار علي إذا لم تنجح.

OpenFileDialog openFileDialog1 = new OpenFileDialog();

openFileDialog1.FileName = "Filename will be ignored";
openFileDialog1.CheckPathExists = true;
openFileDialog1.ShowReadOnly = false;
openFileDialog1.ReadOnlyChecked = true;
openFileDialog1.CheckFileExists = false;
openFileDialog1.ValidateNames = false;

if(openFileDialog1.ShowDialog() == DialogResult.OK)
{
    // openFileDialog1.FileName should contain the folder and a dummy filename
}

تحرير 3: أخيرًا، نظرت إلى مربع الحوار الفعلي المعني، في Visual Studio 2005 (لم أتمكن من الوصول إليه سابقًا). إنه ليس مربع حوار فتح الملف القياسي! إذا قمت بفحص النوافذ في Spy++ ومقارنتها بملف قياسي مفتوح، فسترى أن أسماء البنية والفئات غير متطابقة.عندما تنظر عن كثب، يمكنك أيضًا اكتشاف بعض الاختلافات بين محتويات مربعات الحوار.استنتاجي هو أن Microsoft استبدلت مربع الحوار القياسي في Visual Studio بالكامل لمنحها هذه الإمكانية.الحل الخاص بي أو شيء مشابه سيكون أقرب ما يمكن، إلا إذا كنت على استعداد لبرمجة حل خاص بك من البداية.

حسنًا ، اسمحوا لي أن أحاول توصيل النقطة الأولى ؛-) أن اللعب قليلاً مع Spy ++ أو Winspector يوضح أن مربع نص المجلد في موقع VS Project هو تخصيص للحوار القياسي.إنه ليس نفس الحقل الموجود في مربع نص اسم الملف في مربع حوار الملف القياسي مثل ذلك الموجود في "المفكرة".

من الآن فصاعدًا، أعتقد أن VS يخفي اسم الملف ونوع الملف ومربعات النص/مربعات التحرير والسرد ويستخدم قالب حوار مخصص لإضافة الجزء الخاص به في أسفل مربع الحوار.

يحرر:فيما يلي مثال على هذا التخصيص وكيفية القيام بذلك (في Win32.ليس .NET):

m_ofn هي بنية OPENFILENAME التي تشكل أساس مربع حوار الملف.أضف هذين السطرين:

  m_ofn.lpTemplateName = MAKEINTRESOURCE(IDD_FILEDIALOG_IMPORTXLIFF);
  m_ofn.Flags |= OFN_ENABLETEMPLATE;

حيث IDD_FILEDIALOG_IMPORTXLIFF هو قالب حوار مخصص سيتم إضافته في أسفل مربع الحوار.انظر الجزء باللون الأحمر أدناه.alt text
(مصدر: apptranslator.com)

في هذه الحالة، الجزء المخصص هو مجرد تسمية + ارتباط تشعبي ولكن يمكن أن يكون أي مربع حوار.يمكن أن يحتوي على زر "موافق" الذي يسمح لنا بالتحقق من صحة اختيار المجلد فقط.

لكن كيف سنتخلص من بعض عناصر التحكم في الجزء القياسي من مربع الحوار، لا أعرف.

مزيد من التفاصيل في مقالة MSDN هذه.

يمكنك تصنيف مربع حوار الملف إلى فئة فرعية والوصول إلى جميع عناصر التحكم الخاصة به.لكل منها معرف يمكن استخدامه للحصول على مقبض النافذة الخاص به.يمكنك بعد ذلك إظهارها وإخفائها، والحصول على رسائل منهم حول تغييرات التحديد وما إلى ذلك.إلخ.كل هذا يتوقف على مقدار الجهد الذي تريد القيام به.

لقد قمنا بعملنا باستخدام دعم فئة WTL وقمنا بتخصيص مربع حوار الملف ليشمل شريط أماكن مخصص وطرق عرض COM الإضافية.

MSDN يوفر معلومات حول كيفية القيام بذلك باستخدام Win32، تتضمن مقالة CodeProject هذه مثالاً, ، و توفر مقالة CodeProject هذه مثالاً على .NET.

يمكنك استخدام رمز مثل هذا

  • عامل التصفية هو إخفاء الملفات
  • اسم الملف هو إخفاء النص الأول

للإخفاء المتقدم لمربع النص لاسم الملف الذي تحتاج إلى إلقاء نظرة عليهOpenFileDialogEx

الرمز:

{
    openFileDialog2.FileName = "\r";
    openFileDialog1.Filter = "folders|*.neverseenthisfile";
    openFileDialog1.CheckFileExists = false;
    openFileDialog1.CheckPathExists = false;
}

أفترض أنك تستخدم نظام التشغيل Vista باستخدام VS2008؟في هذه الحالة أعتقد أن خيار FOS_PICKFOLDERS يتم استخدامه عند استدعاء مربع حوار ملف Vista IFileDialog.أخشى أنه في كود .NET قد يتضمن هذا الكثير من كود التشغيل المتداخل P/Invine لبدء العمل.

الحل الأول

لقد قمت بتطوير هذا كنسخة نظيفة من .NET Win 7-style مجلد تحديد الحوار بواسطة بيل سيدون lyquidity.com (ليس لدي أي انتماء).(لقد تعلمت من الكود الخاص به من إجابة أخرى في هذه الصفحة).لقد كتبت الحل الخاص بي لأن حله يتطلب فئة انعكاس إضافية غير مطلوبة لهذا الغرض المركّز، ويستخدم التحكم في التدفق القائم على الاستثناء، ولا يخزن نتائج مكالمات الانعكاس مؤقتًا.لاحظ أن ثابت متداخل VistaDialog class بحيث لا تحاول متغيرات الانعكاس الثابتة الخاصة بها أن يتم ملؤها إذا كان Show لا يتم استدعاء الطريقة أبدًا.إنه يعود إلى مربع حوار ما قبل نظام التشغيل Vista إذا لم يكن في إصدار Windows عالي بما فيه الكفاية.يجب أن يعمل على أنظمة التشغيل Windows 7 و8 و9 و10 والإصدارات الأحدث (نظريًا).

using System;
using System.Reflection;
using System.Windows.Forms;

namespace ErikE.Shuriken {
    /// <summary>
    /// Present the Windows Vista-style open file dialog to select a folder. Fall back for older Windows Versions
    /// </summary>
    public class FolderSelectDialog {
        private string _initialDirectory;
        private string _title;
        private string _fileName = "";

        public string InitialDirectory {
            get { return string.IsNullOrEmpty(_initialDirectory) ? Environment.CurrentDirectory : _initialDirectory; }
            set { _initialDirectory = value; }
        }
        public string Title {
            get { return _title ?? "Select a folder"; }
            set { _title = value; }
        }
        public string FileName { get { return _fileName; } }

        public bool Show() { return Show(IntPtr.Zero); }

        /// <param name="hWndOwner">Handle of the control or window to be the parent of the file dialog</param>
        /// <returns>true if the user clicks OK</returns>
        public bool Show(IntPtr hWndOwner) {
            var result = Environment.OSVersion.Version.Major >= 6
                ? VistaDialog.Show(hWndOwner, InitialDirectory, Title)
                : ShowXpDialog(hWndOwner, InitialDirectory, Title);
            _fileName = result.FileName;
            return result.Result;
        }

        private struct ShowDialogResult {
            public bool Result { get; set; }
            public string FileName { get; set; }
        }

        private static ShowDialogResult ShowXpDialog(IntPtr ownerHandle, string initialDirectory, string title) {
            var folderBrowserDialog = new FolderBrowserDialog {
                Description = title,
                SelectedPath = initialDirectory,
                ShowNewFolderButton = false
            };
            var dialogResult = new ShowDialogResult();
            if (folderBrowserDialog.ShowDialog(new WindowWrapper(ownerHandle)) == DialogResult.OK) {
                dialogResult.Result = true;
                dialogResult.FileName = folderBrowserDialog.SelectedPath;
            }
            return dialogResult;
        }

        private static class VistaDialog {
            private const string c_foldersFilter = "Folders|\n";

            private const BindingFlags c_flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
            private readonly static Assembly s_windowsFormsAssembly = typeof(FileDialog).Assembly;
            private readonly static Type s_iFileDialogType = s_windowsFormsAssembly.GetType("System.Windows.Forms.FileDialogNative+IFileDialog");
            private readonly static MethodInfo s_createVistaDialogMethodInfo = typeof(OpenFileDialog).GetMethod("CreateVistaDialog", c_flags);
            private readonly static MethodInfo s_onBeforeVistaDialogMethodInfo = typeof(OpenFileDialog).GetMethod("OnBeforeVistaDialog", c_flags);
            private readonly static MethodInfo s_getOptionsMethodInfo = typeof(FileDialog).GetMethod("GetOptions", c_flags);
            private readonly static MethodInfo s_setOptionsMethodInfo = s_iFileDialogType.GetMethod("SetOptions", c_flags);
            private readonly static uint s_fosPickFoldersBitFlag = (uint) s_windowsFormsAssembly
                .GetType("System.Windows.Forms.FileDialogNative+FOS")
                .GetField("FOS_PICKFOLDERS")
                .GetValue(null);
            private readonly static ConstructorInfo s_vistaDialogEventsConstructorInfo = s_windowsFormsAssembly
                .GetType("System.Windows.Forms.FileDialog+VistaDialogEvents")
                .GetConstructor(c_flags, null, new[] { typeof(FileDialog) }, null);
            private readonly static MethodInfo s_adviseMethodInfo = s_iFileDialogType.GetMethod("Advise");
            private readonly static MethodInfo s_unAdviseMethodInfo = s_iFileDialogType.GetMethod("Unadvise");
            private readonly static MethodInfo s_showMethodInfo = s_iFileDialogType.GetMethod("Show");

            public static ShowDialogResult Show(IntPtr ownerHandle, string initialDirectory, string title) {
                var openFileDialog = new OpenFileDialog {
                    AddExtension = false,
                    CheckFileExists = false,
                    DereferenceLinks = true,
                    Filter = c_foldersFilter,
                    InitialDirectory = initialDirectory,
                    Multiselect = false,
                    Title = title
                };

                var iFileDialog = s_createVistaDialogMethodInfo.Invoke(openFileDialog, new object[] { });
                s_onBeforeVistaDialogMethodInfo.Invoke(openFileDialog, new[] { iFileDialog });
                s_setOptionsMethodInfo.Invoke(iFileDialog, new object[] { (uint) s_getOptionsMethodInfo.Invoke(openFileDialog, new object[] { }) | s_fosPickFoldersBitFlag });
                var adviseParametersWithOutputConnectionToken = new[] { s_vistaDialogEventsConstructorInfo.Invoke(new object[] { openFileDialog }), 0U };
                s_adviseMethodInfo.Invoke(iFileDialog, adviseParametersWithOutputConnectionToken);

                try {
                    int retVal = (int) s_showMethodInfo.Invoke(iFileDialog, new object[] { ownerHandle });
                    return new ShowDialogResult {
                        Result = retVal == 0,
                        FileName = openFileDialog.FileName
                    };
                }
                finally {
                    s_unAdviseMethodInfo.Invoke(iFileDialog, new[] { adviseParametersWithOutputConnectionToken[1] });
                }
            }
        }

        // Wrap an IWin32Window around an IntPtr
        private class WindowWrapper : IWin32Window {
            private readonly IntPtr _handle;
            public WindowWrapper(IntPtr handle) { _handle = handle; }
            public IntPtr Handle { get { return _handle; } }
        }
    }
}

يتم استخدامه كما هو الحال في نموذج Windows:

var dialog = new FolderSelectDialog {
    InitialDirectory = musicFolderTextBox.Text,
    Title = "Select a folder to import music from"
};
if (dialog.Show(Handle)) {
    musicFolderTextBox.Text = dialog.FileName;
}

يمكنك بالطبع تجربة خياراته والخصائص التي يعرضها.على سبيل المثال، يسمح بالتحديد المتعدد في مربع حوار نمط Vista.

الحل الثاني

أعطى سيمون موريير إجابة يوضح كيفية القيام بنفس المهمة بالضبط باستخدام التشغيل المتداخل مقابل Windows API مباشرة، على الرغم من أنه يجب استكمال نسخته لاستخدام مربع حوار النمط الأقدم إذا كان في إصدار أقدم من Windows.لسوء الحظ، لم أجد منصبه بعد عندما عملت على الحل.اسم السم الخاص بك!

جرب هذا من مشروع كود (الائتمان لنيترون):

أعتقد أنه نفس مربع الحوار الذي تتحدث عنه - ربما يكون من المفيد إذا أضفت لقطة شاشة؟

bool GetFolder(std::string& folderpath, const char* szCaption=NULL, HWND hOwner=NULL)
{
    bool retVal = false;

    // The BROWSEINFO struct tells the shell how it should display the dialog.
    BROWSEINFO bi;
    memset(&bi, 0, sizeof(bi));

    bi.ulFlags   = BIF_USENEWUI;
    bi.hwndOwner = hOwner;
    bi.lpszTitle = szCaption;

    // must call this if using BIF_USENEWUI
    ::OleInitialize(NULL);

    // Show the dialog and get the itemIDList for the selected folder.
    LPITEMIDLIST pIDL = ::SHBrowseForFolder(&bi);

    if(pIDL != NULL)
    {
        // Create a buffer to store the path, then get the path.
        char buffer[_MAX_PATH] = {'\0'};
        if(::SHGetPathFromIDList(pIDL, buffer) != 0)
        {
            // Set the string value.
            folderpath = buffer;
            retVal = true;
        }       

        // free the item id list
        CoTaskMemFree(pIDL);
    }

    ::OleUninitialize();

    return retVal;
}

في نظام فيستا يمكنك استخدامه IFileDialog مع مجموعة الخيارات FOS_PICKFOLDERS.سيؤدي ذلك إلى عرض نافذة تشبه OpenFileDialog حيث يمكنك تحديد المجلدات:

var frm = (IFileDialog)(new FileOpenDialogRCW());
uint options;
frm.GetOptions(out options);
options |= FOS_PICKFOLDERS;
frm.SetOptions(options);

if (frm.Show(owner.Handle) == S_OK) {
    IShellItem shellItem;
    frm.GetResult(out shellItem);
    IntPtr pszString;
    shellItem.GetDisplayName(SIGDN_FILESYSPATH, out pszString);
    this.Folder = Marshal.PtrToStringAuto(pszString);
}

بالنسبة لنظام التشغيل Windows الأقدم، يمكنك دائمًا اللجوء إلى الخدعة من خلال تحديد أي ملف في المجلد.

يمكن العثور على مثال عملي يعمل على .NET Framework 2.0 والإصدارات الأحدث هنا.

يمكنك استخدام رمز مثل هذا

عامل التصفية عبارة عن سلسلة فارغة.اسم الملف هو AnyName ولكنه ليس فارغًا

        openFileDialog.FileName = "AnyFile";
        openFileDialog.Filter = string.Empty;
        openFileDialog.CheckFileExists = false;
        openFileDialog.CheckPathExists = false;

ال مربعات حوار Ookii لـ WPF تحتوي المكتبة على فئة توفر تطبيقًا لمربع حوار متصفح المجلد لـ WPF.

https://github.com/caioproiete/ookii-dialogs-wpf

enter image description here

هناك أيضًا نسخة تعمل مع نماذج ويندوز.

أعلم أن السؤال كان حول تكوين OpenFileDialog ولكن عندما أرى أن Google أحضرني إلى هنا، فقد أشير أيضًا إلى أنه إذا كنت تبحث فقط عن المجلدات، فيجب عليك استخدام FolderBrowserDialog بدلاً من ذلك تمت الإجابة عليه بواسطة سؤال SO آخر أدناه

كيفية تحديد المسار باستخدام مربع حوار الملف المفتوح في vb.net؟

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top