SaveFileDialog AddExtension doesn't work as expected
Asked Answered
P

2

5

I've the following C# code, which is using the SaveFileDialog and set's the AddExtension property to true:

var dialog = new SaveFileDialog();
dialog.AddExtension = true;
dialog.DefaultExt = "txt";
dialog.Filter = "Text files (*.txt)|*.txt|XML files (*.xml)|*.xml";
dialog.OverwritePrompt = true;
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
    label1.Text = dialog.FileName;
}

And I've tested the following combination of File name and Save as type of the dialog.

File name       | Save as type   | label1.Text    | What I expect
----------------+----------------+----------------+----------------
test1           | *.txt          | test1.txt      | test1.txt
test2.txt       | *.txt          | test2.txt      | test2.txt
test3.abc       | *.txt          | test3.abc.txt  | test3.abc.txt
test4           | *.xml          | test4.xml      | test4.xml
test5.xml       | *.xml          | test5.xml      | test5.xml
test6.abc       | *.xml          | test6.abc.xml  | test6.abc.xml
----------------+----------------+----------------+----------------
test7.xml       | *.txt          | test7.xml      | test7.xml.txt
test8.bmp       | *.txt          | test8.bmp      | test8.bmp.txt
test9.bmp       | *.xml          | test9.bmp      | test9.bmp.xml     

For the last three lines of the above table I would expect a double extension like it does for the unknown abc extension. Applications like Microsoft Word behave like that (they always add the double extension if the Save as type doesn't match the extension given by the user in File name).

Is there a way to change that?

I don't want to do it after the dialog is closed because then I've to check again if the file already exists and if the file name is not too long.

Update:

I've tested it as well with the MONO framework using Ubuntu 18.04. In that case a double extension is never created, e.g.: test3.abc using MONO vs test3.abc.txt using .NET Framework 4.5 (Windows 10).

Pinebrook answered 16/4, 2019 at 14:51 Comment(5)
From FileDialog.AddExtension Property: "Gets or sets a value indicating whether the dialog box automatically adds an extension to a file name if the user omits the extension."Physicality
I use the FilterIndex property to decide if I should store it as txt or xml file and not the extension of the FileName property.Pinebrook
@Physicality But if the user adds an extension to the file name which is not in the selected file filter (Save type as) then I would expect a double extension, like *.abc.txt. It adds a double extension to all unknown (for the system) extensions like abc and it doesn't add a double extension for all known (for the system) extensions like bmp. But I cannot expect from the user that he knows which extensions the system knows.Pinebrook
The link mentions that CheckFileExists = true. Don't see that in your post, so try that.Physicality
@Physicality The CheckFileExists=true property doesn't make sense for SaveFileDialog. I don't want to see a warning if the file doesn't exist.Pinebrook
T
1

Source code on my Github (batressc)

In simple terms, all extensions except *.abc are valid file type extension in Windows OS. When you set AddExtension property in true, only if you put a unregistered file extension, .NET Framework autocomplete automatically the file name with value of selected file extension in the save file dialog.

In this example:

  1. In my Windows 10 OS, I don't have registered the file type extension *.abc (We can view file type extensions under HKEY_CLASSES_ROOT using regedit.exe)

Windows OS without "abc" file type extension

  1. I test "test3.abc" case with the expected result

Using third case Expected result

  1. I register the *.abc file type extension in HKEY_CLASSES_ROOT only creating a new key with name .abc

Creating a new key

  1. I repeat point 2 and now the txt part is not visible

Expected disgusting result

To fix this, we can create an extension method that he makes sure to add the selected extension in the save file dialog

// It's good practice create extensions methods in the same namespace of the class to extend
namespace System.Windows.Forms {
    public static class SaveFileDialogFileTypeExtension {
        // Retrieving only text of the file extensions
        private static List<string> GetFileExtensions(string filter) {
            List<string> extensions = new List<string>();
            var filtersRaw = filter.Split('|');
            for (int i = 0; i < filtersRaw.Length; i++) {
                if (i % 2 != 0) {
                    // Supporting multi doted extensions
                    extensions.Add(filtersRaw[i].Trim().Replace("*", "").Substring(1));
                }
            }
            return extensions;
        }

        // Getting filename with selected extension
        public static string FileNameForceExtension(this SaveFileDialog dialog) {
            string fileName = dialog.FileName;
            // Retrieving the current selected filter index
            List<string> extensions = GetFileExtensions(dialog.Filter);
            string selectedExtension = extensions[dialog.FilterIndex - 1];
            // Adding extension if need it
            if (!fileName.EndsWith($".{selectedExtension}")) {
                fileName = $"{fileName}.{selectedExtension}";
            }
            return fileName;
        }
    }
}

Instead to use FileName we can use FileNameForceExtension. In my case, I use it that form:

textBoxFileName.Text = dialog.FileName + " | " + dialog.FileNameForceExtension();

And this is the result using test7.xml with *.txt file extension:

Fix it!

NOTES

In the implementation of FileDialog of Windows Forms (FileDialog.cs on GitHub) inside the code not specified to find the file extensions using OS functions or methods, GetExtension and HasExtension methods only validate the pattern .<extension> at last of the file name (Path.cs on GitHub). Maybe the validation of the registered extensions in the Windows OS is an internal functionality of the Framework and this is not visible for the developer... :(

Terraqueous answered 16/4, 2019 at 19:56 Comment(1)
This solution doesn't fulfill the following requirement: I don't want to do it after the dialog is closed because then I've to check again if the file already exists and if the file name is not too long.Pinebrook
X
0

So I know it's a bit late to answer now as you probably have moved on but for other people who come here in the future looking for an answer as I myself just did here's the answer: The only way to do this is make your own SaveFiledialog control or to modify the existing one Using A partial Class that Inherits SaveFiledialog. Microsoft Marked it as noninheritable. Much of the base classes are protected in some way to stop people from modifying them. It's a ridiculous unnecessary pain but it is what it is. And even if you manage to get your hands on a copy of the class it relies on a bunch of other protected stuff. Your Best bet is to make your own completely or attempt to parse the filename after and add the extension if it is not there yourself as other people said. Yes this sucks because once you add the extension the file name might become too long but nothing you can do about it. The maximum length for a file name on windows 10 however is 260 characters including the extension. Users will rarely try to name a file that long. It's not something you should leave to chance but Microsoft has left us no choice. One option would be to just ask the user to add the extension if they don't using a message box then showing the dialog again. It would be nice if there was at least a property to set the maximum filename length of the dialog to a custom length (which would be useful in more scenarios than just this might I add. @microsoft) then that would allow you to account for the extension possibly needing to be added but there isn't even an option to do that.

Xantho answered 3/1, 2021 at 21:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.