Search code examples
c#wpfwindows-api-code-pack

Customizing OpenFileDialog to modify button text


I wanted a folder dialog box to get the path of a folder that is selected and wanted to save multiple files in to that path.

I am able to get this using WindowsApiCodePack

enter image description here

I want to rename the text in the button "Select folder" to "Save" which was suggested by my clients and there is no other way to satisfy them except to change that.

However, I am able to change the title of the dialog box, but not the button text.

How can I modify the button text?


Solution

  • If you are using this version of the pack, then I don't think there is an easy out-of-the-box soloution. It can be done, but it requires a bit of Reflection to work (if someone found a simpler way, please do share).

    First, you have to reference System.Windows.Forms asembly in your project (if you don't have it already).

    Then, you mock-use it at least once, so the method GetReferencedAssemblies() returns it (again, if you use it anyway, you can skip this step), the following line of code will suffice:

    Form f=null;
    

    Now, this is a class with a few methods to make the necessary reflection easier:

    public class MyReflector
    {
        string myNamespace;
        Assembly myAssembly;
        public MyReflector(string assemblyName, string namespaceName)
        {
            myNamespace = namespaceName;
            myAssembly = null;
            var alist=Assembly.GetExecutingAssembly().GetReferencedAssemblies();
            foreach (AssemblyName aN in alist)
            {
                if (aN.FullName.StartsWith(assemblyName))
                {
                    myAssembly = Assembly.Load(aN);
                    break;
                }
            }
        }
        public Type GetType(string typeName)
        {
            Type type = null;
            string[] names = typeName.Split('.');
    
            if (names.Length > 0)
                type = myAssembly.GetType(myNamespace + "." + names[0]);
    
            for (int i = 1; i < names.Length; ++i)
            {
                type = type.GetNestedType(names[i], BindingFlags.NonPublic);
            }
            return type;
        }
    
        public object Call(Type type, object obj, string func, object[] parameters)
        {
            MethodInfo methInfo = type.GetMethod(func, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            return methInfo.Invoke(obj, parameters);
        }
    
        public object GetField(Type type, object obj, string field)
        {
            FieldInfo fieldInfo = type.GetField(field, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            return fieldInfo.GetValue(obj);
        }
    }
    

    Finally, the code to set the button's text.

        CommonOpenFileDialog ofp = new CommonOpenFileDialog();
        ofp.IsFolderPicker = true;
    
        // In the CommonFileDialog in WindowsAPICodePack, there is a nativeDialog field of type IFileDialog
        // get it first and check if it's not null:
        var r1 = new MyReflector("Microsoft.WindowsAPICodePack", "Microsoft.WindowsAPICodePack");
        Type typeCommonFileDialog = typeof(CommonFileDialog);
        object nativeDialog = r1.GetField(typeCommonFileDialog, ofp, "nativeDialog");
        if (nativeDialog == null)
        {
            // if nativeDialog was null, initialize it:
            r1.Call(ofp.GetType(), ofp, "InitializeNativeFileDialog", new object[]{});
            nativeDialog = r1.Call(ofp.GetType(), ofp, "GetNativeFileDialog", new object[] { });
        }
    
        // call SetOkButtonLabel method on nativeDialog object
        var r2 = new MyReflector("System.Windows.Forms", "System.Windows.Forms");
        Type typeIFileDialog = r2.GetType("FileDialogNative.IFileDialog");
        r2.Call(typeIFileDialog, nativeDialog, "SetOkButtonLabel",new object[] { "Save" });
    
        ofp.ShowDialog();
    

    EXPLANATION:

    In WindowsAPICodePack, CommonOpenFileDialog is a subclass of CommonFileDialog class. In the CommonFileDialog, there is a nativeDialog field of type IFileDialog (the type IFileDialog also isn't public). You can use it to set the text of a button. Sadly, it's private. It is also not initialized after calling just the constructor (some methods initialize it though, so you have to check if it's null at the start). IFileDialog has an internal method SetOkButtonLabel. That's what you need.