From b5873886c1777957e05fc28945cfabdc3e485a13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Ku=C4=8Dera?= Date: Tue, 1 Nov 2022 11:14:09 +0000 Subject: [PATCH 01/21] OpenFolderDialog --- ...esentationFramework-ref-Net48.baseline.txt | 1 + .../PresentationFramework-ref.baseline.txt | 5 +- .../Microsoft/Win32/CommonDialog.cs | 11 +- .../Microsoft/Win32/CommonItemDialog.cs | 609 +++++++++ .../Microsoft/Win32/FileDialog.cs | 1184 +---------------- .../Microsoft/Win32/OpenFileDialog.cs | 147 +- .../Microsoft/Win32/OpenFolderDialog.cs | 326 +++++ .../Microsoft/Win32/SaveFileDialog.cs | 128 +- .../PresentationFramework.csproj | 2 + .../Resources/Strings.resx | 9 - .../Resources/xlf/Strings.cs.xlf | 17 +- .../Resources/xlf/Strings.de.xlf | 17 +- .../Resources/xlf/Strings.es.xlf | 17 +- .../Resources/xlf/Strings.fr.xlf | 17 +- .../Resources/xlf/Strings.it.xlf | 17 +- .../Resources/xlf/Strings.ja.xlf | 17 +- .../Resources/xlf/Strings.ko.xlf | 17 +- .../Resources/xlf/Strings.pl.xlf | 17 +- .../Resources/xlf/Strings.pt-BR.xlf | 17 +- .../Resources/xlf/Strings.ru.xlf | 17 +- .../Resources/xlf/Strings.tr.xlf | 17 +- .../Resources/xlf/Strings.zh-Hans.xlf | 17 +- .../Resources/xlf/Strings.zh-Hant.xlf | 15 - .../ref/PresentationFramework.cs | 37 +- 24 files changed, 1053 insertions(+), 1625 deletions(-) create mode 100644 src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs create mode 100644 src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFolderDialog.cs diff --git a/src/Microsoft.DotNet.Wpf/ApiCompat/Baselines/PresentationFramework-ref-Net48.baseline.txt b/src/Microsoft.DotNet.Wpf/ApiCompat/Baselines/PresentationFramework-ref-Net48.baseline.txt index e1e0e5e898a..b7a37dd2814 100644 --- a/src/Microsoft.DotNet.Wpf/ApiCompat/Baselines/PresentationFramework-ref-Net48.baseline.txt +++ b/src/Microsoft.DotNet.Wpf/ApiCompat/Baselines/PresentationFramework-ref-Net48.baseline.txt @@ -1,5 +1,6 @@ Compat issues with assembly PresentationFramework: MembersMustExist : Member 'internal MS.Internal.AppModel.IFileDialog Microsoft.Win32.FileDialog.CreateVistaDialog()' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'protected System.Int32 Microsoft.Win32.FileDialog.Options.get()' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'internal System.String[] Microsoft.Win32.FileDialog.ProcessVistaFiles(MS.Internal.AppModel.IFileDialog)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'internal System.Boolean Microsoft.Win32.FileDialog.RunFileDialog(MS.Win32.NativeMethods.OPENFILENAME_I)' does not exist in the implementation but it does exist in the contract. CannotRemoveBaseTypeOrInterface : Type 'System.Windows.AttachedPropertyBrowsableForChildrenAttribute' does not implement interface 'System.Runtime.InteropServices._Attribute' in the implementation but it does in the contract. diff --git a/src/Microsoft.DotNet.Wpf/ApiCompat/Baselines/PresentationFramework-ref.baseline.txt b/src/Microsoft.DotNet.Wpf/ApiCompat/Baselines/PresentationFramework-ref.baseline.txt index e60eae0b0cf..3b34a54cbed 100644 --- a/src/Microsoft.DotNet.Wpf/ApiCompat/Baselines/PresentationFramework-ref.baseline.txt +++ b/src/Microsoft.DotNet.Wpf/ApiCompat/Baselines/PresentationFramework-ref.baseline.txt @@ -1,7 +1,6 @@ Compat issues with assembly PresentationFramework: -MembersMustExist : Member 'internal MS.Internal.AppModel.IFileDialog Microsoft.Win32.FileDialog.CreateVistaDialog()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'internal System.String[] Microsoft.Win32.FileDialog.ProcessVistaFiles(MS.Internal.AppModel.IFileDialog)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'internal System.Boolean Microsoft.Win32.FileDialog.RunFileDialog(MS.Win32.NativeMethods.OPENFILENAME_I)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'private protected MS.Internal.AppModel.IFileDialog Microsoft.Win32.CommonItemDialog.CreateDialog()' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'private protected System.String[] Microsoft.Win32.FileDialog.ProcessFiles(MS.Internal.AppModel.IFileDialog)' does not exist in the implementation but it does exist in the contract. CannotChangeAttribute : Attribute 'System.ComponentModel.DesignerSerializationVisibilityAttribute' on 'System.ComponentModel.DesignerProperties.GetIsInDesignMode(System.Windows.DependencyObject)' changed from '[DesignerSerializationVisibilityAttribute(0)]' in the contract to '[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]' in the implementation. CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'System.Windows.AttachedPropertyBrowsableForChildrenAttribute' changed from '[AttributeUsageAttribute(64, AllowMultiple=false)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Method, AllowMultiple=false)]' in the implementation. CannotChangeAttribute : Attribute 'System.ComponentModel.DesignerSerializationVisibilityAttribute' on 'System.Windows.DataTemplate.Triggers' changed from '[DesignerSerializationVisibilityAttribute(2)]' in the contract to '[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Content)]' in the implementation. diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonDialog.cs index fb7b6fcc7c8..9dd271c8eb6 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonDialog.cs @@ -221,7 +221,7 @@ public object Tag //--------------------------------------------------- #region Protected Methods - + // This method is not used by IFileDialog API. Keeping for API compatibility with .NET Framework. /// /// Defines the common dialog box hook procedure that is overridden to /// add specific functionality to a common dialog box. @@ -247,6 +247,7 @@ protected virtual IntPtr HookProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lP return IntPtr.Zero; } + /// /// /// When overridden in a derived class, displays a particular type of common dialog box. /// @@ -275,13 +276,14 @@ protected virtual void CheckPermissionsToShowDialog() //--------------------------------------------------- #region Internal Methods + // This method is not used by IFileDialog API. Keeping for API compatibility with .NET Framework. /// /// Centers the given window on the screen. This method is used by HookProc /// to center the dialog on the screen before it is shown. We can't mark it /// private because we need to call it from our derived classes like /// FileDialog. /// - internal void MoveToScreenCenter(HandleRef hWnd) + private void MoveToScreenCenter(HandleRef hWnd) { // Create an IntPtr to store a handle to the monitor. IntPtr hMonitor = IntPtr.Zero; @@ -343,9 +345,8 @@ internal void MoveToScreenCenter(HandleRef hWnd) // Internal Properties // //--------------------------------------------------- - #region Internal Properties - - #endregion Internal Properties + //#region Internal Properties + //#endregion Internal Properties //--------------------------------------------------- // diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs new file mode 100644 index 00000000000..4eb0905aedd --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs @@ -0,0 +1,609 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// +// +// Description: +// CommonItemDialog is an abstract class derived from CommonDialog +// that implements shared functionality common to all IFileDialog +// variants. It provides a common options storage and events handling. +// + +namespace Microsoft.Win32 +{ + using MS.Internal; + using MS.Internal.AppModel; + using MS.Internal.Interop; + using MS.Win32; + + using System; + using System.ComponentModel; + using System.Collections.Generic; + using System.IO; + using System.Runtime.InteropServices; + using System.Security; + using System.Text; + using System.Threading; + using System.Windows; + + using HRESULT = MS.Internal.Interop.HRESULT; + + /// + /// Provides a common base class for wrappers around both the + /// File Open and File Save common dialog boxes. Derives from + /// CommonDialog. + /// + /// This class is not intended to be derived from except by + /// the OpenFileDialog and SaveFileDialog classes. + /// + public abstract class CommonItemDialog : CommonDialog + { + //--------------------------------------------------- + // + // Constructors + // + //--------------------------------------------------- + #region Constructors + + /// + /// In an inherited class, initializes a new instance of + /// the System.Windows.CommonItemDialog class. + /// + protected CommonItemDialog() + { + // Call Initialize to set defaults for fields + // and to set defaults for some option flags. + // Initialize() is also called from the virtual + // Reset() function to restore defaults. + Initialize(); + } + + #endregion Constructors + + //--------------------------------------------------- + // + // Public Methods + // + //--------------------------------------------------- + #region Public Methods + + /// + /// Resets all properties to their default values. + /// Classes derived from FileDialog are expected to + /// call Base.Reset() at the beginning of their + /// implementation of Reset() if they choose to + /// override this function. + /// + public override void Reset() + { + Initialize(); + } + + /// + /// Returns a string representation of the file dialog with key information + /// for debugging purposes. + /// + // We overload ToString() so that we can provide a useful representation of + // this object for users' debugging purposes. + public override string ToString() + { + return base.ToString() + ": Title: " + Title; + } + + #endregion Public Methods + + //--------------------------------------------------- + // + // Public Properties + // + //--------------------------------------------------- + #region Public Properties + + // The actual flag is FOS_NODEREFERENCELINKS (set = do not dereference, unset = deref) - + // while we have true = dereference and false=do not dereference. Because we expose + // the opposite of the Windows flag as a property to be clearer, we need to negate + // the value in both the getter and the setter here. + /// + /// Gets or sets a value indicating whether the dialog box returns the location + /// of the file referenced by the shortcut or whether it returns the location + /// of the shortcut (.lnk). + /// + public bool DereferenceLinks + { + get + { + return !GetOption(FOS.NODEREFERENCELINKS); + } + set + { + + SetOption(FOS.NODEREFERENCELINKS, !value); + } + } + + /// + /// Gets or sets the initial directory displayed by the file dialog box. + /// + public string InitialDirectory + { + get + { + // Avoid returning a null string - return String.Empty instead. + return _initialDirectory.Value == null ? String.Empty : _initialDirectory.Value; + } + set + { + + _initialDirectory.Value = value; + } + } + + /// + /// Restores the current directory to its original value if the user + /// changed the directory while searching for files. + /// + /// This property is only valid for SaveFileDialog; it has no effect + /// when set on an OpenFileDialog. + /// + public bool RestoreDirectory + { + get + { + return GetOption(FOS.NOCHANGEDIR); + } + set + { + + SetOption(FOS.NOCHANGEDIR, value); + } + } + + /// + /// Gets or sets a string shown in the title bar of the file dialog. + /// If this property is null, a localized default from the operating + /// system itself will be used (typically something like "Save As" or "Open") + /// + public string Title + { + get + { + // Avoid returning a null string - return String.Empty instead. + return _title.Value == null ? String.Empty : _title.Value; + } + set + { + + _title.Value = value; + } + } + + // If false, the file dialog boxes will allow invalid characters in the returned file name. + // We are actually responsible for dealing with this flag - it determines whether all of the + // processing in ProcessFileNames (which includes things such as the AddExtension feature) + // occurs. + /// + /// Gets or sets a value indicating whether to check for situations that would prevent + /// an application from opening the selected file, such as sharing violations or access denied errors. + /// + public bool ValidateNames + { + get + { + return !GetOption(FOS.NOVALIDATE); + } + set + { + + SetOption(FOS.NOVALIDATE, !value); + } + } + + #endregion Public Properties + + //--------------------------------------------------- + // + // Public Events + // + //--------------------------------------------------- + #region Public Events + + /// + /// Occurs when the user clicks on the Open or Save button on a file dialog + /// box. + /// + public event CancelEventHandler FileOk; + + #endregion Public Events + + //--------------------------------------------------- + // + // Protected Methods + // + //--------------------------------------------------- + #region Protected Methods + + /// + /// Raises the System.Windows.FileDialog.FileOk event. + /// + protected void OnFileOk(CancelEventArgs e) + { + if (FileOk != null) + { + FileOk(this, e); + } + } + + // Because this class, FileDialog, is the parent class for both OpenFileDialog + // and SaveFileDialog, this function will perform the common setup tasks + // shared between Open and Save, and will then call RunFileDialog, which is + // overridden in both of the derived classes to show the correct dialog. + // Both derived classes know about the COM IFileDialog interfaces and can + // display those if they're available and there aren't any properties set + // that should cause us not to. + // + /// + /// Performs initialization work in preparation for calling RunFileDialog + /// to show a file open or save dialog box. + /// + protected override bool RunDialog(IntPtr hwndOwner) + { + IFileDialog dialog = CreateDialog(); + + PrepareDialog(dialog); + + using (VistaDialogEvents events = new VistaDialogEvents(dialog, HandleFileOk)) + { + return dialog.Show(hwndOwner).Succeeded; + } + } + + #endregion Protected Methods + + //--------------------------------------------------- + // + // Internal Methods + // + //--------------------------------------------------- + #region Internal Methods + + /// + /// Returns the state of the given options flag. + /// + internal bool GetOption(FOS option) + { + return (_dialogOptions.Value & option) != 0; + } + + /// + /// Sets the given option to the given boolean value. + /// + internal void SetOption(FOS option, bool value) + { + if (value) + { + // if value is true, bitwise OR the option with _dialogOptions + _dialogOptions.Value |= option; + } + else + { + // if value is false, AND the bitwise complement of the + // option with _dialogOptions + _dialogOptions.Value &= ~option; + } + } + + /// + /// Prompts the user with a System.Windows.MessageBox + /// with the given parameters. It also ensures that + /// the focus is set back on the window that had + /// the focus to begin with (before we displayed + /// the MessageBox). + /// + /// Returns the choice the user made in the message box + /// (true if MessageBoxResult.Yes, + /// false if OK or MessageBoxResult.No) + /// + /// We have to do this instead of just calling MessageBox because + /// of an issue where keyboard navigation would fail after showing + /// a message box. See http://bugcheck/default.asp?URL=/Bugs/URT/84016.asp + /// (WinForms ASURT 80262) + /// + internal bool MessageBoxWithFocusRestore(string message, + MessageBoxButton buttons, + MessageBoxImage image) + { + bool ret = false; + + // Get the window that currently has focus and temporarily cache a handle to it + IntPtr focusHandle = UnsafeNativeMethods.GetFocus(); + + try + { + // Show the message box and compare the return value to MessageBoxResult.Yes to get the + // actual return value. + ret = (MessageBox.Show(message, DialogCaption, buttons, image, MessageBoxResult.OK /*default button is OK*/, 0) + == + MessageBoxResult.Yes); + } + finally + { + // Return focus to the window that had focus before we showed the messagebox. + // SetFocus can handle improper hwnd values, including null. + UnsafeNativeMethods.SetFocus(new HandleRef(this, focusHandle)); + } + return ret; + } + + #endregion Internal Methods + + //--------------------------------------------------- + // + // Internal Properties + // + //--------------------------------------------------- + //#region Internal Properties + //#endregion Internal Properties + + //--------------------------------------------------- + // + // Internal Events + // + //--------------------------------------------------- + //#region Internal Events + //#endregion Internal Events + + //--------------------------------------------------- + // + // Private Methods + // + //--------------------------------------------------- + #region Private Methods + + // Provides the actual implementation of initialization tasks. + // Initialize() is called from both the constructor and the + // public Reset() function to set default values for member + // variables and for the options bitmask. + private void Initialize() + { + // + // Initialize Options Flags + // + _dialogOptions.Value = 0; // _dialogOptions is an int containing a set of + // bit flags used to initialize the dialog box. + // Within our code, we only use GetOption and SetOption + // (change from Windows Forms, which sometimes directly + // modified _dialogOptions). As such, we initialize to 0 + // here and then call SetOption to get _dialogOptions + // into the default state. + + // + // Set some default options + // + // - Specifies that the user can type only valid paths and file names. If this flag is + // used and the user types an invalid path and file name in the File Name entry field, + // we will display a warning in a message box. + SetOption(FOS.PATHMUSTEXIST, true); + + // - Force no mini mode for the SaveFileDialog. + SetOption(FOS.DEFAULTNOMINIMODE, true); + + // + // Initialize additional properties + // + _title.Value = null; + _initialDirectory.Value = null; + + // Set this to an empty list so callers can simply add to it. They can also replace it wholesale. + CustomPlaces = new List(); + } + + private protected virtual bool HandleFileOk(IFileDialog dialog) + { + // When this callback occurs, the HWND is visible and we need to + // grab it because it is used for various things like looking up the + // DialogCaption. + UnsafeNativeMethods.IOleWindow oleWindow = (UnsafeNativeMethods.IOleWindow)dialog; + oleWindow.GetWindow(out _hwndFileDialog); + return true; + } + + #endregion Private Methods + + //--------------------------------------------------- + // + // Private Properties + // + //--------------------------------------------------- + #region Private Properties + + /// + /// Gets a string containing the title of the file dialog. + /// + // When showing message boxes onscreen, we want them to have the + // same title bar as the file open or save dialog itself. We can't + // just use the Title property, because if it's null the operating + // system substitutes a standard localized title. + // + // The solution is this private property, which returns the title of the + // file dialog (using the stored handle of the dialog _hwndFileDialog to + // call GetWindowText). + // + // It is designed to only be called by MessageBoxWithFocusRestore. + private string DialogCaption + { + get + { + if (!UnsafeNativeMethods.IsWindow(new HandleRef(this, _hwndFileDialog))) + { + return String.Empty; + } + + // Determine the length of the text we want to retrieve... + int textLen = UnsafeNativeMethods.GetWindowTextLength(new HandleRef(this, _hwndFileDialog)); + // then make a StringBuilder... + StringBuilder sb = new StringBuilder(textLen + 1); + // and call GetWindowText to fill it up... + UnsafeNativeMethods.GetWindowText(new HandleRef(this, _hwndFileDialog), + sb /*target string*/, + sb.Capacity /* max # of chars to copy before truncation occurs */ + ); + // then return the results. + return sb.ToString(); + } + } + + #endregion Private Properties + + #region Vista COM interfaces Augmentation + + /// + /// Events sink for IFileDialog. MSDN says to return E_NOTIMPL for several, but not all, of these methods when we don't want to support them. + /// + /// + /// Be sure to explictly Dispose of it, or use it in a using block. Unadvise happens as a result of Dispose. + /// + private protected sealed class VistaDialogEvents : IFileDialogEvents, IDisposable + { + public delegate bool OnOkCallback(IFileDialog dialog); + + private IFileDialog _dialog; + + private OnOkCallback _okCallback; + uint _eventCookie; + + public VistaDialogEvents(IFileDialog dialog, OnOkCallback okCallback) + { + _dialog = dialog; + _eventCookie = dialog.Advise(this); + _okCallback = okCallback; + } + + HRESULT IFileDialogEvents.OnFileOk(IFileDialog pfd) + { + return _okCallback(pfd) ? HRESULT.S_OK : HRESULT.S_FALSE; + } + + HRESULT IFileDialogEvents.OnFolderChanging(IFileDialog pfd, IShellItem psiFolder) + { + return HRESULT.E_NOTIMPL; + } + + HRESULT IFileDialogEvents.OnFolderChange(IFileDialog pfd) + { + return HRESULT.S_OK; + } + + HRESULT IFileDialogEvents.OnSelectionChange(IFileDialog pfd) + { + return HRESULT.S_OK; + } + + HRESULT IFileDialogEvents.OnShareViolation(IFileDialog pfd, IShellItem psi, out FDESVR pResponse) + { + pResponse = FDESVR.DEFAULT; + return HRESULT.S_OK; + } + + HRESULT IFileDialogEvents.OnTypeChange(IFileDialog pfd) + { + return HRESULT.S_OK; + } + + HRESULT IFileDialogEvents.OnOverwrite(IFileDialog pfd, IShellItem psi, out FDEOR pResponse) + { + pResponse = FDEOR.DEFAULT; + return HRESULT.S_OK; + } + + void IDisposable.Dispose() + { + _dialog.Unadvise(_eventCookie); + } + } + + public IList CustomPlaces { get; set; } + + #region Internal and Protected Methods + + private protected abstract IFileDialog CreateDialog(); + + private protected virtual void PrepareDialog(IFileDialog dialog) + { + if (!string.IsNullOrEmpty(InitialDirectory)) + { + IShellItem initialDirectory = ShellUtil.GetShellItemForPath(InitialDirectory); + if (initialDirectory != null) + { + // Setting both of these so the dialog doesn't display errors when a remembered folder is missing. + dialog.SetDefaultFolder(initialDirectory); + dialog.SetFolder(initialDirectory); + } + } + + dialog.SetTitle(Title); + + // Only accept physically backed locations. + FOS options = _dialogOptions.Value | FOS.FORCEFILESYSTEM; + dialog.SetOptions(options); + + IList places = CustomPlaces; + if (places != null && places.Count != 0) + { + foreach (FileDialogCustomPlace customPlace in places) + { + IShellItem shellItem = ResolveCustomPlace(customPlace); + if (shellItem != null) + { + try + { + dialog.AddPlace(shellItem, FDAP.BOTTOM); + } + catch (ArgumentException) + { + // The dialog doesn't allow some ShellItems to be set as Places (like device ports). + // Silently swallow errors here. + } + } + } + } + } + + #endregion + + #region Private Methods + + private static IShellItem ResolveCustomPlace(FileDialogCustomPlace customPlace) + { + // Use the KnownFolder Guid if it exists. Otherwise use the Path. + return ShellUtil.GetShellItemForPath(ShellUtil.GetPathForKnownFolder(customPlace.KnownFolder) ?? customPlace.Path); + } + + #endregion + + #endregion + + //--------------------------------------------------- + // + // Private Fields + // + //--------------------------------------------------- + #region Private Fields + + // _dialogOptions is a set of bit flags used to control the behavior + // of the Win32 dialog box. + private SecurityCriticalDataForSet _dialogOptions; + + // These private variables store data for the various public properties + // that control the appearance of the file dialog box. + private SecurityCriticalDataForSet _title; // Title bar of the message box + private SecurityCriticalDataForSet _initialDirectory; // Starting directory + + // We store the handle of the file dialog inside our class + // for a variety of purposes (like getting the title of the dialog + // box when we need to show a message box with the same title bar caption) + private IntPtr _hwndFileDialog; + + #endregion Private Fields + } +} diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs index 3e0de1d1356..73748c9fa8a 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs @@ -5,17 +5,9 @@ // // // Description: -// FileDialog is an abstract class derived from CommonDialog +// FileDialog is an abstract class derived from CommonItemDialog // that implements shared functionality common to both File -// Open and File Save common dialogs. It provides a hook -// procedure that handles messages received while the dialog -// is visible and numerous properties to control the appearance -// and behavior of the dialog. -// The actual call to display the dialog to GetOpenFileName() -// or GetSaveFileName() (both functions defined in commdlg.dll) -// is implemented in a derived class's RunFileDialog method. -// -// When running on Vista, the COM IFileDialog interfaces are used. +// Open and File Save common dialogs. // Creation of the specific IFileOpenDialog and IFileSaveDialog are // deferred to the derived classes. // @@ -38,10 +30,6 @@ namespace Microsoft.Win32 using System.Threading; using System.Windows; - using CharBuffer = MS.Win32.NativeMethods.CharBuffer; - using HRESULT = MS.Internal.Interop.HRESULT; - using SecurityHelper = MS.Internal.PresentationFramework.SecurityHelper; - /// /// Provides a common base class for wrappers around both the /// File Open and File Save common dialog boxes. Derives from @@ -50,7 +38,7 @@ namespace Microsoft.Win32 /// This class is not intended to be derived from except by /// the OpenFileDialog and SaveFileDialog classes. /// - public abstract class FileDialog : CommonDialog + public abstract class FileDialog : CommonItemDialog { //--------------------------------------------------- // @@ -90,7 +78,7 @@ protected FileDialog() /// public override void Reset() { - + base.Reset(); Initialize(); } @@ -103,7 +91,7 @@ public override void Reset() // any files selected. public override string ToString() { - StringBuilder sb = new StringBuilder(base.ToString() + ": Title: " + Title + ", FileName: "); + StringBuilder sb = new StringBuilder(base.ToString() + ", FileName: "); sb.Append(FileName); return sb.ToString(); } @@ -122,35 +110,18 @@ public override string ToString() // on whether CheckFileExists is set and whether the // filter contains a valid extension to use. For // details, see the ProcessFileNames function. - // - // It's worth noting that unlike most of these - // properties, AddExtension is a custom flag that - // is unique to our implementation. As such, it is - // a constant value in our class, not stored in - // NativeMethods like the other flags. /// /// Gets or sets a value indicating whether the /// dialog box automatically adds an extension to a /// file name if the user omits the extension. /// - public bool AddExtension - { - get - { - return GetOption(OPTION_ADDEXTENSION); - } - set - { - - SetOption(OPTION_ADDEXTENSION, value); - } - } + public bool AddExtension { get; set; } // - // OFN_FILEMUSTEXIST is only used for Open dialog + // FOS_FILEMUSTEXIST is only used for Open dialog // boxes, according to MSDN. It implies - // OFN_PATHMUSTEXIST and "cannot be used" with a + // FOS_PATHMUSTEXIST and "cannot be used" with a // Save As dialog box... in practice, it seems // to be ignored when used with Save As boxes /// @@ -162,12 +133,12 @@ public virtual bool CheckFileExists { get { - return GetOption(NativeMethods.OFN_FILEMUSTEXIST); + return GetOption(FOS.FILEMUSTEXIST); } set { - SetOption(NativeMethods.OFN_FILEMUSTEXIST, value); + SetOption(FOS.FILEMUSTEXIST, value); } } @@ -181,12 +152,12 @@ public bool CheckPathExists { get { - return GetOption(NativeMethods.OFN_PATHMUSTEXIST); + return GetOption(FOS.PATHMUSTEXIST); } set { - SetOption(NativeMethods.OFN_PATHMUSTEXIST, value); + SetOption(FOS.PATHMUSTEXIST, value); } } @@ -224,28 +195,6 @@ public string DefaultExt } } - // The actual flag is OFN_NODEREFERENCELINKS (set = do not dereference, unset = deref) - - // while we have true = dereference and false=do not dereference. Because we expose - // the opposite of the Windows flag as a property to be clearer, we need to negate - // the value in both the getter and the setter here. - /// - /// Gets or sets a value indicating whether the dialog box returns the location - /// of the file referenced by the shortcut or whether it returns the location - /// of the shortcut (.lnk). - /// - public bool DereferenceLinks - { - get - { - return !GetOption(NativeMethods.OFN_NODEREFERENCELINKS); - } - set - { - - SetOption(NativeMethods.OFN_NODEREFERENCELINKS, !value); - } - } - /// /// Gets a string containing the filename component of the /// file selected in the dialog box. @@ -369,9 +318,6 @@ public string[] FileNames /// separated by the vertical bar character '|' (that is, the new filter string is invalid.) /// /// - /// If DereferenceLinks is true and the filter string is null, a blank - /// filter string (equivalent to "|*.*") will be automatically substituted to work - /// around the issue documented in Knowledge Base article 831559 /// Callers must have FileIOPermission(PermissionState.Unrestricted) to call this API. /// public string Filter @@ -442,83 +388,6 @@ public int FilterIndex } } - /// - /// Gets or sets the initial directory displayed by the file dialog box. - /// - public string InitialDirectory - { - get - { - // Avoid returning a null string - return String.Empty instead. - return _initialDirectory.Value == null ? String.Empty : _initialDirectory.Value; - } - set - { - - _initialDirectory.Value = value; - } - } - - /// - /// Restores the current directory to its original value if the user - /// changed the directory while searching for files. - /// - /// This property is only valid for SaveFileDialog; it has no effect - /// when set on an OpenFileDialog. - /// - public bool RestoreDirectory - { - get - { - return GetOption(NativeMethods.OFN_NOCHANGEDIR); - } - set - { - - SetOption(NativeMethods.OFN_NOCHANGEDIR, value); - } - } - - /// - /// Gets or sets a string shown in the title bar of the file dialog. - /// If this property is null, a localized default from the operating - /// system itself will be used (typically something like "Save As" or "Open") - /// - public string Title - { - get - { - // Avoid returning a null string - return String.Empty instead. - return _title.Value == null ? String.Empty : _title.Value; - } - set - { - - _title.Value = value; - } - } - - // If false, the file dialog boxes will allow invalid characters in the returned file name. - // We are actually responsible for dealing with this flag - it determines whether all of the - // processing in ProcessFileNames (which includes things such as the AddExtension feature) - // occurs. - /// - /// Gets or sets a value indicating whether the dialog box accepts only valid - /// Win32 file names. - /// - public bool ValidateNames - { - get - { - return !GetOption(NativeMethods.OFN_NOVALIDATE); - } - set - { - - SetOption(NativeMethods.OFN_NOVALIDATE, !value); - } - } - #endregion Public Properties //--------------------------------------------------- @@ -527,14 +396,6 @@ public bool ValidateNames // //--------------------------------------------------- #region Public Events - - /// - /// Occurs when the user clicks on the Open or Save button on a file dialog - /// box. - /// - // We fire this event from DoFileOk. - public event CancelEventHandler FileOk; - #endregion Public Events //--------------------------------------------------- @@ -543,434 +404,6 @@ public bool ValidateNames // //--------------------------------------------------- #region Protected Methods - - /// - /// Defines the common dialog box hook procedure that is overridden to add - /// specific functionality to the file dialog box. - /// - protected override IntPtr HookProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam) - { - // Assume we are successful unless we encounter a problem. - IntPtr returnValue = IntPtr.Zero; - - // Our File Dialogs are Explorer-style dialogs with hook procedure enabled - // (OFN_ENABLEHOOK | OFN_EXPLORER). As such, we will get the following - // messages: (as per MSDN) - // - // WM_INITDIALOG - // WM_NOTIFY (indicating actions taken by the user or other dialog box events) - // Messages for any additional controls defined by specifying a child dialog template - switch ((WindowMessage)msg) - { - case WindowMessage.WM_NOTIFY: - // Our hookproc is actually the hook procedure for a child template hosted - // inside the actual file dialog box. We want the hwnd of the actual dialog, - // so we call GetParent on the hwnd passed to the hookproc. - _hwndFileDialog = UnsafeNativeMethods.GetParent(new HandleRef(this, hwnd)); - - // When we receive WM_NOTIFY, lParam is a pointer to an OFNOTIFY - // structure that defines the action. OFNOTIFY is a structure - // specific to file open and save dialogs with three members: - // (defined in Commdlg.h - see MSDN for more details) - // - // struct _OFNOTIFY { - // NMHDR hdr; // this is a by-value structure; - // // the implementation in UnsafeNativeMethods breaks it into - // // hdr_hwndFrom (HWND, handle to control sending message), - // // hdr_idFrom (UINT, ID of control sending message) and - // // hdr_code (UINT, one of the CDN_??? notification constants) - // - // LPOPENFILENAME lpOFN; // pointer to the OPENFILENAME structure we created in - // // RunFileDialog when showing this dialog box. - // - // LPTSTR pszFile; // if a network sharing violation has occurred, this - // // is the name of the file affected. Only valid with - // // hdr_code = CDN_SHAREVIOLATION. - // } - // - // Convert the pointer to our OFNOTIFY stored in lparam to an object using PtrToStructure. - NativeMethods.OFNOTIFY notify = Marshal.PtrToStructure(lParam); - - // WM_NOTIFY indicates that the dialog is sending us a notification message. - // notify.hdr_code is an int defining which notification is being received. - // These codes are integer constants defined originally in commdlg.h. - switch (notify.hdr_code) - { - case NativeMethods.CDN_INITDONE: - // CDN_INITDONE is sent by Explorer-style file dialogs when the - // system has finished arranging the controls in the dialog box. - // - // We use this opportunity to move the dialog box to the center - // of the appropriate monitor. - // - // As an aside, this only seems to work the first time we show - // a dialog - after that, Windows remembers the position of the - // dialog. But that's the Winforms behavior too, so it's fine. - - MoveToScreenCenter(new HandleRef(this, _hwndFileDialog)); - break; - - - case NativeMethods.CDN_SELCHANGE: - // CDN_SELCHANGE is sent by Explorer-style file dialogs when the - // selection changes in the list box that displays the contents - // of the currently opened folder or directory. - // - // When we get this message, we check to make sure our character - // buffer is big enough to hold all of the filenames that have - // been selected. If it isn't, we create a new, bigger buffer - // and substitute it in the OPENFILENAME structure. - - // Retrieve the OPENFILENAME structure from the OFNOTIFY structure - // so we can access the CharBuffer inside it. - NativeMethods.OPENFILENAME_I ofn = Marshal.PtrToStructure(notify.lpOFN); - - - // Get the buffer size required to store the selected file names. - // We would like to accomplish this by sending a CDM_GETFILEPATH message - // - to which the file dialog responds with the number of unicode - // characters needed to store the file names and paths. - // - // Windows Forms used CDM_GETSPEC here, but that only retrieves the length - // of the filenames - not of the complete path. So in cases with network - // shortcuts and dereference links enabled, we end up with not enough buffer - // and an FNERR_BUFFERTOOSMALL error. - // - // Unfortunately, CDM_GETFILEPATH returns -1 when a bunch of files are - // selected, so changing to it actually makes things worse with very large - // cases. So we'll stick with CDM_GETSPEC plus extra buffer space. - // - int sizeNeeded = (int)UnsafeNativeMethods.UnsafeSendMessage(_hwndFileDialog, // hWnd of window to receive message - (WindowMessage)NativeMethods.CDM_GETSPEC, // Msg (message to send) - IntPtr.Zero, // wParam (additional info) - IntPtr.Zero); // lParam (additional info) - - if (sizeNeeded > ofn.nMaxFile) - { - // A bigger buffer is required, so we'll allocate a new - // CharBuffer and substitute it for the existing one. - - //try - //{ - // Make the new buffer equal to the size the dialog told us we needed - // plus a reasonable growth factor. - int newBufferSize = sizeNeeded + (FILEBUFSIZE / 4); - - // Allocate a new CharBuffer in the appropriate size. - CharBuffer charBufferTmp = CharBuffer.CreateBuffer(newBufferSize); - - // Allocate unmanaged memory for the buffer and store the pointer. - IntPtr newBuffer = charBufferTmp.AllocCoTaskMem(); - - // Free the old, smaller buffer stored in ofn.lpstrFile - Marshal.FreeCoTaskMem(ofn.lpstrFile); - - // Substitute buffer and update the buffer maximum size in - // the dialog. - ofn.lpstrFile = newBuffer; - ofn.nMaxFile = newBufferSize; - - // Store the reference to the character buffer inside our - // class so we can free it when we're done. - this._charBuffer = charBufferTmp; - - // Marshal the OPENFILENAME structure back into the - // OFNOTIFY structure, then marshal the OFNOTIFY structure - // back into lparam to update the dialog. - Marshal.StructureToPtr(ofn, notify.lpOFN, true); - Marshal.StructureToPtr(notify, lParam, true); - // } - // Windows Forms had a catch-all exception handler here - // but no justification for why it existed. If exceptions - // are thrown when we grow the buffer, re-add this catch - // and perform handling specific to the exception you are seeing. - // - // I don't see anywhere an exception would be thrown that - // we would want to simply discard in this try block, so - // we'll remove this catch and let any exceptions through. - // - // catch (Exception) - // { - // intentionally not throwing here. - // } - } - break; - - - case NativeMethods.CDN_SHAREVIOLATION: - // CDN_SHAREVIOLATION is sent by Explorer-style boxes when OK is clicked - // and a network sharing violation occurs for the selected file. - // Network sharing violation is a bit misleading of a term - it could - // also mean the user doesn't have permissions for the file, or it could mean - // the file is already opened by another process on the same machine. - // - // We process this message because of some odd behavior seen when a file - // is locked for writing. (for details, see VS Whidbey 95342) - // - // We get this notification followed by *two* CDN_FILEOK notifications... but only - // if the path is entered in the textbox and not selected from the folder view. - // - // If we get a CDN_SHAREVIOLATION, we'll set a flag and a counter so we can track - // which CDN_FILEOK notification we're on to avoid showing two message boxes. - this._ignoreSecondFileOkNotification = true; // We want to ignore the second CDN_FILEOK - this._fileOkNotificationCount = 0; // to avoid a second prompt by PromptFileOverwrite. - break; - - - case NativeMethods.CDN_FILEOK: - // CDN_FILEOK is sent when the user specifies a filename and clicks OK. - // We need to process the files selected and make sure everything's acceptable. - // If it's all OK, we don't need to do anything. - // - // To tell the dialogs to stay open after we receive a CDN_FILEOK, we must both - // return a non-zero value from this hook procedure and call SetWindowLong to - // set a nonzero value for DWL_MSGRESULT. - - - // --- Begin VS Whidbey 95342 Workaround --- - // See the CDN_SHAREVIOLATION case above for background info about this issue. - if (this._ignoreSecondFileOkNotification) - { - // We got a CDN_SHAREVIOLATION notification and want to ignore the second CDN_FILEOK notification. - // We'll allow the first one through and block the second. - // Recall that we initialize _fileOkNotificationCount to 0 when we get the CDN_SHAREVIOLATION. - if (this._fileOkNotificationCount == 0) - { - // This is the first CDN_FILEOK, record that we received - // it and then allow DoFileOk to be called. - this._fileOkNotificationCount = 1; - } - else - { - // This is the second CDN_FILEOK, so we want to ignore it. - this._ignoreSecondFileOkNotification = false; - - // Call SetWindowLong to set the DWL_MSGRESULT value of the file dialog window - // to a non-zero number to tell the dialog to stay open. - // NativeMethods.InvalidIntPtr is defined as -1. - UnsafeNativeMethods.CriticalSetWindowLong(new HandleRef(this, hwnd), // hWnd (which window are we affecting) - NativeMethods.DWL_MSGRESULT, // nIndex (which value are we setting) - NativeMethods.InvalidIntPtr); // dwNewLong (what is the new value) - - // We also need to return a non-zero value to tell the dialog to stay open. - returnValue = NativeMethods.InvalidIntPtr; - break; - } - } - // --- End VS Whidbey 95342 Workaround --- - - // Call DoFileOk to check if the files that have been selected - // are acceptable. (See DoFileOk for details.) - // - // If it returns false, we must notify the dialog box that it - // needs to stay open for further input. - if (!DoFileOk(notify.lpOFN)) - { - // Call SetWindowLong to set the DWL_MSGRESULT value of the file dialog window - // to a non-zero number to tell the dialog to stay open. - // NativeMethods.InvalidIntPtr is defined as -1. - UnsafeNativeMethods.CriticalSetWindowLong(new HandleRef(this, hwnd), // hWnd (which window are we affecting) - NativeMethods.DWL_MSGRESULT, // nIndex (which value are we setting) - NativeMethods.InvalidIntPtr); // dwNewLong (what is the new value) - - // We also need to return a non-zero value to tell the dialog to stay open. - returnValue = NativeMethods.InvalidIntPtr; - break; - } - break; - } - break; - default: - returnValue = base.HookProc(hwnd, msg, wParam, lParam); - break; - } - - // Return IntPtr.Zero to indicate success, unless we have - // adjusted the return value elsewhere in the function. - return returnValue; - } - - /// - /// Raises the System.Windows.FileDialog.FileOk event. - /// - protected void OnFileOk(CancelEventArgs e) - { - if (FileOk != null) - { - FileOk(this, e); - } - } - - // Because this class, FileDialog, is the parent class for both OpenFileDialog - // and SaveFileDialog, this function will perform the common setup tasks - // shared between Open and Save, and will then call RunFileDialog, which is - // overridden in both of the derived classes to show the correct dialog. - // Both derived classes know about the COM IFileDialog interfaces and can - // display those if they're available and there aren't any properties set - // that should cause us not to. - // - /// - /// Performs initialization work in preparation for calling RunFileDialog - /// to show a file open or save dialog box. - /// - protected override bool RunDialog(IntPtr hwndOwner) - { - // Verify thread access here. Generally we'd want to enforce thread affinity - // but barring that we really don't want to let multiple instances of this object - // display native dialogs. - // On XP, we have a buffer used in the hook that can get corrupted with multi-thread access. - - if (UseVistaDialog) - { - return RunVistaDialog(hwndOwner); - } - return RunLegacyDialog(hwndOwner); - } - - /// - /// Performs initialization work in preparation for calling RunFileDialog - /// to show a file open or save dialog box. - /// - private bool RunLegacyDialog(IntPtr hwndOwner) - { - // Once we run the dialog, all of our communication with it is handled - // by processing WM_NOTIFY messages in our hook procedure, this.HookProc. - // NativeMethods.WndProc is a delegate with the appropriate signature - // needed for a Win32 window hook procedure. - NativeMethods.WndProc hookProcPtr = new NativeMethods.WndProc(this.HookProc); - - // Create a new OPENFILENAME structure. OPENFILENAME is a structure defined - // in Win32's commdlg.h that contains most of the information needed to - // successfully display a file dialog box. - // NOTE: Despite the name, OPENFILENAME is the proper structure for both - // file open and file save dialogs. - NativeMethods.OPENFILENAME_I ofn = new NativeMethods.OPENFILENAME_I(); - - // do everything in a try block, so we always free memory in the finalizer - try - { - // Create an appropriately sized buffer to hold the filenames. - // The buffer's initial size is controlled by the FILEBUFSIZE constant, - // an arbitrary value chosen so that we will rarely have to grow the buffer. - _charBuffer = CharBuffer.CreateBuffer(FILEBUFSIZE); - - // If we have a filename stored in our internal array _fileNames, - // place it in the buffer as a default filename. - if (_fileNames != null) - { - _charBuffer.PutString(_fileNames[0]); - } - - // --- Set up the OPENFILENAME structure --- - - // lStructSize - // Specifies the length, in bytes, of the structure. - ofn.lStructSize = Marshal.SizeOf(typeof(NativeMethods.OPENFILENAME_I)); - - // hwndOwner - // Handle to the window that owns the dialog box. This member can be any - // valid window handle, or it can be NULL if the dialog box has no owner. - ofn.hwndOwner = hwndOwner; - - // hInstance - // This property is ignored unless OFN_ENABLETEMPLATEHANDLE or - // OFN_ENABLETEMPLATE are set. Since we do not set either, - // hInstance is ignored, so we can set it to zero. - ofn.hInstance = IntPtr.Zero; - - // lpstrFilter - // Pointer to a buffer containing pairs of null-terminated filter strings. - // The last string in the buffer must be terminated by two NULL characters. - // Since our filter strings are stored terminated by vertical bar '|' chars, - // we call MakeFilterString to reformat and validate the filter string. - ofn.lpstrFilter = MakeFilterString(_filter, this.DereferenceLinks); - - // nFilterIndex - // Specifies the index of the currently selected filter in the File Types - // control. Note that since 0 is reserved for a custom filter (which we - // do not support), our valid filter indexes begin at 1. - ofn.nFilterIndex = _filterIndex; - - // lpstrFile - // Pointer to a buffer used to store filenames. When initializing the - // dialog, this name is used as an initial value in the File Name edit - // control. When files are selected and the function returns, the buffer - // contains the full path to every file selected. - ofn.lpstrFile = _charBuffer.AllocCoTaskMem(); - - // nMaxFile - // Size of the lpstrFile buffer in number of Unicode characters. - ofn.nMaxFile = _charBuffer.Length; - - // lpstrInitialDir - // Pointer to a null terminated string that can specify the initial directory. - // A relatively complex algorithm is used to determine which directory is - // actually used as the initial directory - for details, see MSDN for the - // OPENFILENAME structure. - ofn.lpstrInitialDir = _initialDirectory.Value; - - // lpstrTitle - // Pointer to a string to be placed in the title bar of the dialog box. - // NULL causes the title bar to display the operating system default string. - ofn.lpstrTitle = _title.Value; - - // Flags - // A set of bit flags you can use to initialize the dialog box. - // Most of these will be set through public properties that then call - // GetOption or SetOption. We retrieve the flags using the Options property - // and then add three additional flags here: - // - // OFN_EXPLORER - // display an Explorer-style box (newer style) - // OFN_ENABLEHOOK - // enable the hook procedure (important for much of our functionality) - // OFN_ENABLESIZING - // allow the user to resize the dialog box - // - ofn.Flags = Options | (NativeMethods.OFN_EXPLORER | - NativeMethods.OFN_ENABLEHOOK | - NativeMethods.OFN_ENABLESIZING); - - // lpfnHook - // Pointer to the hook procedure. - // Ignored unless OFN_ENABLEHOOK is set in Flags. - ofn.lpfnHook = hookProcPtr; - - // FlagsEx - // Can be either zero or OFN_EX_NOPLACESBAR, depending on whether - // the Places Bar (My Computer/Favorites/etc) should be shown on the - // left side of the file dialog. - ofn.FlagsEx = NativeMethods.OFN_USESHELLITEM; - - // lpstrDefExt - // Pointer to a buffer that contains the default extension; it will - // be appended to filenames if the user does not type an extension. - // Only the first three characters are appended by Windows. If this - // is NULL, no extension is appended. - if (_defaultExtension != null && AddExtension) - { - ofn.lpstrDefExt = _defaultExtension; - } - - // Call into either OpenFileDialog or SaveFileDialog to show the - // actual dialog box. This call blocks until the dialog is closed; - // while dialog is open, all interaction is through HookProc. - return RunFileDialog(ofn); - } - finally - { - // Explicitly set the character buffer to null. - _charBuffer = null; - - // If there is still a pointer to a memory location in - // ofn.lpstrFile, we explicitly free that memory here. - if (ofn.lpstrFile != IntPtr.Zero) - { - Marshal.FreeCoTaskMem(ofn.lpstrFile); - } - } - } - #endregion Protected Methods //--------------------------------------------------- @@ -980,74 +413,6 @@ private bool RunLegacyDialog(IntPtr hwndOwner) //--------------------------------------------------- #region Internal Methods - /// - /// Returns the state of the given options flag. - /// - internal bool GetOption(int option) - { - return (_dialogOptions.Value & option) != 0; - } - - /// - /// Sets the given option to the given boolean value. - /// - internal void SetOption(int option, bool value) - { - if (value) - { - // if value is true, bitwise OR the option with _dialogOptions - _dialogOptions.Value |= option; - } - else - { - // if value is false, AND the bitwise complement of the - // option with _dialogOptions - _dialogOptions.Value &= ~option; - } - } - - /// - /// Prompts the user with a System.Windows.MessageBox - /// with the given parameters. It also ensures that - /// the focus is set back on the window that had - /// the focus to begin with (before we displayed - /// the MessageBox). - /// - /// Returns the choice the user made in the message box - /// (true if MessageBoxResult.Yes, - /// false if OK or MessageBoxResult.No) - /// - /// We have to do this instead of just calling MessageBox because - /// of an issue where keyboard navigation would fail after showing - /// a message box. See http://bugcheck/default.asp?URL=/Bugs/URT/84016.asp - /// (WinForms ASURT 80262) - /// - internal bool MessageBoxWithFocusRestore(string message, - MessageBoxButton buttons, - MessageBoxImage image) - { - bool ret = false; - - // Get the window that currently has focus and temporarily cache a handle to it - IntPtr focusHandle = UnsafeNativeMethods.GetFocus(); - - try - { - // Show the message box and compare the return value to MessageBoxResult.Yes to get the - // actual return value. - ret = (MessageBox.Show(message, DialogCaption, buttons, image, MessageBoxResult.OK /*default button is OK*/, 0) - == - MessageBoxResult.Yes); - } - finally - { - // Return focus to the window that had focus before we showed the messagebox. - // SetFocus can handle improper hwnd values, including null. - UnsafeNativeMethods.SetFocus(new HandleRef(this, focusHandle)); - } - return ret; - } - /// /// PromptUserIfAppropriate is a virtual function that shows any prompt /// message boxes (like "Do you want to overwrite this file") necessary after @@ -1062,7 +427,7 @@ internal bool MessageBoxWithFocusRestore(string message, /// its unique properties. /// /// For FileDialog: - /// If OFN_FILEMUSTEXIST is set, we check to be sure the path passed in on the + /// If FOS_FILEMUSTEXIST is set, we check to be sure the path passed in on the /// fileName parameter exists as an actual file on the hard disk. If so, we /// call PromptFileNotFound to inform the user that they must select an actual /// file that already exists. @@ -1072,8 +437,8 @@ internal virtual bool PromptUserIfAppropriate(string fileName) bool fileExists = true; // The only option we deal with in this implementation of - // PromptUserIfAppropriate is OFN_FILEMUSTEXIST. - if (GetOption(NativeMethods.OFN_FILEMUSTEXIST)) + // PromptUserIfAppropriate is FOS_FILEMUSTEXIST. + if (GetOption(FOS.FILEMUSTEXIST)) { try { @@ -1099,11 +464,6 @@ internal virtual bool PromptUserIfAppropriate(string fileName) return fileExists; } - /// - /// Implements the actual call to GetOpenFileName or GetSaveFileName. - /// - internal abstract bool RunFileDialog(NativeMethods.OPENFILENAME_I ofn); - #endregion Internal Methods //--------------------------------------------------- @@ -1112,6 +472,7 @@ internal virtual bool PromptUserIfAppropriate(string fileName) // //--------------------------------------------------- #region Internal Properties + /// /// In cases where we need to return an array of strings, we return /// a clone of the array. We also need to make sure we return a @@ -1132,7 +493,6 @@ internal string[] FileNamesInternal } } - #endregion Internal Properties //--------------------------------------------------- @@ -1150,269 +510,26 @@ internal string[] FileNamesInternal //--------------------------------------------------- #region Private Methods - /// - /// Processes the CDN_FILEOK notification, which is sent by an - /// Explorer-style Open or Save As dialog box when the user specifies - /// a file name and clicks the OK button. - /// - /// - /// true if the dialog can close, or false if we need to return to - /// the dialog for additional input. - /// - private bool DoFileOk(IntPtr lpOFN) - { - NativeMethods.OPENFILENAME_I ofn = Marshal.PtrToStructure(lpOFN); - - // While processing the results we get from the OPENFILENAME struct, - // we will adjust several properties of our own class to reflect the - // new data. In case we discover we need to send the user back to - // the dialog for further input, we need to be able to revert these - // changes - so we backup _dialogOptions, _filterIndex and _fileNames. - // - // We only assign brand new string arrays to _FileNames, so it's OK - // to back up by reference here. - int saveOptions = _dialogOptions.Value; - int saveFilterIndex = _filterIndex; - string[] saveFileNames = _fileNames; - - // ok is a flag to determine whether we need to show the dialog - // again (false) or if we're satisfied with the results we received (true). - bool ok = false; - - try - { - // Replace the ReadOnly flag in DialogOptions with the ReadOnly flag - // from the OPENFILEDIALOG structure - that is, store the user's - // choice from the Read Only checkbox so our property is up to date. - _dialogOptions.Value = _dialogOptions.Value & ~NativeMethods.OFN_READONLY | - ofn.Flags & NativeMethods.OFN_READONLY; - - // Similarly, update the filterIndex to reflect the selected filter. - _filterIndex = ofn.nFilterIndex; - - // Ask the character buffer to copy the memory from the location - // referenced by lpstrFile into our internal character buffer. - _charBuffer.PutCoTaskMem(ofn.lpstrFile); - - if (!GetOption(NativeMethods.OFN_ALLOWMULTISELECT)) - { - // Since we're selecting a single file, make a string - // array with a single element containing the entire contents - // of the character buffer. - _fileNames = new string[] { _charBuffer.GetString() }; - } - else - { - // Multiselect is a bit more complex - call GetMultiselectFiles - // to handle that case. - _fileNames = GetMultiselectFiles(_charBuffer); - } - - // Call ProcessFileNames() to do validation and post-processing - // tasks (see that function for details; it checks if files exist, - // prompts users with message boxes if invalid selections are made, etc.) - if (ProcessFileNames()) - { - // ProcessFileNames returned true, so it's OK to fire the - // OnFileOk event. - CancelEventArgs ceevent = new CancelEventArgs(); - OnFileOk(ceevent); - - // We allow our calling code to do even more post-processing - // through the OnFileOk event - and therefore offer them the - // opportunity to redisplay the dialog for additional input - // using the event arguments if their validation failed. - // - // If OnFileOk is not handled, ceevent.Cancel will be false. - ok = !ceevent.Cancel; - } - } - finally - { - // No matter what happened, we need to restore dialog state - // if the result was not ok=true. - if (!ok) - { - _dialogOptions.Value = saveOptions; - _filterIndex = saveFilterIndex; - _fileNames = saveFileNames; - } - } - return ok; - } - - /// - /// Extracts the filename(s) returned by the file dialog. - /// - /// Marked static for perf reasons because this function doesn't - /// actually access any instance data as per FxCop CA1822. - private static string[] GetMultiselectFiles(CharBuffer charBuffer) - { - // Iff OFN_ALLOWMULTISELECT is set for an Explorer-style dialog box - // and the user selects multiple files, lpstrFile points to a string - // containing the current directory, followed by a NULL, followed by - // two or more filenames that are NULL separated, with an extra NULL - // character after the last filename. - // - // We'll use the GetString() function of the character buffer to get - // two of these null-terminated chunks at a time, one into directory - // and one into filename. - string directory = charBuffer.GetString(); - string fileName = charBuffer.GetString(); - - // If OFN_ALLOWMULTISELECT is enabled but the user selects only - // one file, we get the filename and path concatenated together without - // a null separator. This will cause our directory variable to - // contain the full path and fileName to be empty, so make a new - // string array with the contents of directory as its single element. - // - if (fileName.Length == 0) - { - return new string[] { directory }; - } - - // If the directory was provided without a directory separator - // character (typically '\' on Windows) at the end, we add it. - if (!directory.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal)) - { - directory = directory + Path.DirectorySeparatorChar; - } - - // Create a generic list of strings to hold the names. - List names = new List(); - - do - { - // With DereferenceLinks enabled, we can sometimes end - // up with full paths provided as filenames. We need - // to check for two cases here - the case where the - // filename begins with '\', indicating a UNC share path, - // or the case where we have a full hard disk path - // (e.g. C:\file.txt), where [1] will be the : volume - // separator and [2] will be the \ directory separator. - - bool isUncPath = (fileName[0] == Path.DirectorySeparatorChar && fileName[1] == Path.DirectorySeparatorChar); - - bool isFullPath = (fileName.Length > 3 && - fileName[1] == Path.VolumeSeparatorChar && - fileName[2] == Path.DirectorySeparatorChar); - - if (!(isUncPath || isFullPath)) - { - // filename is not a full path, so we need to - // add on the directory - fileName = directory + fileName; - } - - names.Add(fileName); - - // Get the next filename - fileName = charBuffer.GetString(); - } while (!String.IsNullOrEmpty(fileName)); - - return names.ToArray(); - } - // Provides the actual implementation of initialization tasks. // Initialize() is called from both the constructor and the // public Reset() function to set default values for member // variables and for the options bitmask. private void Initialize() { - // - // Initialize Options Flags - // - _dialogOptions.Value = 0; // _dialogOptions is an int containing a set of - // bit flags used to initialize the dialog box. - // It is placed directly into the OPENFILEDIALOG - // struct used to instantiate the file dialog box. - // Within our code, we only use GetOption and SetOption - // (change from Windows Forms, which sometimes directly - // modified _dialogOptions). As such, we initialize to 0 - // here and then call SetOption to get _dialogOptions - // into the default state. - - // - // Set some default options - // - // - Hide the Read Only check box. - SetOption(NativeMethods.OFN_HIDEREADONLY, true); - - // - Specifies that the user can type only valid paths and file names. If this flag is - // used and the user types an invalid path and file name in the File Name entry field, - // we will display a warning in a message box. - SetOption(NativeMethods.OFN_PATHMUSTEXIST, true); - - // - This is our own flag, not a standard one defined in OPENFILEDIALOG. We use this to - // indicate to ourselves that we should add the default extension automatically if the + // - Specifies that we should add the default extension automatically if the // user does not enter it in themselves in ProcessFileNames. (See that function for // details.) - SetOption(OPTION_ADDEXTENSION, true); - + AddExtension = true; // // Initialize additional properties - // - _title.Value = null; - _initialDirectory.Value = null; - _defaultExtension = null; + // _fileNames = null; + _defaultExtension = null; _filter = null; _filterIndex = 1; // The index of the first filter entry is 1, not 0. // 0 is reserved for the custom filter functionality // provided by Windows, which we do not expose to the user. - - // Variables used for bug workaround: - // When the selected file is locked for writing, we get a sharing violation notification - // followed by *two* CDN_FILEOK notifications. These flags are used to track the multiple - // notifications so we only show one error message box to the user. - // For a more complete explanation and PS bug information, see HookProc. - _ignoreSecondFileOkNotification = false; - _fileOkNotificationCount = 0; - - // Set this to an empty list so callers can simply add to it. They can also replace it wholesale. - CustomPlaces = new List(); - } - - /// - /// Converts the given filter string to the format required in an OPENFILENAME_I - /// structure. - /// - private static string MakeFilterString(string s, bool dereferenceLinks) - { - if (String.IsNullOrEmpty(s)) - { - // Workaround for VSWhidbey bug #95338 (carried over from Winforms implementation) - // Apparently, when filter is null, the common dialogs in Windows XP will not dereference - // links properly. The work around is to provide a default filter; " |*.*" is used to - // avoid localization issues from description text. - // - // This behavior is now documented in MSDN on the OPENFILENAME structure, so I don't - // expect it to change anytime soon. - if (dereferenceLinks && System.Environment.OSVersion.Version.Major >= 5) - { - s = " |*.*"; - } - else - { - // Even if we don't need the bug workaround, change empty - // strings into null strings. - return null; - } - } - - StringBuilder nullSeparatedFilter = new StringBuilder(s); - - // Replace the vertical bar with a null to conform to the Windows - // filter string format requirements - nullSeparatedFilter.Replace('|', '\0'); - - // Append two nulls at the end - nullSeparatedFilter.Append('\0'); - nullSeparatedFilter.Append('\0'); - - // Return the results as a string. - return nullSeparatedFilter.ToString(); } /// @@ -1423,8 +540,8 @@ private static string MakeFilterString(string s, bool dereferenceLinks) /// private bool ProcessFileNames() { - // Only process the filenames if OFN_NOVALIDATE is not set. - if (!GetOption(NativeMethods.OFN_NOVALIDATE)) + // Only process the filenames if FOS_NOVALIDATE is not set. + if (!GetOption(FOS.NOVALIDATE)) { // Call the FilterExtensions private property to get // a list of valid extensions from the filter(s). @@ -1476,9 +593,9 @@ private bool ProcessFileNames() newFilename = string.Concat(fileName.AsSpan(0, fileName.Length - currentExtension.Length), ".", extensions[j]); } - // If OFN_FILEMUSTEXIST is not set, or if it is set but the filename we generated + // If FOS_FILEMUSTEXIST is not set, or if it is set but the filename we generated // does in fact exist, we update fileName and stop trying new extensions. - if (!GetOption(NativeMethods.OFN_FILEMUSTEXIST) || File.Exists(newFilename)) + if (!GetOption(FOS.FILEMUSTEXIST) || File.Exists(newFilename)) { fileName = newFilename; break; @@ -1548,41 +665,6 @@ private string CriticalFileName } } } - /// - /// Gets a string containing the title of the file dialog. - /// - // When showing message boxes onscreen, we want them to have the - // same title bar as the file open or save dialog itself. We can't - // just use the Title property, because if it's null the operating - // system substitutes a standard localized title. - // - // The solution is this private property, which returns the title of the - // file dialog (using the stored handle of the dialog _hwndFileDialog to - // call GetWindowText). - // - // It is designed to only be called by MessageBoxWithFocusRestore. - private string DialogCaption - { - get - { - if (!UnsafeNativeMethods.IsWindow(new HandleRef(this, _hwndFileDialog))) - { - return String.Empty; - } - - // Determine the length of the text we want to retrieve... - int textLen = UnsafeNativeMethods.GetWindowTextLength(new HandleRef(this, _hwndFileDialog)); - // then make a StringBuilder... - StringBuilder sb = new StringBuilder(textLen + 1); - // and call GetWindowText to fill it up... - UnsafeNativeMethods.GetWindowText(new HandleRef(this, _hwndFileDialog), - sb /*target string*/, - sb.Capacity /* max # of chars to copy before truncation occurs */ - ); - // then return the results. - return sb.ToString(); - } - } /// /// Extracts the file extensions specified by the current file filter into @@ -1663,136 +745,25 @@ private string[] GetFilterExtensions() return extensions.ToArray(); } - /// - /// Gets an integer representing the Win32 common Open File Dialog OFN_* option flags - /// used to display a dialog with the current set of property values. - /// - // - // We bitwise AND _dialogOptions with all of the options we consider valid - // before returning the resulting bitmask to avoid accidentally setting a - // flag we don't intend to. Note that this list doesn't include a few of the - // flags we set right before showing the dialog in RunDialog (like - // NativeMethods.OFN_EXPLORER), since those are only added when creating - // the OPENFILENAME structure. - // - // Also note that our private flags are not included in this list (like - // OPTION_ADDEXTENSION) - protected int Options - { - get - { - return _dialogOptions.Value & (NativeMethods.OFN_READONLY | NativeMethods.OFN_HIDEREADONLY | - NativeMethods.OFN_NOCHANGEDIR | NativeMethods.OFN_NOVALIDATE | - NativeMethods.OFN_ALLOWMULTISELECT | NativeMethods.OFN_PATHMUSTEXIST | - NativeMethods.OFN_NODEREFERENCELINKS); - } - } - #endregion Private Properties #region Vista COM interfaces Augmentation - /// - /// Events sink for IFileDialog. MSDN says to return E_NOTIMPL for several, but not all, of these methods when we don't want to support them. - /// - /// - /// Be sure to explictly Dispose of it, or use it in a using block. Unadvise happens as a result of Dispose. - /// - private sealed class VistaDialogEvents : IFileDialogEvents, IDisposable - { - public delegate bool OnOkCallback(IFileDialog dialog); - - private IFileDialog _dialog; - - private OnOkCallback _okCallback; - uint _eventCookie; - - public VistaDialogEvents(IFileDialog dialog, OnOkCallback okCallback) - { - _dialog = dialog; - _eventCookie = dialog.Advise(this); - _okCallback = okCallback; - } - - HRESULT IFileDialogEvents.OnFileOk(IFileDialog pfd) - { - return _okCallback(pfd) ? HRESULT.S_OK : HRESULT.S_FALSE; - } - - HRESULT IFileDialogEvents.OnFolderChanging(IFileDialog pfd, IShellItem psiFolder) - { - return HRESULT.E_NOTIMPL; - } - - HRESULT IFileDialogEvents.OnFolderChange(IFileDialog pfd) - { - return HRESULT.S_OK; - } - - HRESULT IFileDialogEvents.OnSelectionChange(IFileDialog pfd) - { - return HRESULT.S_OK; - } - - HRESULT IFileDialogEvents.OnShareViolation(IFileDialog pfd, IShellItem psi, out FDESVR pResponse) - { - pResponse = FDESVR.DEFAULT; - return HRESULT.S_OK; - } - - HRESULT IFileDialogEvents.OnTypeChange(IFileDialog pfd) - { - return HRESULT.S_OK; - } - - HRESULT IFileDialogEvents.OnOverwrite(IFileDialog pfd, IShellItem psi, out FDEOR pResponse) - { - pResponse = FDEOR.DEFAULT; - return HRESULT.S_OK; - } - - void IDisposable.Dispose() - { - _dialog.Unadvise(_eventCookie); - } - } - - public IList CustomPlaces { get; set; } - #region Internal and Protected Methods - // These methods are intended to be internal AND protected, but C# doesn't allow that declaration. - - internal abstract IFileDialog CreateVistaDialog(); - internal abstract string[] ProcessVistaFiles(IFileDialog dialog); + private protected abstract string[] ProcessFiles(IFileDialog dialog); #endregion #region Internal Methods - internal virtual void PrepareVistaDialog(IFileDialog dialog) + private protected override void PrepareDialog(IFileDialog dialog) { - dialog.SetDefaultExtension(DefaultExt); + base.PrepareDialog(dialog); dialog.SetFileName(CriticalFileName); - if (!string.IsNullOrEmpty(InitialDirectory)) - { - IShellItem initialDirectory = ShellUtil.GetShellItemForPath(InitialDirectory); - if (initialDirectory != null) - { - // Setting both of these so the dialog doesn't display errors when a remembered folder is missing. - dialog.SetDefaultFolder(initialDirectory); - dialog.SetFolder(initialDirectory); - } - } - - dialog.SetTitle(Title); - - // Force no mini mode for the SaveFileDialog. - // Only accept physically backed locations. - FOS options = ((FOS)Options & c_VistaFileDialogMask) | FOS.DEFAULTNOMINIMODE | FOS.FORCEFILESYSTEM; - dialog.SetOptions(options); + dialog.SetDefaultExtension(DefaultExt); COMDLG_FILTERSPEC[] filterItems = GetFilterItems(Filter); if (filterItems.Length > 0) @@ -1800,59 +771,19 @@ internal virtual void PrepareVistaDialog(IFileDialog dialog) dialog.SetFileTypes((uint)filterItems.Length, filterItems); dialog.SetFileTypeIndex(unchecked((uint)FilterIndex)); } - - IList places = CustomPlaces; - if (places != null && places.Count != 0) - { - foreach (FileDialogCustomPlace customPlace in places) - { - IShellItem shellItem = ResolveCustomPlace(customPlace); - if (shellItem != null) - { - try - { - dialog.AddPlace(shellItem, FDAP.BOTTOM); - } - catch (ArgumentException) - { - // The dialog doesn't allow some ShellItems to be set as Places (like device ports). - // Silently swallow errors here. - } - } - } - } } #endregion #region Private Methods - private bool UseVistaDialog + private protected override bool HandleFileOk(IFileDialog dialog) { - get { return Environment.OSVersion.Version.Major >= 6; } - } - - private bool RunVistaDialog(IntPtr hwndOwner) - { - IFileDialog dialog = CreateVistaDialog(); - - PrepareVistaDialog(dialog); - - using (VistaDialogEvents events = new VistaDialogEvents(dialog, HandleVistaFileOk)) + if (!base.HandleFileOk(dialog)) { - return dialog.Show(hwndOwner).Succeeded; + return false; } - } - private bool HandleVistaFileOk(IFileDialog dialog) - { - // When this callback occurs, the HWND is visible and we need to - // grab it because it is used for various things like looking up the - // DialogCaption. - UnsafeNativeMethods.IOleWindow oleWindow = (UnsafeNativeMethods.IOleWindow)dialog; - oleWindow.GetWindow(out _hwndFileDialog); - - int saveOptions = _dialogOptions.Value; int saveFilterIndex = _filterIndex; string[] saveFileNames = _fileNames; bool ok = false; @@ -1861,7 +792,7 @@ private bool HandleVistaFileOk(IFileDialog dialog) { uint filterIndexTemp = dialog.GetFileTypeIndex(); _filterIndex = unchecked((int)filterIndexTemp); - _fileNames = ProcessVistaFiles(dialog); + _fileNames = ProcessFiles(dialog); if (ProcessFileNames()) { var cancelArgs = new CancelEventArgs(); @@ -1874,18 +805,8 @@ private bool HandleVistaFileOk(IFileDialog dialog) if (!ok) { _fileNames = saveFileNames; - _dialogOptions.Value = saveOptions; _filterIndex = saveFilterIndex; } - else - { - if (0 != (Options & NativeMethods.OFN_HIDEREADONLY)) - { - // When the dialog is dismissed OK, the Readonly bit can't be left on if ShowReadOnly was false - // Downlevel this happens automatically. On Vista we need to watch out for it. - _dialogOptions.Value &= ~NativeMethods.OFN_READONLY; - } - } } return ok; } @@ -1915,12 +836,6 @@ private static COMDLG_FILTERSPEC[] GetFilterItems(string filter) return extensions.ToArray(); } - private static IShellItem ResolveCustomPlace(FileDialogCustomPlace customPlace) - { - // Use the KnownFolder Guid if it exists. Otherwise use the Path. - return ShellUtil.GetShellItemForPath(ShellUtil.GetPathForKnownFolder(customPlace.KnownFolder) ?? customPlace.Path); - } - #endregion #endregion @@ -1932,22 +847,6 @@ private static IShellItem ResolveCustomPlace(FileDialogCustomPlace customPlace) //--------------------------------------------------- #region Private Fields - private const FOS c_VistaFileDialogMask = FOS.OVERWRITEPROMPT | FOS.NOCHANGEDIR | FOS.NOVALIDATE | FOS.ALLOWMULTISELECT | FOS.PATHMUSTEXIST | FOS.FILEMUSTEXIST | FOS.CREATEPROMPT | FOS.NODEREFERENCELINKS; - - // _dialogOptions is a set of bit flags used to control the behavior - // of the Win32 dialog box. - private SecurityCriticalDataForSet _dialogOptions; - - // These two flags are related to a fix for an issue where Windows - // sends two FileOK notifications back to back after a sharing - // violation occurs. See CDN_SHAREVIOLATION in HookProc for details. - private bool _ignoreSecondFileOkNotification; - private int _fileOkNotificationCount; - - // These private variables store data for the various public properties - // that control the appearance of the file dialog box. - private SecurityCriticalDataForSet _title; // Title bar of the message box - private SecurityCriticalDataForSet _initialDirectory; // Starting directory private string _defaultExtension; // Extension appended first if AddExtension // is enabled private string _filter; // The file extension filters that display @@ -1958,30 +857,11 @@ private static IShellItem ResolveCustomPlace(FileDialogCustomPlace customPlace) // the user selected afterwards.) This // index is 1-based, not 0-based. - // Since we have to interop with native code to show the file dialogs, - // we use the CharBuffer class to help with the marshalling of - // unmanaged memory that stores the user-selected file names. - private CharBuffer _charBuffer; - - // We store the handle of the file dialog inside our class - // for a variety of purposes (like getting the title of the dialog - // box when we need to show a message box with the same title bar caption) - private IntPtr _hwndFileDialog; - // This is the array that stores the filename(s) the user selected in the // dialog box. If Multiselect is not enabled, only the first element // of this array will be used. private string[] _fileNames; - // Constant to control the initial size of the character buffer; - // 8192 is an arbitrary but reasonable size that should minimize the - // number of times we need to grow the buffer. - private const int FILEBUFSIZE = 8192; - - // OPTION_ADDEXTENSION is our own bit flag that we use to control our - // own automatic extension appending feature. - private const int OPTION_ADDEXTENSION = unchecked(unchecked((int)0x80000000)); - #endregion Private Fields } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs index 534d28149aa..7605292b00e 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs @@ -7,7 +7,6 @@ // Description: // OpenFileDialog is a sealed class derived from FileDialog that // implements File Open dialog-specific functions. It contains -// the actual commdlg.dll call to GetOpenFileName() as well as // additional properties relevant only to save dialogs. // @@ -86,7 +85,7 @@ public Stream OpenFile() // tell the user we don't have any files to open. if (String.IsNullOrEmpty(filename)) { - throw new InvalidOperationException(SR.FileNameMustNotBeNull); + throw new InvalidOperationException(SR.Get(SRID.FileNameMustNotBeNull)); } FileStream fileStream = null; @@ -126,7 +125,7 @@ public Stream[] OpenFiles() if (String.IsNullOrEmpty(filename)) { - throw new InvalidOperationException(SR.FileNameMustNotBeNull); + throw new InvalidOperationException(SR.Get(SRID.FileNameMustNotBeNull)); } FileStream fileStream = null; @@ -142,7 +141,7 @@ public Stream[] OpenFiles() } // We override the FileDialog implementation to set a default - // for OFN_FILEMUSTEXIST in addition to the other option flags + // for FOS_FILEMUSTEXIST in addition to the other option flags // defined in FileDialog. /// /// Resets all properties to their default values. @@ -173,9 +172,8 @@ public override void Reset() //--------------------------------------------------- #region Public Properties - // OFN_ALLOWMULTISELECT - // Specifies that the File Name list box allows multiple - // selections. + // FOS_ALLOWMULTISELECT + // Enables the user to select multiple items in the open dialog. // /// /// Gets or sets an option flag indicating whether the @@ -185,43 +183,22 @@ public bool Multiselect { get { - return GetOption(NativeMethods.OFN_ALLOWMULTISELECT); + return GetOption(FOS.ALLOWMULTISELECT); } set { - SetOption(NativeMethods.OFN_ALLOWMULTISELECT, value); + SetOption(FOS.ALLOWMULTISELECT, value); } } - // OFN_READONLY - // Causes the Read Only check box to be selected initially - // when the dialog box is created. This flag indicates the - // state of the Read Only check box when the dialog box is - // closed. - // + // OFN_HIDEREADONLY currently not supported #6346 /// /// Gets or sets a value indicating whether the read-only /// check box is selected. /// - public bool ReadOnlyChecked - { - get - { - return GetOption(NativeMethods.OFN_READONLY); - } - set - { - SetOption(NativeMethods.OFN_READONLY, value); - } - } + public bool ReadOnlyChecked { get; set; } - // - // Our property is the inverse of the Win32 flag, - // OFN_HIDEREADONLY. - // - // OFN_HIDEREADONLY - // Hides the Read Only check box. - // + // OFN_HIDEREADONLY currently not supported #6346 /// /// Gets or sets a value indicating whether the dialog /// contains a read-only check box. @@ -230,14 +207,11 @@ public bool ShowReadOnly { get { - // OFN_HIDEREADONLY is the inverse of our property, - // so negate the results of GetOption... - return !GetOption(NativeMethods.OFN_HIDEREADONLY); + return _showReadOnly; } set { - // ... and SetOption. - SetOption(NativeMethods.OFN_HIDEREADONLY, !value); + _showReadOnly = false; } } @@ -256,17 +230,8 @@ public bool ShowReadOnly // Protected Methods // //--------------------------------------------------- - #region Protected Methods - - /// - /// Demands permissions appropriate to the dialog to be shown. - /// - protected override void CheckPermissionsToShowDialog() - { - base.CheckPermissionsToShowDialog(); - } - - #endregion Protected Methods + // #region Protected Methods + // #endregion Protected Methods //--------------------------------------------------- // @@ -275,78 +240,7 @@ protected override void CheckPermissionsToShowDialog() //--------------------------------------------------- #region Internal Methods - /// - /// Performs the actual call to display a file open dialog. - /// - /// - /// Thrown if there is an invalid filename, if - /// a subclass failure occurs or if the buffer length - /// allocated to store the filenames occurs. - /// - /// - /// The call chain is ShowDialog > RunDialog > - /// RunFileDialog (this function). In - /// FileDialog.RunDialog, we created the OPENFILENAME - /// structure - so all this function needs to do is - /// call GetOpenFileName and process the result code. - /// - internal override bool RunFileDialog(NativeMethods.OPENFILENAME_I ofn) - { - bool result = false; - - // Make the actual call to GetOpenFileName. This function - // blocks on GetOpenFileName until the entire dialog display - // is completed - any interaction we have with the dialog - // while it's open takes place through our HookProc. The - // return value is a bool; true = success. - result = UnsafeNativeMethods.GetOpenFileName(ofn); - - if (!result) // result was 0 (false), so an error occurred. - { - // Something may have gone wrong - check for error conditions - // by calling CommDlgExtendedError to get the specific error. - int errorCode = UnsafeNativeMethods.CommDlgExtendedError(); - - // Throw an appropriate exception if we know what happened: - switch (errorCode) - { - // FNERR_INVALIDFILENAME is usually triggered when an invalid initial filename is specified - case NativeMethods.FNERR_INVALIDFILENAME: - throw new InvalidOperationException(SR.Format(SR.FileDialogInvalidFileName, SafeFileName)); - - case NativeMethods.FNERR_SUBCLASSFAILURE: - throw new InvalidOperationException(SR.FileDialogSubClassFailure); - - // note for FNERR_BUFFERTOOSMALL: - // This error likely indicates a problem with our buffer size growing code; - // take a look at that part of HookProc if customers report this error message is occurring. - case NativeMethods.FNERR_BUFFERTOOSMALL: - throw new InvalidOperationException(SR.FileDialogBufferTooSmall); - - /* - * According to MSDN, the following errors can also occur, but we do not handle them as - * they are very unlikely, and if they do occur, they indicate a catastrophic failure. - * Most are related to features we do not wrap in our implementation. - * - * CDERR_DIALOGFAILURE - * CDERR_FINDRESFAILURE - * CDERR_NOHINSTANCE - * CDERR_INITIALIZATION - * CDERR_NOHOOK - * CDERR_LOCKRESFAILURE - * CDERR_NOTEMPLATE - * CDERR_LOADRESFAILURE - * CDERR_STRUCTSIZE - * CDERR_LOADSTRFAILURE - * CDERR_MEMALLOCFAILURE - * CDERR_MEMLOCKFAILURE - */ - } - } - return result; - } - - internal override string[] ProcessVistaFiles(IFileDialog dialog) + private protected override string[] ProcessFiles(IFileDialog dialog) { var openDialog = (IFileOpenDialog)dialog; if (Multiselect) @@ -368,7 +262,7 @@ internal override string[] ProcessVistaFiles(IFileDialog dialog) } } - internal override IFileDialog CreateVistaDialog() + private protected override IFileDialog CreateDialog() { return (IFileDialog)Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid(CLSID.FileOpenDialog))); } @@ -409,12 +303,12 @@ internal override IFileDialog CreateVistaDialog() // private void Initialize() { - // OFN_FILEMUSTEXIST + // FOS_FILEMUSTEXIST // Specifies that the user can type only names of existing files // in the File Name entry field. If this flag is specified and // the user enters an invalid name, we display a warning in a - // message box. Implies OFN_PATHMUSTEXIST. - SetOption(NativeMethods.OFN_FILEMUSTEXIST, true); + // message box. Implies FOS_PATHMUSTEXIST. + SetOption(FOS.FILEMUSTEXIST, true); } #endregion Private Methods @@ -433,6 +327,9 @@ private void Initialize() // //--------------------------------------------------- //#region Private Fields + + private bool _showReadOnly = false; + //#endregion Private Fields } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFolderDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFolderDialog.cs new file mode 100644 index 00000000000..1e1b23e6f93 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFolderDialog.cs @@ -0,0 +1,326 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// +// +// Description: +// OpenFolderDialog is a sealed class derived from CommonItemDialog that +// implements folder picking-specific functions. It contains +// additional properties relevant only to folder open dialog. +// + +namespace Microsoft.Win32 +{ + using System; + using System.Collections.Generic; + using System.ComponentModel; + using System.IO; + using System.Security; + using System.Text; + using System.Windows; + + using MS.Internal.AppModel; + using MS.Internal.Interop; + using MS.Internal.PresentationFramework; + using MS.Win32; + + /// + /// Represents a common dialog box that allows the user to open one or more file(s). + /// This class cannot be inherited. + /// + public sealed class OpenFolderDialog : CommonItemDialog + { + //--------------------------------------------------- + // + // Constructors + // + //--------------------------------------------------- + #region Constructors + + // We add a constructor for our OpenFolderDialog since we have + // additional initialization tasks to do in addition to the + // ones that CommonItemDialog's constructor takes care of. + /// + /// Initializes a new instance of the OpenFolderDialog class. + /// + public OpenFolderDialog() : base() + { + Initialize(); + } + + #endregion Constructors + + //--------------------------------------------------- + // + // Public Methods + // + //--------------------------------------------------- + #region Public Methods + + // We override the CommonItemDialog implementation to set + // FOS_PICKFOLDERS in addition to the other option flags + // defined in CommonItemDialog. + /// + /// Resets all properties to their default values. + /// + /// + /// Callers must have FileIOPermission(PermissionState.Unrestricted) to call this API. + /// + public override void Reset() + { + base.Reset(); + Initialize(); + } + + /// + /// Returns a string representation of the file dialog with key information + /// for debugging purposes. + /// + // We overload ToString() so that we can provide a useful representation of + // this object for users' debugging purposes. It provides the full pathname for + // any folder selected. + public override string ToString() + { + StringBuilder sb = new StringBuilder(base.ToString() + ", FolderName: "); + sb.Append(FolderName); + return sb.ToString(); + } + + #endregion Public Methods + + //--------------------------------------------------- + // + // Public Properties + // + //--------------------------------------------------- + #region Public Properties + + /// + /// Gets a string containing the filename component of the + /// folder selected in the dialog box. + /// + /// Example: if FolderName = "c:\windows\sytem32" , + /// SafeFolderName = "system32" + /// + public string SafeFolderName + { + get + { + // Use the FileName property to avoid directly accessing + // the _fileNames field, then call Path.GetFileName + // to do the actual work of stripping out the folder name + // from the path. + string safeFN = Path.GetFileName(CriticalFileName); + + // Check to make sure Path.GetFileName does not return null. + // If it does, set safeFN to String.Empty instead to accomodate + // programmers that fail to check for null when reading strings. + if (safeFN == null) + { + safeFN = String.Empty; + } + + return safeFN; + } + } + + /// + /// Gets or sets a string containing the full path of the file selected in + /// the file dialog box. + /// + public string FolderName + { + get + { + return CriticalFileName; + } + set + { + // Allow users to set a filename to stored in _fileNames. + // If null is passed in, we clear the entire list. + // If we get a string, we clear the entire list and make a new one-element + // array with the new string. + if (value == null) + { + _fileNames = null; + } + else + { + _fileNames = new string[] { value }; + } + } + } + + #endregion Public Properties + + //--------------------------------------------------- + // + // Public Events + // + //--------------------------------------------------- + //#region Public Events + //#endregion Public Events + + //--------------------------------------------------- + // + // Protected Methods + // + //--------------------------------------------------- + //#region Protected Methods + //#endregion Protected Methods + + //--------------------------------------------------- + // + // Internal Methods + // + //--------------------------------------------------- + #region Internal Methods + + private string[] ProcessFolders(IFileDialog dialog) + { + var openDialog = (IFileOpenDialog)dialog; + IShellItem item = openDialog.GetResult(); + return new[] { item.GetDisplayName(SIGDN.DESKTOPABSOLUTEPARSING) }; + } + + private protected override IFileDialog CreateDialog() + { + return (IFileDialog)Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid(CLSID.FileOpenDialog))); + } + + private protected override void PrepareDialog(IFileDialog dialog) + { + base.PrepareDialog(dialog); + + dialog.SetFileName(CriticalFileName); + } + + private protected override bool HandleFileOk(IFileDialog dialog) + { + if (!base.HandleFileOk(dialog)) + { + return false; + } + + string[] saveFileNames = _fileNames; + bool ok = false; + + try + { + _fileNames = ProcessFolders(dialog); + + var cancelArgs = new CancelEventArgs(); + OnFileOk(cancelArgs); + ok = !cancelArgs.Cancel; + } + finally + { + if (!ok) + { + _fileNames = saveFileNames; + } + } + return ok; + } + + #endregion Internal Methods + + //--------------------------------------------------- + // + // Internal Properties + // + //--------------------------------------------------- + //#region Internal Properties + //#endregion Internal Properties + + //--------------------------------------------------- + // + // Internal Events + // + //--------------------------------------------------- + //#region Internal Events + //#endregion Internal Events + + //--------------------------------------------------- + // + // Private Methods + // + //--------------------------------------------------- + #region Private Methods + + // Provides the actual implementation of initialization tasks. + // Initialize() is called from both the constructor and the + // public Reset() function to set default values for member + // variables and for the options bitmask. + // + // We only perform OpenFolderDialog() specific reset tasks here; + // it's the calling code's responsibility to ensure that the + // base is initialized first. + // + private void Initialize() + { + // FOS_FILEMUSTEXIST + // Folder must exist. Otherwise, the folder name remaining in + // text box might be returned as a nested folder. This flag is + // now enforced by FOS_PICKFOLDERS. + SetOption(FOS.FILEMUSTEXIST, true); + + // FOS_PICKFOLDERS + // This is a folder selecting dialog. + SetOption(FOS.PICKFOLDERS, true); + } + + #endregion Private Methods + + //--------------------------------------------------- + // + // Private Properties + // + //--------------------------------------------------- + #region Private Properties + + /// + /// Gets a string containing the full path of the file selected in + /// the file dialog box. + /// + private string CriticalFileName + { + get + { + if (_fileNames == null) // No filename stored internally... + { + return String.Empty; // So we return String.Empty + } + else + { + // Return the first filename in the array if it is non-empty. + if (_fileNames[0].Length > 0) + { + return _fileNames[0]; + } + else + { + return String.Empty; + } + } + } + } + + #endregion Private Properties + + //--------------------------------------------------- + // + // Private Fields + // + //--------------------------------------------------- + #region Private Fields + + // This is the array that stores the filename(s) the user selected in the + // dialog box. If Multiselect is not enabled, only the first element + // of this array will be used. + private string[] _fileNames; + + #endregion Private Fields + } +} diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/SaveFileDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/SaveFileDialog.cs index 0b1fc6be4d0..175ede6aa0a 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/SaveFileDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/SaveFileDialog.cs @@ -7,7 +7,6 @@ // Description: // SaveFileDialog is a sealed class derived from FileDialog that // implements File Save dialog-specific functions. It contains -// the actual commdlg.dll call to GetSaveFileName() as well as // additional properties relevant only to save dialogs. // // @@ -82,7 +81,7 @@ public Stream OpenFile() // tell the user we don't have any files to open. if (String.IsNullOrEmpty(filename)) { - throw new InvalidOperationException(SR.FileNameMustNotBeNull); + throw new InvalidOperationException(SR.Get(SRID.FileNameMustNotBeNull)); } // Create a new FileStream from the file and return it. @@ -91,7 +90,7 @@ public Stream OpenFile() // // We override the FileDialog implementation to set a default - // for OFN_FILEMUSTEXIST in addition to the other option flags + // for FOS_FILEMUSTEXIST in addition to the other option flags // defined in FileDialog. /// /// Resets all properties to their default values. @@ -122,11 +121,12 @@ public override void Reset() //--------------------------------------------------- #region Public Properties - // OFN_CREATEPROMPT // If the user specifies a file that does not exist, this flag causes our code // to prompt the user for permission to create the file. If the user chooses // to create the file, the dialog box closes and the function returns the // specified name; otherwise, the dialog box remains open. + // + // We use our own prompt, so not using FOS_CREATEPROMPT (which is for open dialogs only). // /// /// Gets or sets a value indicating whether the dialog box prompts the user for @@ -135,23 +135,13 @@ public override void Reset() /// /// Callers must have UIPermission.AllWindows to call this API. /// - public bool CreatePrompt - { - get - { - return GetOption(NativeMethods.OFN_CREATEPROMPT); - } - set - { - - SetOption(NativeMethods.OFN_CREATEPROMPT, value); - } - } + public bool CreatePrompt { get; set; } - // OFN_OVERWRITEPROMPT // Causes our code to generate a message box if the selected file already // exists. The user must confirm whether to overwrite the file. // + // We use our own prompt, so not using FOS_OVERWRITEPROMPT (for backward compatibility). + // /// /// Gets or sets a value indicating whether the Save As dialog box displays a /// warning if the user specifies a file name that already exists. @@ -159,18 +149,7 @@ public bool CreatePrompt /// /// Callers must have UIPermission.AllWindows to call this API. /// - public bool OverwritePrompt - { - get - { - return GetOption(NativeMethods.OFN_OVERWRITEPROMPT); - } - set - { - - SetOption(NativeMethods.OFN_OVERWRITEPROMPT, value); - } - } + public bool OverwritePrompt { get; set; } #endregion Public Properties @@ -208,8 +187,8 @@ public bool OverwritePrompt /// /// /// We first call the base class implementation to deal with any messages handled there. - /// Then, if OFN_OVERWRITEPROMPT (for a message box if the selected file already exists) - /// or OFN_CREATEPROMPT (for a message box if a file is specified that does not exist) + /// Then, if FOS_OVERWRITEPROMPT (for a message box if the selected file already exists) + /// or FOS_CREATEPROMPT (for a message box if a file is specified that does not exist) /// flags are set, we check to see if it is appropriate to show the dialog(s) in this /// method. If so, we then call PromptFileOverwrite or PromptFileCreate, respectively. /// @@ -227,7 +206,7 @@ internal override bool PromptUserIfAppropriate(string fileName) bool fExist = File.Exists(Path.GetFullPath(fileName)); - // If the file does not exist, check if OFN_CREATEPROMPT is + // If the file does not exist, check if CreatePrompt is // set. If so, display the appropriate message box and act // on the user's choice. // Note that File.Exists requires a full path as a parameter. @@ -239,7 +218,7 @@ internal override bool PromptUserIfAppropriate(string fileName) } } - // If the file already exists, check if OFN_OVERWRITEPROMPT is + // If the file already exists, check if OverwritePrompt is // set. If so, display the appropriate message box and act // on the user's choice. // Note that File.Exists requires a full path as a parameter. @@ -250,90 +229,18 @@ internal override bool PromptUserIfAppropriate(string fileName) return false; } } - // Since all dialog boxes we showed resulted in a positive outcome, // returning true allows the file dialog box to close. return true; } - /// - /// Performs the actual call to display a file save dialog. - /// - /// - /// The call chain is ShowDialog > RunDialog > - /// RunFileDialog (this function). In - /// FileDialog.RunDialog, we created the OPENFILENAME - /// structure - so all this function needs to do is - /// call GetSaveFileName and process the result code. - /// - /// - /// Thrown if there is an invalid filename, if - /// a subclass failure occurs or if the buffer length - /// allocated to store the filenames occurs. - /// - internal override bool RunFileDialog(NativeMethods.OPENFILENAME_I ofn) - { - bool result = false; - - // Make the actual call to GetSaveFileName. This function - // blocks on GetSaveFileName until the entire dialog display - // is completed - any interaction we have with the dialog - // while it's open takes place through our HookProc. The - // return value is a bool; true = success. - result = UnsafeNativeMethods.GetSaveFileName(ofn); - - if (!result) // result was 0 (false), so an error occurred. - { - // Something may have gone wrong - check for error conditions - // by calling CommDlgExtendedError to get the specific error. - int errorCode = UnsafeNativeMethods.CommDlgExtendedError(); - - // Throw an appropriate exception if we know what happened: - switch (errorCode) - { - // FNERR_INVALIDFILENAME is usually triggered when an invalid initial filename is specified - case NativeMethods.FNERR_INVALIDFILENAME: - throw new InvalidOperationException(SR.Format(SR.FileDialogInvalidFileName, SafeFileName)); - - case NativeMethods.FNERR_SUBCLASSFAILURE: - throw new InvalidOperationException(SR.FileDialogSubClassFailure); - - // note for FNERR_BUFFERTOOSMALL: - // This error likely indicates a problem with our buffer size growing code; - // take a look at that part of HookProc if customers report this error message is occurring. - case NativeMethods.FNERR_BUFFERTOOSMALL: - throw new InvalidOperationException(SR.FileDialogBufferTooSmall); - - /* - * According to MSDN, the following errors can also occur, but we do not handle them as - * they are very unlikely, and if they do occur, they indicate a catastrophic failure. - * Most are related to features we do not wrap in our implementation. - * - * CDERR_DIALOGFAILURE - * CDERR_FINDRESFAILURE - * CDERR_INITIALIZATION - * CDERR_LOADRESFAILURE - * CDERR_LOADSTRFAILURE - * CDERR_LOCKRESFAILURE - * CDERR_MEMALLOCFAILURE - * CDERR_MEMLOCKFAILURE - * CDERR_NOHINSTANCE - * CDERR_NOHOOK - * CDERR_NOTEMPLATE - * CDERR_STRUCTSIZE - */ - } - } - return result; - } - - internal override string[] ProcessVistaFiles(IFileDialog dialog) + private protected override string[] ProcessFiles(IFileDialog dialog) { IShellItem item = dialog.GetResult(); return new[] { item.GetDisplayName(SIGDN.DESKTOPABSOLUTEPARSING) }; } - internal override IFileDialog CreateVistaDialog() + private protected override IFileDialog CreateDialog() { return (IFileDialog)Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid(CLSID.FileSaveDialog))); } @@ -373,11 +280,10 @@ internal override IFileDialog CreateVistaDialog() // base is initialized first. private void Initialize() { - // OFN_OVERWRITEPROMPT // Causes the Save As dialog box to generate a message box if // the selected file already exists. The user must confirm // whether to overwrite the file. Default is true. - SetOption(NativeMethods.OFN_OVERWRITEPROMPT, true); + OverwritePrompt = true; } /// @@ -388,7 +294,7 @@ private void Initialize() /// private bool PromptFileCreate(string fileName) { - return MessageBoxWithFocusRestore(SR.Format(SR.FileDialogCreatePrompt, fileName), + return MessageBoxWithFocusRestore(SR.Get(SRID.FileDialogCreatePrompt, fileName), MessageBoxButton.YesNo, MessageBoxImage.Warning); } @@ -400,7 +306,7 @@ private bool PromptFileCreate(string fileName) /// private bool PromptFileOverwrite(string fileName) { - return MessageBoxWithFocusRestore(SR.Format(SR.FileDialogOverwritePrompt, fileName), + return MessageBoxWithFocusRestore(SR.Get(SRID.FileDialogOverwritePrompt, fileName), MessageBoxButton.YesNo, MessageBoxImage.Warning); } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/PresentationFramework.csproj b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/PresentationFramework.csproj index a120ce3fac6..91c17599028 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/PresentationFramework.csproj +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/PresentationFramework.csproj @@ -62,11 +62,13 @@ + + diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/Strings.resx b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/Strings.resx index 37dc5a6fbc8..11e12a887bf 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/Strings.resx +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/Strings.resx @@ -1023,9 +1023,6 @@ Failed to navigate using the Hyperlink target. - - Too many files selected. Select fewer files and try again. - '{0}' does not exist. Do you want to create it? @@ -1034,9 +1031,6 @@ Do you want to create it? '{0}' does not exist. Verify that the file name is correct. - - '{0}' is not a valid file name. - Provided filter string is not valid. Filter string should contain a description of the filter, followed by a vertical bar and the filter pattern. Must also separate multiple filter description and pattern pairs by a vertical bar. Must separate multiple extensions in a filter pattern with a semicolon. Example: \"Image files (*.bmp, *.jpg)|*.bmp;*.jpg|All files (*.*)|*.*\" @@ -1047,9 +1041,6 @@ Verify that the file name is correct. '{0}' already exists. Do you want to replace it? - - Cannot subclass a file dialog because sufficient memory is not available. - Cannot access file because file name is null or empty. diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.cs.xlf b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.cs.xlf index 3eb07936096..9db82ee975f 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.cs.xlf +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.cs.xlf @@ -1607,11 +1607,6 @@ Nepodařilo se převést zdroj na objekt. - - Too many files selected. Select fewer files and try again. - Bylo vybráno příliš mnoho souborů. Vyberte menší počet souborů a akci zopakujte. - - '{0}' does not exist. Do you want to create it? @@ -1626,11 +1621,6 @@ Verify that the file name is correct. Ověřte, zda je správný název souboru. - - '{0}' is not a valid file name. - Název {0} není platným názvem souboru. - - Provided filter string is not valid. Filter string should contain a description of the filter, followed by a vertical bar and the filter pattern. Must also separate multiple filter description and pattern pairs by a vertical bar. Must separate multiple extensions in a filter pattern with a semicolon. Example: \"Image files (*.bmp, *.jpg)|*.bmp;*.jpg|All files (*.*)|*.*\" Řetězec filtru není platný. Řetězec filtru by měl obsahovat popis filtru následovaný svislou čárou a vzorkem filtru. Musí také od sebe oddělovat popisy různých filtrů a dvojic vzorků svislou čárou. Dále musí oddělovat různé přípony vzorce filtru středníkem. Příklad: Soubory obrázků (*.bmp, *.jpg)|*.bmp;*.jpg|Všechny soubory(*.*)|*.* @@ -1648,11 +1638,6 @@ Do you want to replace it? Chcete ho nahradit? - - Cannot subclass a file dialog because sufficient memory is not available. - Nelze vytvořit podtřídu dialogu souboru, protože není k dispozici dostatek paměti. - - Input file or data stream does not conform to the expected file format specification. Vstupní soubor nebo datový proud neodpovídají očekávané specifikaci formátu souboru. @@ -6475,4 +6460,4 @@ Chcete ho nahradit? - \ No newline at end of file + diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.de.xlf b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.de.xlf index 6099c5c33e2..c38dfdd6041 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.de.xlf +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.de.xlf @@ -1607,11 +1607,6 @@ Fehler beim Konvertieren der Ressource in ein Objekt. - - Too many files selected. Select fewer files and try again. - Zu viele Dateien ausgewählt. Wählen Sie weniger Dateien aus, und wiederholen Sie den Vorgang. - - '{0}' does not exist. Do you want to create it? @@ -1626,11 +1621,6 @@ Verify that the file name is correct. Überprüfen Sie den Dateinamen. - - '{0}' is not a valid file name. - '{0}' ist ein ungültiger Dateiname. - - Provided filter string is not valid. Filter string should contain a description of the filter, followed by a vertical bar and the filter pattern. Must also separate multiple filter description and pattern pairs by a vertical bar. Must separate multiple extensions in a filter pattern with a semicolon. Example: \"Image files (*.bmp, *.jpg)|*.bmp;*.jpg|All files (*.*)|*.*\" Die bereitgestellte Filterzeichenfolge ist ungültig. Die Filterzeichenfolge muss eine Beschreibung des Filters enthalten, gefolgt von einem vertikalen Strich und dem Filtermuster. Weitere Paare aus Filterbeschreibung und Muster müssen ebenfalls durch einen vertikalen Strich getrennt werden. Mehrere Erweiterungen in Filtermustern werden mit einem Semikolon getrennt. Beispiel: \"Bilddateien (*.bmp, *.jpg)|*.bmp;*.jpg|Alle Dateien (*.*)|*.*\" @@ -1648,11 +1638,6 @@ Do you want to replace it? Möchten Sie das Element ersetzen? - - Cannot subclass a file dialog because sufficient memory is not available. - Ein Dateidialogfeld kann nicht als Unterklasse verwendet werden, da nicht genügend Speicher zur Verfügung steht. - - Input file or data stream does not conform to the expected file format specification. Die Eingabedatei oder der Datenstrom entspricht nicht der erwarteten Dateiformatspezifikation. @@ -6475,4 +6460,4 @@ Möchten Sie das Element ersetzen? - \ No newline at end of file + diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.es.xlf b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.es.xlf index a9d3c5bd0f5..1d8bcc4fb07 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.es.xlf +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.es.xlf @@ -1607,11 +1607,6 @@ Error al convertir el recurso en un objeto. - - Too many files selected. Select fewer files and try again. - Demasiados archivos seleccionados. Seleccione menos archivos e inténtelo de nuevo. - - '{0}' does not exist. Do you want to create it? @@ -1626,11 +1621,6 @@ Verify that the file name is correct. Compruebe que el nombre de archivo sea correcto. - - '{0}' is not a valid file name. - '{0}' no es un nombre de archivo válido. - - Provided filter string is not valid. Filter string should contain a description of the filter, followed by a vertical bar and the filter pattern. Must also separate multiple filter description and pattern pairs by a vertical bar. Must separate multiple extensions in a filter pattern with a semicolon. Example: \"Image files (*.bmp, *.jpg)|*.bmp;*.jpg|All files (*.*)|*.*\" La cadena de filtro especificada no es válida. La cadena de filtro debería incluir una descripción del filtro, seguida de una barra vertical y del patrón del filtro. Las descripciones de múltiples filtros y los pares de patrones también se deben separar mediante una barra vertical. Las múltiples extensiones de un patrón de filtros se deben separar mediante un punto y coma. Ejemplo: \"Archivos de imágenes (*.bmp, *.jpg)|*.bmp;*.jpg|Todos los archivos (*.*)|*.*\" @@ -1648,11 +1638,6 @@ Do you want to replace it? ¿Desea reemplazarlo? - - Cannot subclass a file dialog because sufficient memory is not available. - No se puede asignar como subclase un cuadro de diálogo de archivo porque no hay suficiente memoria disponible. - - Input file or data stream does not conform to the expected file format specification. El archivo de entrada o el flujo de datos no se ajusta a la especificación de formato de archivo esperada. @@ -6475,4 +6460,4 @@ Do you want to replace it? - \ No newline at end of file + diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.fr.xlf b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.fr.xlf index af8622dd717..fad8a2fc284 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.fr.xlf +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.fr.xlf @@ -1607,11 +1607,6 @@ Échec de la conversion de la ressource en objet. - - Too many files selected. Select fewer files and try again. - Trop de fichiers sélectionnés. Sélectionnez moins de fichiers et réessayez. - - '{0}' does not exist. Do you want to create it? @@ -1626,11 +1621,6 @@ Verify that the file name is correct. Vérifiez que le nom de fichier est correct. - - '{0}' is not a valid file name. - '{0}' n'est pas un nom de fichier valide. - - Provided filter string is not valid. Filter string should contain a description of the filter, followed by a vertical bar and the filter pattern. Must also separate multiple filter description and pattern pairs by a vertical bar. Must separate multiple extensions in a filter pattern with a semicolon. Example: \"Image files (*.bmp, *.jpg)|*.bmp;*.jpg|All files (*.*)|*.*\" La chaîne de filtre fournie est non valide. La chaîne de filtre doit contenir une description du filtre, suivie d'une barre verticale et du modèle du filtre. Les paires description / modèle de plusieurs filtres doivent également être séparées par une barre verticale. Les extensions multiples d'un modèle de filtre doivent être séparées par un point-virgule. Exemple : \"Fichiers image (*.bmp, *.jpg)|*.bmp;*.jpg|Tous les fichiers (*.*)|*.*\" @@ -1648,11 +1638,6 @@ Do you want to replace it? Voulez-vous le remplacer ? - - Cannot subclass a file dialog because sufficient memory is not available. - Impossible de sous-classer une boîte de dialogue, car la mémoire disponible n'est pas suffisante. - - Input file or data stream does not conform to the expected file format specification. Le fichier d'entrée ou le flux de données ne sont pas conformes aux spécifications de format de fichier attendues. @@ -6475,4 +6460,4 @@ Voulez-vous le remplacer ? - \ No newline at end of file + diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.it.xlf b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.it.xlf index 12e7c3495d2..a71e8a0a18b 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.it.xlf +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.it.xlf @@ -1607,11 +1607,6 @@ Impossibile convertire la risorsa in oggetto. - - Too many files selected. Select fewer files and try again. - Sono stati selezionati troppi file. Selezionare un numero inferiore di file e riprovare. - - '{0}' does not exist. Do you want to create it? @@ -1626,11 +1621,6 @@ Verify that the file name is correct. Verificare che il nome di file sia corretto. - - '{0}' is not a valid file name. - '{0}' non è un nome di file valido. - - Provided filter string is not valid. Filter string should contain a description of the filter, followed by a vertical bar and the filter pattern. Must also separate multiple filter description and pattern pairs by a vertical bar. Must separate multiple extensions in a filter pattern with a semicolon. Example: \"Image files (*.bmp, *.jpg)|*.bmp;*.jpg|All files (*.*)|*.*\" La stringa di filtro specificata non è valida. La stringa di filtro deve contenere una descrizione del filtro, seguita da una barra verticale e dal criterio di filtro. È inoltre necessario usare una barra verticale per separare più coppie di descrizioni e criteri di filtro. Esempio: \"File immagine (*.bmp, *.jpg)|*.bmp;*.jpg|Tutti i file (*.*)|*.*\" @@ -1648,11 +1638,6 @@ Do you want to replace it? Sostituirlo? - - Cannot subclass a file dialog because sufficient memory is not available. - Impossibile impostare una finestra di dialogo file come sottoclasse. Memoria insufficiente. - - Input file or data stream does not conform to the expected file format specification. Il file di input o il flusso di dati non è conforme alla specifica di formato di file prevista. @@ -6475,4 +6460,4 @@ Sostituirlo? - \ No newline at end of file + diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.ja.xlf b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.ja.xlf index 905333a284a..42119ff2b4a 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.ja.xlf +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.ja.xlf @@ -1607,11 +1607,6 @@ リソースをオブジェクトに変換できませんでした。 - - Too many files selected. Select fewer files and try again. - 選択したファイルが多すぎます。選択ファイル数を減らして、やり直してください。 - - '{0}' does not exist. Do you want to create it? @@ -1626,11 +1621,6 @@ Verify that the file name is correct. ファイル名が正しいことを確認してください。 - - '{0}' is not a valid file name. - '{0}' は無効なファイル名です。 - - Provided filter string is not valid. Filter string should contain a description of the filter, followed by a vertical bar and the filter pattern. Must also separate multiple filter description and pattern pairs by a vertical bar. Must separate multiple extensions in a filter pattern with a semicolon. Example: \"Image files (*.bmp, *.jpg)|*.bmp;*.jpg|All files (*.*)|*.*\" 指定されたフィルター文字列は無効です。フィルター文字列には、フィルターの説明と、その後に縦線およびフィルター パターンが含まれている必要があります。また、複数のフィルターの説明とパターンのペアの間は、縦線で区切る必要があります。フィルター パターンの複数の拡張子は、セミコロンで区切る必要があります。例: \"イメージ ファイル (*.bmp, *.jpg)|*.bmp;*.jpg|すべてのファイル (*.*)|*.*\" @@ -1648,11 +1638,6 @@ Do you want to replace it? 置き換えますか? - - Cannot subclass a file dialog because sufficient memory is not available. - 十分なメモリがないため、ファイル ダイアログをサブクラス化することができません。 - - Input file or data stream does not conform to the expected file format specification. 入力ファイルまたはデータ ストリームは、予測されるファイル形式の仕様に準拠していません。 @@ -6475,4 +6460,4 @@ Do you want to replace it? - \ No newline at end of file + diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.ko.xlf b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.ko.xlf index 6bdc92e219b..f52437c235d 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.ko.xlf +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.ko.xlf @@ -1607,11 +1607,6 @@ 리소스를 개체로 변환하지 못했습니다. - - Too many files selected. Select fewer files and try again. - 파일을 너무 많이 선택했습니다. 현재 선택한 파일에서 몇 개를 빼고 다시 시도하십시오. - - '{0}' does not exist. Do you want to create it? @@ -1626,11 +1621,6 @@ Verify that the file name is correct. 파일 이름이 올바른지 확인하십시오. - - '{0}' is not a valid file name. - '{0}'은(는) 잘못된 파일 이름입니다. - - Provided filter string is not valid. Filter string should contain a description of the filter, followed by a vertical bar and the filter pattern. Must also separate multiple filter description and pattern pairs by a vertical bar. Must separate multiple extensions in a filter pattern with a semicolon. Example: \"Image files (*.bmp, *.jpg)|*.bmp;*.jpg|All files (*.*)|*.*\" 제공된 필터 문자열이 잘못되었습니다. 필터 문자열에는 필터의 설명과 함께 세로 막대와 필터 패턴이 있어야 합니다. 또한 여러 개의 필터 설명 및 패턴 쌍을 세로 막대로 구분해야 합니다. 필터 패턴에서는 여러 개의 확장명을 \"Image files (*.bmp, *.jpg)|*.bmp;*.jpg|All files (*.*)|*.*\"와 같이 세미콜론으로 구분해야 합니다. @@ -1648,11 +1638,6 @@ Do you want to replace it? 바꾸시겠습니까? - - Cannot subclass a file dialog because sufficient memory is not available. - 메모리가 부족하여 파일 대화 상자를 서브클래스로 확장할 수 없습니다. - - Input file or data stream does not conform to the expected file format specification. 입력 파일 또는 데이터 스트림이 필요한 파일 형식 사양에 맞지 않습니다. @@ -6475,4 +6460,4 @@ Do you want to replace it? - \ No newline at end of file + diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.pl.xlf b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.pl.xlf index 5d599f327c1..8d13591fe0d 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.pl.xlf +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.pl.xlf @@ -1607,11 +1607,6 @@ Konwersja zasobu w obiekt nie powiodła się. - - Too many files selected. Select fewer files and try again. - Wybrano zbyt wiele plików. Wybierz mniej plików i ponów próbę. - - '{0}' does not exist. Do you want to create it? @@ -1626,11 +1621,6 @@ Verify that the file name is correct. Sprawdź, czy nazwa pliku jest prawidłowa. - - '{0}' is not a valid file name. - „{0}” nie jest prawidłową nazwą pliku. - - Provided filter string is not valid. Filter string should contain a description of the filter, followed by a vertical bar and the filter pattern. Must also separate multiple filter description and pattern pairs by a vertical bar. Must separate multiple extensions in a filter pattern with a semicolon. Example: \"Image files (*.bmp, *.jpg)|*.bmp;*.jpg|All files (*.*)|*.*\" Podany ciąg filtru jest nieprawidłowy. Łańcuch filtru powinien zawierać opis filtru, po którym znajduje się pionowa kreska i szablon filtru. Wiele par opisów filtru i szablonów należy oddzielić pionową kreską. Wiele rozszerzeń w szablonie filtru należy oddzielić średnikiem. Przykład: \„Pliki obrazów (*.bmp, *.jpg)|*.bmp;*.jpg|Wszystkie pliki (*.*)|*.*\” @@ -1648,11 +1638,6 @@ Do you want to replace it? Czy chcesz go zastąpić? - - Cannot subclass a file dialog because sufficient memory is not available. - Nie można uzyskać dostępu do podklasy okna dialogowego pliku z powodu braku wystarczającej ilości pamięci. - - Input file or data stream does not conform to the expected file format specification. Plik wejściowy lub strumień danych nie odpowiada oczekiwanej specyfikacji formatu pliku. @@ -6475,4 +6460,4 @@ Czy chcesz go zastąpić? - \ No newline at end of file + diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.pt-BR.xlf b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.pt-BR.xlf index 0a61fa07b8b..6ef6235d772 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.pt-BR.xlf +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.pt-BR.xlf @@ -1607,11 +1607,6 @@ Falha ao converter o recurso em objeto. - - Too many files selected. Select fewer files and try again. - Número excessivo de arquivos selecionados. Selecione menos arquivos e tente novamente. - - '{0}' does not exist. Do you want to create it? @@ -1626,11 +1621,6 @@ Verify that the file name is correct. Verifique se o nome de arquivo está correto. - - '{0}' is not a valid file name. - '{0}' não é um nome de arquivo válido. - - Provided filter string is not valid. Filter string should contain a description of the filter, followed by a vertical bar and the filter pattern. Must also separate multiple filter description and pattern pairs by a vertical bar. Must separate multiple extensions in a filter pattern with a semicolon. Example: \"Image files (*.bmp, *.jpg)|*.bmp;*.jpg|All files (*.*)|*.*\" A cadeia de caracteres de filtro fornecida não é válida. A cadeia de caracteres de filtro deve conter uma descrição do filtro, seguida de uma barra vertical e do padrão de filtro. Também é necessário separar os vários pares de descrição e padrão de filtro com uma barra vertical. É necessário separar várias extensões em um padrão de filtro com um ponto-e-vírgula. Exemplo: \"Arquivos de imagem (*.bmp, *.jpg)|*.bmp;*.jpg|Todos os arquivos (*.*)|*.*\" @@ -1648,11 +1638,6 @@ Do you want to replace it? Deseja substituí-lo? - - Cannot subclass a file dialog because sufficient memory is not available. - Não é possível criar uma subclasse de uma caixa de diálogo de arquivo porque não há memória disponível suficiente. - - Input file or data stream does not conform to the expected file format specification. O arquivo de entrada ou o fluxo de dados não está de acordo com a especificação de formato de arquivo esperada. @@ -6475,4 +6460,4 @@ Deseja substituí-lo? - \ No newline at end of file + diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.ru.xlf b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.ru.xlf index 6bce25ef47c..755817a8e87 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.ru.xlf +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.ru.xlf @@ -1607,11 +1607,6 @@ Не удалось преобразовать ресурс в объект. - - Too many files selected. Select fewer files and try again. - Выбрано слишком много файлов. Выберите меньшее число файлов и повторите попытку. - - '{0}' does not exist. Do you want to create it? @@ -1626,11 +1621,6 @@ Verify that the file name is correct. Проверьте имя файла. - - '{0}' is not a valid file name. - Недопустимое имя файла "{0}". - - Provided filter string is not valid. Filter string should contain a description of the filter, followed by a vertical bar and the filter pattern. Must also separate multiple filter description and pattern pairs by a vertical bar. Must separate multiple extensions in a filter pattern with a semicolon. Example: \"Image files (*.bmp, *.jpg)|*.bmp;*.jpg|All files (*.*)|*.*\" Предоставлена недопустимая строка фильтра. Строка фильтра должна содержать описание фильтра, за которым следует вертикальная черта и шаблон фильтра. Вертикальная черта должна также отделять разные пары описания и шаблона фильтра. Разные расширения в шаблоне фильтра должны разделяться точкой с запятой. Пример: \"Файлы рисунков (*.bmp, *.jpg)|*.bmp;*.jpg|Все файлы (*.*)|*.*\" @@ -1648,11 +1638,6 @@ Do you want to replace it? Заменить его? - - Cannot subclass a file dialog because sufficient memory is not available. - Невозможно определить подкласс диалогового окна файла из-за нехватки памяти. - - Input file or data stream does not conform to the expected file format specification. Входной файл или поток данных не соответствует спецификации ожидаемого формата файлов. @@ -6475,4 +6460,4 @@ Do you want to replace it? - \ No newline at end of file + diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.tr.xlf b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.tr.xlf index 6946611bf2b..b0c40108036 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.tr.xlf +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.tr.xlf @@ -1607,11 +1607,6 @@ Kaynak nesneye dönüştürülemedi. - - Too many files selected. Select fewer files and try again. - Çok fazla dosya seçildi. Daha az dosya seçin ve yeniden deneyin. - - '{0}' does not exist. Do you want to create it? @@ -1626,11 +1621,6 @@ Verify that the file name is correct. Dosya adının doğruluğunu denetleyin. - - '{0}' is not a valid file name. - '{0}' geçerli bir dosya adı değildir. - - Provided filter string is not valid. Filter string should contain a description of the filter, followed by a vertical bar and the filter pattern. Must also separate multiple filter description and pattern pairs by a vertical bar. Must separate multiple extensions in a filter pattern with a semicolon. Example: \"Image files (*.bmp, *.jpg)|*.bmp;*.jpg|All files (*.*)|*.*\" Sağlanan filtre dizesi geçerli değil. Filtre dizesi bir filtre açıklaması, ardından bir dikey çubuk ve filtre deseni içermelidir. Birden fazla filtre açıklaması ve desen çiftleri de bir dikey çubukla ayrılmalıdır. Filtre deseninde birden fazla uzantı noktalı virgülle ayrılmalıdır. Örnek: \"Görüntü dosyaları (*.bmp, *.jpg)|*.bmp;*.jpg|Tüm dosyalar (*.*)|*.*\" @@ -1648,11 +1638,6 @@ Do you want to replace it? Değiştirmek istiyor musunuz? - - Cannot subclass a file dialog because sufficient memory is not available. - Yeterli kullanılabilir bellek olmadığı için bir dosya iletişim kutusunun alt sınıfı oluşturulamıyor. - - Input file or data stream does not conform to the expected file format specification. Giriş dosyası veya veri akışı beklenen dosya biçimi belirtimine uymuyor. @@ -6475,4 +6460,4 @@ Değiştirmek istiyor musunuz? - \ No newline at end of file + diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.zh-Hans.xlf b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.zh-Hans.xlf index cbbbd641df4..383692a5cfe 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.zh-Hans.xlf @@ -1607,11 +1607,6 @@ 无法将资源转换成对象。 - - Too many files selected. Select fewer files and try again. - 选定的文件太多。请选择较少的文件并重试。 - - '{0}' does not exist. Do you want to create it? @@ -1626,11 +1621,6 @@ Verify that the file name is correct. 请验证文件名是否正确。 - - '{0}' is not a valid file name. - “{0}”不是有效的文件名。 - - Provided filter string is not valid. Filter string should contain a description of the filter, followed by a vertical bar and the filter pattern. Must also separate multiple filter description and pattern pairs by a vertical bar. Must separate multiple extensions in a filter pattern with a semicolon. Example: \"Image files (*.bmp, *.jpg)|*.bmp;*.jpg|All files (*.*)|*.*\" 提供的筛选器字符串无效。筛选器字符串应包含筛选器的说明,后跟竖线(|)和筛选模式。多个筛选器说明和模式对还必须以竖线分隔。在一个筛选器模式中的多个扩展名必须用分号分隔。例如: \"图像文件(*.bmp, *.jpg)|*.bmp;*.jpg|所有文件(*.*)|*.*\" @@ -1648,11 +1638,6 @@ Do you want to replace it? 是否替换它? - - Cannot subclass a file dialog because sufficient memory is not available. - 由于没有足够的内存可用,因此无法对文件对话框划分子类。 - - Input file or data stream does not conform to the expected file format specification. 输入文件或数据流不符合要求的文件格式规格。 @@ -6475,4 +6460,4 @@ Do you want to replace it? - \ No newline at end of file + diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.zh-Hant.xlf b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.zh-Hant.xlf index be9e75c7626..5236941b183 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/xlf/Strings.zh-Hant.xlf @@ -1607,11 +1607,6 @@ 無法將資源轉換為物件。 - - Too many files selected. Select fewer files and try again. - 選取太多檔案。請少選一點檔案再試一次。 - - '{0}' does not exist. Do you want to create it? @@ -1626,11 +1621,6 @@ Verify that the file name is correct. 請確認檔案名稱是正確的。 - - '{0}' is not a valid file name. - '{0}' 不是有效的檔案名稱。 - - Provided filter string is not valid. Filter string should contain a description of the filter, followed by a vertical bar and the filter pattern. Must also separate multiple filter description and pattern pairs by a vertical bar. Must separate multiple extensions in a filter pattern with a semicolon. Example: \"Image files (*.bmp, *.jpg)|*.bmp;*.jpg|All files (*.*)|*.*\" 提供的篩選字串無效。篩選字串應包含篩選的描述,其後接著分隔號與篩選樣式。多個成對的篩選描述與樣式組之間,也必須用分隔號分隔。篩選樣式中的多個副檔名必須用分號分隔。例如: : \"影像檔 (*.bmp、*.jpg)|*.bmp;*.jpg|所有檔案 (*.*)|*.*\" @@ -1648,11 +1638,6 @@ Do you want to replace it? 您想要取代它嗎? - - Cannot subclass a file dialog because sufficient memory is not available. - 因為記憶體不足,無法將檔案對話方塊設定為子類別。 - - Input file or data stream does not conform to the expected file format specification. 輸入檔或資料流不符合預期的檔案格式規格。 diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs index 3d41aa1de05..cf4df4f3599 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs @@ -11,31 +11,36 @@ protected virtual void CheckPermissionsToShowDialog() { } public virtual bool? ShowDialog() { throw null; } public bool? ShowDialog(System.Windows.Window owner) { throw null; } } - public abstract partial class FileDialog : Microsoft.Win32.CommonDialog + public abstract partial class CommonItemDialog : Microsoft.Win32.CommonDialog + { + protected CommonItemDialog() { } + public System.Collections.Generic.IList CustomPlaces { get { throw null; } set { } } + public bool DereferenceLinks { get { throw null; } set { } } + public string InitialDirectory { get { throw null; } set { } } + public bool RestoreDirectory { get { throw null; } set { } } + public string Title { get { throw null; } set { } } + public bool ValidateNames { get { throw null; } set { } } + public event System.ComponentModel.CancelEventHandler FileOk { add { } remove { } } + protected void OnFileOk(System.ComponentModel.CancelEventArgs e) { } + protected override bool RunDialog(System.IntPtr hwndOwner) { throw null; } + public override void Reset() { } + public override string ToString() { throw null; } + + } + public abstract partial class FileDialog : Microsoft.Win32.CommonItemDialog { protected FileDialog() { } public bool AddExtension { get { throw null; } set { } } public virtual bool CheckFileExists { get { throw null; } set { } } public bool CheckPathExists { get { throw null; } set { } } - public System.Collections.Generic.IList CustomPlaces { get { throw null; } set { } } public string DefaultExt { get { throw null; } set { } } - public bool DereferenceLinks { get { throw null; } set { } } public string FileName { get { throw null; } set { } } public string[] FileNames { get { throw null; } } public string Filter { get { throw null; } set { } } public int FilterIndex { get { throw null; } set { } } - public string InitialDirectory { get { throw null; } set { } } - protected int Options { get { throw null; } } - public bool RestoreDirectory { get { throw null; } set { } } public string SafeFileName { get { throw null; } } public string[] SafeFileNames { get { throw null; } } - public string Title { get { throw null; } set { } } - public bool ValidateNames { get { throw null; } set { } } - public event System.ComponentModel.CancelEventHandler FileOk { add { } remove { } } - protected override System.IntPtr HookProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam) { throw null; } - protected void OnFileOk(System.ComponentModel.CancelEventArgs e) { } public override void Reset() { } - protected override bool RunDialog(System.IntPtr hwndOwner) { throw null; } public override string ToString() { throw null; } } public sealed partial class FileDialogCustomPlace @@ -71,11 +76,17 @@ public OpenFileDialog() { } public bool Multiselect { get { throw null; } set { } } public bool ReadOnlyChecked { get { throw null; } set { } } public bool ShowReadOnly { get { throw null; } set { } } - protected override void CheckPermissionsToShowDialog() { } public System.IO.Stream OpenFile() { throw null; } public System.IO.Stream[] OpenFiles() { throw null; } public override void Reset() { } } + public sealed partial class OpenFolderDialog : Microsoft.Win32.CommonItemDialog + { + public OpenFolderDialog() { } + public string FolderName { get { throw null; } set { } } + public string SafeFolderName { get { throw null; } } + public override void Reset() { } + } public sealed partial class SaveFileDialog : Microsoft.Win32.FileDialog { public SaveFileDialog() { } From 0c4e8d982c537cf7c113f776370a6b8d9daeafa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Ku=C4=8Dera?= Date: Wed, 2 Nov 2022 12:44:55 +0000 Subject: [PATCH 02/21] Multiselect and FileNames for folders --- ...esentationFramework-ref-Net48.baseline.txt | 3 + .../Microsoft/Win32/CommonItemDialog.cs | 360 ++++++++++++++---- .../Microsoft/Win32/FileDialog.cs | 351 ++++------------- .../Microsoft/Win32/OpenFileDialog.cs | 61 +-- .../Microsoft/Win32/OpenFolderDialog.cs | 165 +------- .../Microsoft/Win32/SaveFileDialog.cs | 20 +- .../ref/PresentationFramework.cs | 13 +- 7 files changed, 397 insertions(+), 576 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/ApiCompat/Baselines/PresentationFramework-ref-Net48.baseline.txt b/src/Microsoft.DotNet.Wpf/ApiCompat/Baselines/PresentationFramework-ref-Net48.baseline.txt index b7a37dd2814..d7cd9a976bf 100644 --- a/src/Microsoft.DotNet.Wpf/ApiCompat/Baselines/PresentationFramework-ref-Net48.baseline.txt +++ b/src/Microsoft.DotNet.Wpf/ApiCompat/Baselines/PresentationFramework-ref-Net48.baseline.txt @@ -1,4 +1,7 @@ Compat issues with assembly PresentationFramework: +CannotMakeMemberNonVirtual : Member 'public System.Boolean Microsoft.Win32.FileDialog.CheckFileExists' is non-virtual in the implementation but is virtual in the contract. +CannotMakeMemberNonVirtual : Member 'public System.Boolean Microsoft.Win32.FileDialog.CheckFileExists.get()' is non-virtual in the implementation but is virtual in the contract. +CannotMakeMemberNonVirtual : Member 'public void Microsoft.Win32.FileDialog.CheckFileExists.set(System.Boolean)' is non-virtual in the implementation but is virtual in the contract. MembersMustExist : Member 'internal MS.Internal.AppModel.IFileDialog Microsoft.Win32.FileDialog.CreateVistaDialog()' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'protected System.Int32 Microsoft.Win32.FileDialog.Options.get()' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'internal System.String[] Microsoft.Win32.FileDialog.ProcessVistaFiles(MS.Internal.AppModel.IFileDialog)' does not exist in the implementation but it does exist in the contract. diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs index 4eb0905aedd..de277119ec4 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs @@ -22,9 +22,7 @@ namespace Microsoft.Win32 using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; - using System.Security; using System.Text; - using System.Threading; using System.Windows; using HRESULT = MS.Internal.Interop.HRESULT; @@ -88,7 +86,9 @@ public override void Reset() // this object for users' debugging purposes. public override string ToString() { - return base.ToString() + ": Title: " + Title; + StringBuilder sb = new StringBuilder(base.ToString() + ": Title: " + Title + ", FileName: "); + sb.Append(FileName); + return sb.ToString(); } #endregion Public Methods @@ -100,6 +100,109 @@ public override string ToString() //--------------------------------------------------- #region Public Properties + /// + /// Gets a string containing the filename component of the + /// file selected in the dialog box. + /// + /// Example: if FileName = "c:\windows\explorer.exe" , + /// SafeFileName = "explorer.exe" + /// + public string SafeFileName + { + get + { + // Use the FileName property to avoid directly accessing + // the _fileNames field, then call Path.GetFileName + // to do the actual work of stripping out the file name + // from the path. + string safeFN = Path.GetFileName(CriticalFileName); + + // Check to make sure Path.GetFileName does not return null. + // If it does, set safeFN to String.Empty instead to accomodate + // programmers that fail to check for null when reading strings. + if (safeFN == null) + { + safeFN = String.Empty; + } + + return safeFN; + } + } + + /// + /// Gets a string array containing the filename of each file selected + /// in the dialog box. + /// + public string[] SafeFileNames + { + get + { + // Retrieve the existing filenames into an array, then make + // another array of the same length to hold the safe version. + string[] unsafeFileNames = CloneFileNames(); + string[] safeFileNames = new string[unsafeFileNames.Length]; + + for (int i = 0; i < unsafeFileNames.Length; i++) + { + // Call Path.GetFileName to retrieve only the filename + // component of the current full path. + safeFileNames[i] = Path.GetFileName(unsafeFileNames[i]); + + // Check to make sure Path.GetFileName does not return null. + // If it does, set this filename to String.Empty instead to accomodate + // programmers that fail to check for null when reading strings. + if (safeFileNames[i] == null) + { + safeFileNames[i] = String.Empty; + } + } + + return safeFileNames; + } + } + + // If multiple files are selected, we only return the first filename. + /// + /// Gets or sets a string containing the full path of the file or folder selected in + /// the file dialog box. + /// + public string FileName + { + get + { + return CriticalFileName; + } + set + { + + // Allow users to set a filename to stored in _fileNames. + // If null is passed in, we clear the entire list. + // If we get a string, we clear the entire list and make a new one-element + // array with the new string. + if (value == null) + { + _fileNames = null; + } + else + { + // UNDONE : ChrisAn: This broke the save file dialog. + //string temp = Path.GetFullPath(value); // ensure filename is valid... + _fileNames = new string[] { value }; + } + } + } + + /// + /// Gets the file names of all selected files or folders in the dialog box. + /// + public string[] FileNames + { + get + { + return CloneFileNames(); + } + } + // The actual flag is FOS_NODEREFERENCELINKS (set = do not dereference, unset = deref) - // while we have true = dereference and false=do not dereference. Because we expose // the opposite of the Windows flag as a property to be clearer, we need to negate @@ -199,6 +302,8 @@ public bool ValidateNames } } + public IList CustomPlaces { get; set; } + #endregion Public Properties //--------------------------------------------------- @@ -337,13 +442,116 @@ internal bool MessageBoxWithFocusRestore(string message, #endregion Internal Methods + #region Internal and Protected Methods + + private protected abstract IFileDialog CreateDialog(); + + private protected virtual void PrepareDialog(IFileDialog dialog) + { + if (!string.IsNullOrEmpty(InitialDirectory)) + { + IShellItem initialDirectory = ShellUtil.GetShellItemForPath(InitialDirectory); + if (initialDirectory != null) + { + // Setting both of these so the dialog doesn't display errors when a remembered folder is missing. + dialog.SetDefaultFolder(initialDirectory); + dialog.SetFolder(initialDirectory); + } + } + + dialog.SetTitle(Title); + dialog.SetFileName(CriticalFileName); + + // Only accept physically backed locations. + FOS options = _dialogOptions.Value | FOS.FORCEFILESYSTEM; + dialog.SetOptions(options); + + IList places = CustomPlaces; + if (places != null && places.Count != 0) + { + foreach (FileDialogCustomPlace customPlace in places) + { + IShellItem shellItem = ResolveCustomPlace(customPlace); + if (shellItem != null) + { + try + { + dialog.AddPlace(shellItem, FDAP.BOTTOM); + } + catch (ArgumentException) + { + // The dialog doesn't allow some ShellItems to be set as Places (like device ports). + // Silently swallow errors here. + } + } + } + } + } + + // The FileOk event expects all properties to be set, but if the event is cancelled, they need to be reverted. + // This method is called inside a try block, and inheritors can store any data to be reverted in the revertState. + private protected virtual bool TryHandleFileOk(IFileDialog dialog, out object revertState) + { + revertState = null; + return true; + } + + // This method is called inside a finally block when OK event was cancelled. + // Inheritors should revert properties to the state before the dialog was shown, so that it can be shown again. + private protected virtual void RevertFileOk(object state) { } + + #endregion + //--------------------------------------------------- // // Internal Properties // //--------------------------------------------------- - //#region Internal Properties - //#endregion Internal Properties + #region Internal Properties + + // If multiple files are selected, we only return the first filename. + /// + /// Gets a string containing the full path of the file selected in + /// the file dialog box. + /// + private protected string CriticalFileName + { + get + { + if (_fileNames?.Length > 0) + { + return _fileNames[0]; + } + else + { + return string.Empty; + } + } + } + + private protected string[] MutableFileNames + { + get { return _fileNames; } + } + + /// + /// In cases where we need to return an array of strings, we return + /// a clone of the array. We also need to make sure we return a + /// string[0] instead of a null if we don't have any filenames. + /// + private protected string[] CloneFileNames() + { + if (_fileNames == null) + { + return Array.Empty(); + } + else + { + return (string[])_fileNames.Clone(); + } + } + + #endregion Internal Properties //--------------------------------------------------- // @@ -391,6 +599,7 @@ private void Initialize() // // Initialize additional properties // + _fileNames = null; _title.Value = null; _initialDirectory.Value = null; @@ -398,14 +607,83 @@ private void Initialize() CustomPlaces = new List(); } - private protected virtual bool HandleFileOk(IFileDialog dialog) + private bool HandleFileOk(IFileDialog dialog) { // When this callback occurs, the HWND is visible and we need to // grab it because it is used for various things like looking up the // DialogCaption. UnsafeNativeMethods.IOleWindow oleWindow = (UnsafeNativeMethods.IOleWindow)dialog; oleWindow.GetWindow(out _hwndFileDialog); - return true; + + string[] saveFileNames = _fileNames; + object saveState = null; + bool ok = false; + + try + { + IShellItem[] shellItems = ResolveResults(dialog); + _fileNames = GetParsingNames(shellItems); + + if (TryHandleFileOk(dialog, out saveState)) + { + var cancelArgs = new CancelEventArgs(); + OnFileOk(cancelArgs); + ok = !cancelArgs.Cancel; + } + } + finally + { + if (!ok) + { + RevertFileOk(saveState); + _fileNames = saveFileNames; + } + } + return ok; + } + + private static string[] GetParsingNames(IShellItem[] items) + { + if (items == null) + { + return null; + } + + string[] names = new string[items.Length]; + for (int i = 0; i < items.Length; i++) + { + names[i] = items[i].GetDisplayName(SIGDN.DESKTOPABSOLUTEPARSING); + } + return names; + } + + private static IShellItem[] ResolveResults(IFileDialog dialog) + { + // covers both file and folder dialogs + if (dialog is IFileOpenDialog openDialog) + { + IShellItemArray results = openDialog.GetResults(); + uint count = results.GetCount(); + + IShellItem[] items = new IShellItem[count]; + for (uint i = 0; i < count; ++i) + { + items[i] = results.GetItemAt(i); + } + + return items; + } + else + { + IShellItem item = dialog.GetResult(); + return new[] { item }; + } + } + + private static IShellItem ResolveCustomPlace(FileDialogCustomPlace customPlace) + { + // Use the KnownFolder Guid if it exists. Otherwise use the Path. + return ShellUtil.GetShellItemForPath(ShellUtil.GetPathForKnownFolder(customPlace.KnownFolder) ?? customPlace.Path); } #endregion Private Methods @@ -455,8 +733,6 @@ private string DialogCaption #endregion Private Properties - #region Vista COM interfaces Augmentation - /// /// Events sink for IFileDialog. MSDN says to return E_NOTIMPL for several, but not all, of these methods when we don't want to support them. /// @@ -522,67 +798,6 @@ void IDisposable.Dispose() } } - public IList CustomPlaces { get; set; } - - #region Internal and Protected Methods - - private protected abstract IFileDialog CreateDialog(); - - private protected virtual void PrepareDialog(IFileDialog dialog) - { - if (!string.IsNullOrEmpty(InitialDirectory)) - { - IShellItem initialDirectory = ShellUtil.GetShellItemForPath(InitialDirectory); - if (initialDirectory != null) - { - // Setting both of these so the dialog doesn't display errors when a remembered folder is missing. - dialog.SetDefaultFolder(initialDirectory); - dialog.SetFolder(initialDirectory); - } - } - - dialog.SetTitle(Title); - - // Only accept physically backed locations. - FOS options = _dialogOptions.Value | FOS.FORCEFILESYSTEM; - dialog.SetOptions(options); - - IList places = CustomPlaces; - if (places != null && places.Count != 0) - { - foreach (FileDialogCustomPlace customPlace in places) - { - IShellItem shellItem = ResolveCustomPlace(customPlace); - if (shellItem != null) - { - try - { - dialog.AddPlace(shellItem, FDAP.BOTTOM); - } - catch (ArgumentException) - { - // The dialog doesn't allow some ShellItems to be set as Places (like device ports). - // Silently swallow errors here. - } - } - } - } - } - - #endregion - - #region Private Methods - - private static IShellItem ResolveCustomPlace(FileDialogCustomPlace customPlace) - { - // Use the KnownFolder Guid if it exists. Otherwise use the Path. - return ShellUtil.GetShellItemForPath(ShellUtil.GetPathForKnownFolder(customPlace.KnownFolder) ?? customPlace.Path); - } - - #endregion - - #endregion - //--------------------------------------------------- // // Private Fields @@ -604,6 +819,11 @@ private static IShellItem ResolveCustomPlace(FileDialogCustomPlace customPlace) // box when we need to show a message box with the same title bar caption) private IntPtr _hwndFileDialog; + // This is the array that stores the item(s) the user selected in the + // dialog box. If Multiselect is not enabled, only the first element + // of this array will be used. + private string[] _fileNames; + #endregion Private Fields } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs index 73748c9fa8a..78de53cecbd 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs @@ -18,16 +18,11 @@ namespace Microsoft.Win32 using MS.Internal; using MS.Internal.AppModel; using MS.Internal.Interop; - using MS.Win32; using System; - using System.ComponentModel; using System.Collections.Generic; using System.IO; - using System.Runtime.InteropServices; - using System.Security; using System.Text; - using System.Threading; using System.Windows; /// @@ -82,20 +77,6 @@ public override void Reset() Initialize(); } - /// - /// Returns a string representation of the file dialog with key information - /// for debugging purposes. - /// - // We overload ToString() so that we can provide a useful representation of - // this object for users' debugging purposes. It provides the full pathname for - // any files selected. - public override string ToString() - { - StringBuilder sb = new StringBuilder(base.ToString() + ", FileName: "); - sb.Append(FileName); - return sb.ToString(); - } - #endregion Public Methods //--------------------------------------------------- @@ -129,7 +110,7 @@ public override string ToString() /// the dialog box displays a warning if the /// user specifies a file name that does not exist. /// - public virtual bool CheckFileExists + public bool CheckFileExists { get { @@ -195,114 +176,6 @@ public string DefaultExt } } - /// - /// Gets a string containing the filename component of the - /// file selected in the dialog box. - /// - /// Example: if FileName = "c:\windows\explorer.exe" , - /// SafeFileName = "explorer.exe" - /// - public string SafeFileName - { - get - { - // Use the FileName property to avoid directly accessing - // the _fileNames field, then call Path.GetFileName - // to do the actual work of stripping out the file name - // from the path. - string safeFN = Path.GetFileName(CriticalFileName); - - // Check to make sure Path.GetFileName does not return null. - // If it does, set safeFN to String.Empty instead to accomodate - // programmers that fail to check for null when reading strings. - if (safeFN == null) - { - safeFN = String.Empty; - } - - return safeFN; - } - } - - /// - /// Gets a string array containing the filename of each file selected - /// in the dialog box. - /// - public string[] SafeFileNames - { - get - { - // Retrieve the existing filenames into an array, then make - // another array of the same length to hold the safe version. - string[] unsafeFileNames = FileNamesInternal; - string[] safeFileNames = new string[unsafeFileNames.Length]; - - for (int i = 0; i < unsafeFileNames.Length; i++) - { - // Call Path.GetFileName to retrieve only the filename - // component of the current full path. - safeFileNames[i] = Path.GetFileName(unsafeFileNames[i]); - - // Check to make sure Path.GetFileName does not return null. - // If it does, set this filename to String.Empty instead to accomodate - // programmers that fail to check for null when reading strings. - if (safeFileNames[i] == null) - { - safeFileNames[i] = String.Empty; - } - } - - return safeFileNames; - } - } - - // If multiple files are selected, we only return the first filename. - /// - /// Gets or sets a string containing the full path of the file selected in - /// the file dialog box. - /// - public string FileName - { - get - { - return CriticalFileName; - } - set - { - - // Allow users to set a filename to stored in _fileNames. - // If null is passed in, we clear the entire list. - // If we get a string, we clear the entire list and make a new one-element - // array with the new string. - if (value == null) - { - _fileNames = null; - } - else - { - // UNDONE : ChrisAn: This broke the save file dialog. - //string temp = Path.GetFullPath(value); // ensure filename is valid... - _fileNames = new string[] { value }; - } - } - } - - - /// - /// Gets the file names of all selected files in the dialog box. - /// - public string[] FileNames - { - get - { - - // FileNamesInternal is a property we use to clone - // the string array before returning it. - string[] files = FileNamesInternal; - return files; - } - } - // The filter string also controls how the AddExtension feature behaves. For // details, see the ProcessFileNames method. /// @@ -395,16 +268,16 @@ public int FilterIndex // Public Events // //--------------------------------------------------- - #region Public Events - #endregion Public Events + // #region Public Events + // #endregion Public Events //--------------------------------------------------- // // Protected Methods // //--------------------------------------------------- - #region Protected Methods - #endregion Protected Methods + // #region Protected Methods + // #endregion Protected Methods //--------------------------------------------------- // @@ -466,34 +339,46 @@ internal virtual bool PromptUserIfAppropriate(string fileName) #endregion Internal Methods - //--------------------------------------------------- - // - // Internal Properties - // - //--------------------------------------------------- - #region Internal Properties + #region Internal and Protected Methods - /// - /// In cases where we need to return an array of strings, we return - /// a clone of the array. We also need to make sure we return a - /// string[0] instead of a null if we don't have any filenames. - /// - internal string[] FileNamesInternal + private protected override void PrepareDialog(IFileDialog dialog) { - get + base.PrepareDialog(dialog); + + dialog.SetFileName(CriticalFileName); + + dialog.SetDefaultExtension(DefaultExt); + + COMDLG_FILTERSPEC[] filterItems = GetFilterItems(Filter); + if (filterItems.Length > 0) { - if (_fileNames == null) - { - return Array.Empty(); - } - else - { - return (string[])_fileNames.Clone(); - } + dialog.SetFileTypes((uint)filterItems.Length, filterItems); + dialog.SetFileTypeIndex(unchecked((uint)FilterIndex)); } } - #endregion Internal Properties + private protected override bool TryHandleFileOk(IFileDialog dialog, out object restoreState) + { + restoreState = _filterIndex; + uint filterIndexTemp = dialog.GetFileTypeIndex(); + _filterIndex = unchecked((int)filterIndexTemp); + return ProcessFileNames(); + } + + private protected override void RevertFileOk(object state) + { + _filterIndex = (int)state; + } + + #endregion + + //--------------------------------------------------- + // + // Internal Properties + // + //--------------------------------------------------- + //#region Internal Properties + //#endregion Internal Properties //--------------------------------------------------- // @@ -524,7 +409,6 @@ private void Initialize() // // Initialize additional properties // - _fileNames = null; _defaultExtension = null; _filter = null; _filterIndex = 1; // The index of the first filter entry is 1, not 0. @@ -552,9 +436,9 @@ private bool ProcessFileNames() // For each filename: // - Process AddExtension // - Call PromptUserIfAppropriate to display necessary dialog boxes. - for (int i = 0; i < _fileNames.Length; i++) + for (int i = 0; i < MutableFileNames.Length; i++) { - string fileName = _fileNames[i]; + string fileName = MutableFileNames[i]; // If AddExtension is enabled and we do not already have an extension: if (AddExtension && !Path.HasExtension(fileName)) @@ -602,7 +486,7 @@ private bool ProcessFileNames() } } // Store this filename back in the _fileNames array. - _fileNames[i] = fileName; + MutableFileNames[i] = fileName; } // Call PromptUserIfAppropriate to show necessary dialog boxes. @@ -629,43 +513,40 @@ private void PromptFileNotFound(string fileName) System.Windows.MessageBoxButton.OK, MessageBoxImage.Warning); } - #endregion Private Methods - - //--------------------------------------------------- - // - // Private Properties - // - //--------------------------------------------------- - #region Private Properties - - // If multiple files are selected, we only return the first filename. - /// - /// Gets a string containing the full path of the file selected in - /// the file dialog box. - /// - private string CriticalFileName + private static COMDLG_FILTERSPEC[] GetFilterItems(string filter) { - get + // Expecting pipe delimited filter string pairs. + // First is the label, second is semi-colon delimited list of extensions. + var extensions = new List(); + + if (!string.IsNullOrEmpty(filter)) { - if (_fileNames == null) // No filename stored internally... - { - return String.Empty; // So we return String.Empty - } - else + string[] tokens = filter.Split('|'); + if (0 == tokens.Length % 2) { - // Return the first filename in the array if it is non-empty. - if (_fileNames[0].Length > 0) - { - return _fileNames[0]; - } - else + for (int i = 1; i < tokens.Length; i += 2) { - return String.Empty; + extensions.Add( + new COMDLG_FILTERSPEC + { + pszName = tokens[i - 1], + pszSpec = tokens[i], + }); } } } + return extensions.ToArray(); } + #endregion Private Methods + + //--------------------------------------------------- + // + // Private Properties + // + //--------------------------------------------------- + #region Private Properties + /// /// Extracts the file extensions specified by the current file filter into /// an array of strings. None of the extensions contain .'s, and the @@ -747,99 +628,6 @@ private string[] GetFilterExtensions() #endregion Private Properties - #region Vista COM interfaces Augmentation - - #region Internal and Protected Methods - - private protected abstract string[] ProcessFiles(IFileDialog dialog); - - #endregion - - #region Internal Methods - - private protected override void PrepareDialog(IFileDialog dialog) - { - base.PrepareDialog(dialog); - - dialog.SetFileName(CriticalFileName); - - dialog.SetDefaultExtension(DefaultExt); - - COMDLG_FILTERSPEC[] filterItems = GetFilterItems(Filter); - if (filterItems.Length > 0) - { - dialog.SetFileTypes((uint)filterItems.Length, filterItems); - dialog.SetFileTypeIndex(unchecked((uint)FilterIndex)); - } - } - - #endregion - - #region Private Methods - - private protected override bool HandleFileOk(IFileDialog dialog) - { - if (!base.HandleFileOk(dialog)) - { - return false; - } - - int saveFilterIndex = _filterIndex; - string[] saveFileNames = _fileNames; - bool ok = false; - - try - { - uint filterIndexTemp = dialog.GetFileTypeIndex(); - _filterIndex = unchecked((int)filterIndexTemp); - _fileNames = ProcessFiles(dialog); - if (ProcessFileNames()) - { - var cancelArgs = new CancelEventArgs(); - OnFileOk(cancelArgs); - ok = !cancelArgs.Cancel; - } - } - finally - { - if (!ok) - { - _fileNames = saveFileNames; - _filterIndex = saveFilterIndex; - } - } - return ok; - } - - private static COMDLG_FILTERSPEC[] GetFilterItems(string filter) - { - // Expecting pipe delimited filter string pairs. - // First is the label, second is semi-colon delimited list of extensions. - var extensions = new List(); - - if (!string.IsNullOrEmpty(filter)) - { - string[] tokens = filter.Split('|'); - if (0 == tokens.Length % 2) - { - for (int i = 1; i < tokens.Length; i += 2) - { - extensions.Add( - new COMDLG_FILTERSPEC - { - pszName = tokens[i - 1], - pszSpec = tokens[i], - }); - } - } - } - return extensions.ToArray(); - } - - #endregion - - #endregion - //--------------------------------------------------- // // Private Fields @@ -857,11 +645,6 @@ private static COMDLG_FILTERSPEC[] GetFilterItems(string filter) // the user selected afterwards.) This // index is 1-based, not 0-based. - // This is the array that stores the filename(s) the user selected in the - // dialog box. If Multiselect is not enabled, only the first element - // of this array will be used. - private string[] _fileNames; - #endregion Private Fields } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs index 7605292b00e..b33b4211d7f 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs @@ -62,7 +62,7 @@ public OpenFileDialog() : base() /// whether or not the Read Only checkbox is checked in the dialog. /// /// The filename used to open the file is the first element of the - /// FileNamesInternal array. + /// FileNames array. /// /// Thrown if there are no filenames stored in the OpenFileDialog. /// @@ -71,29 +71,16 @@ public OpenFileDialog() : base() /// public Stream OpenFile() { - string filename = null; - - // FileNamesInternal never returns null. - // If the dialog hasn't yet been shown, it returns an array of 0 items. - string[] cachedFileNames = FileNamesInternal; - if (cachedFileNames.Length != 0) - { - filename = cachedFileNames[0]; - } + string filename = CriticalFileName; // If we got an empty or null filename, throw an exception to // tell the user we don't have any files to open. - if (String.IsNullOrEmpty(filename)) + if (string.IsNullOrEmpty(filename)) { throw new InvalidOperationException(SR.Get(SRID.FileNameMustNotBeNull)); } - FileStream fileStream = null; - - fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read); - - // Create a new FileStream from the file and return it. - return fileStream; + return new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read); } /// @@ -108,32 +95,28 @@ public Stream OpenFile() /// public Stream[] OpenFiles() { - // Cache FileNamesInternal to avoid perf issues as per + // Cache FileNames to avoid perf issues as per // FxCop #CA1817 - String[] cachedFileNames = FileNamesInternal; + string[] cachedFileNames = CloneFileNames(); // Create an array to hold the streams that is exactly - // as long as FileNamesInternal. + // as long as FileNames. Stream[] streams = new Stream[cachedFileNames.Length]; - // For each element in FileNamesInternal: + // For each element in FileNames: for (int i = 0; i < cachedFileNames.Length; i++) { - // Verify that the filename at this index in the FileNamesInternal + // Verify that the filename at this index in the FileNames // array is not null or empty. string filename = cachedFileNames[i]; - if (String.IsNullOrEmpty(filename)) + if (string.IsNullOrEmpty(filename)) { throw new InvalidOperationException(SR.Get(SRID.FileNameMustNotBeNull)); } - FileStream fileStream = null; - - fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read); - // Open the file and add it to the list of streams. - streams[i] = fileStream; + streams[i] = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read); } // Return the array of open streams. @@ -240,28 +223,6 @@ public bool ShowReadOnly //--------------------------------------------------- #region Internal Methods - private protected override string[] ProcessFiles(IFileDialog dialog) - { - var openDialog = (IFileOpenDialog)dialog; - if (Multiselect) - { - IShellItemArray results = openDialog.GetResults(); - uint count = results.GetCount(); - string[] paths = new string[count]; - for (uint i = 0; i < count; ++i) - { - IShellItem item = results.GetItemAt(i); - paths[i] = item.GetDisplayName(SIGDN.DESKTOPABSOLUTEPARSING); - } - return paths; - } - else - { - IShellItem item = openDialog.GetResult(); - return new[] { item.GetDisplayName(SIGDN.DESKTOPABSOLUTEPARSING) }; - } - } - private protected override IFileDialog CreateDialog() { return (IFileDialog)Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid(CLSID.FileOpenDialog))); diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFolderDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFolderDialog.cs index 1e1b23e6f93..f0a7db686bf 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFolderDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFolderDialog.cs @@ -13,20 +13,12 @@ namespace Microsoft.Win32 { using System; - using System.Collections.Generic; - using System.ComponentModel; - using System.IO; - using System.Security; - using System.Text; - using System.Windows; using MS.Internal.AppModel; using MS.Internal.Interop; - using MS.Internal.PresentationFramework; - using MS.Win32; /// - /// Represents a common dialog box that allows the user to open one or more file(s). + /// Represents a common dialog box that allows the user to open one or more folder(s). /// This class cannot be inherited. /// public sealed class OpenFolderDialog : CommonItemDialog @@ -73,20 +65,6 @@ public override void Reset() Initialize(); } - /// - /// Returns a string representation of the file dialog with key information - /// for debugging purposes. - /// - // We overload ToString() so that we can provide a useful representation of - // this object for users' debugging purposes. It provides the full pathname for - // any folder selected. - public override string ToString() - { - StringBuilder sb = new StringBuilder(base.ToString() + ", FolderName: "); - sb.Append(FolderName); - return sb.ToString(); - } - #endregion Public Methods //--------------------------------------------------- @@ -96,62 +74,25 @@ public override string ToString() //--------------------------------------------------- #region Public Properties + // FOS_ALLOWMULTISELECT + // Enables the user to select multiple items in the open dialog. + // /// - /// Gets a string containing the filename component of the - /// folder selected in the dialog box. - /// - /// Example: if FolderName = "c:\windows\sytem32" , - /// SafeFolderName = "system32" + /// Gets or sets an option flag indicating whether the + /// dialog box allows multiple folders to be selected. /// - public string SafeFolderName + public bool Multiselect { get { - // Use the FileName property to avoid directly accessing - // the _fileNames field, then call Path.GetFileName - // to do the actual work of stripping out the folder name - // from the path. - string safeFN = Path.GetFileName(CriticalFileName); - - // Check to make sure Path.GetFileName does not return null. - // If it does, set safeFN to String.Empty instead to accomodate - // programmers that fail to check for null when reading strings. - if (safeFN == null) - { - safeFN = String.Empty; - } - - return safeFN; - } - } - - /// - /// Gets or sets a string containing the full path of the file selected in - /// the file dialog box. - /// - public string FolderName - { - get - { - return CriticalFileName; + return GetOption(FOS.ALLOWMULTISELECT); } set { - // Allow users to set a filename to stored in _fileNames. - // If null is passed in, we clear the entire list. - // If we get a string, we clear the entire list and make a new one-element - // array with the new string. - if (value == null) - { - _fileNames = null; - } - else - { - _fileNames = new string[] { value }; - } + SetOption(FOS.ALLOWMULTISELECT, value); } } - + #endregion Public Properties //--------------------------------------------------- @@ -177,53 +118,11 @@ public string FolderName //--------------------------------------------------- #region Internal Methods - private string[] ProcessFolders(IFileDialog dialog) - { - var openDialog = (IFileOpenDialog)dialog; - IShellItem item = openDialog.GetResult(); - return new[] { item.GetDisplayName(SIGDN.DESKTOPABSOLUTEPARSING) }; - } - private protected override IFileDialog CreateDialog() { return (IFileDialog)Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid(CLSID.FileOpenDialog))); } - private protected override void PrepareDialog(IFileDialog dialog) - { - base.PrepareDialog(dialog); - - dialog.SetFileName(CriticalFileName); - } - - private protected override bool HandleFileOk(IFileDialog dialog) - { - if (!base.HandleFileOk(dialog)) - { - return false; - } - - string[] saveFileNames = _fileNames; - bool ok = false; - - try - { - _fileNames = ProcessFolders(dialog); - - var cancelArgs = new CancelEventArgs(); - OnFileOk(cancelArgs); - ok = !cancelArgs.Cancel; - } - finally - { - if (!ok) - { - _fileNames = saveFileNames; - } - } - return ok; - } - #endregion Internal Methods //--------------------------------------------------- @@ -263,7 +162,7 @@ private void Initialize() // FOS_FILEMUSTEXIST // Folder must exist. Otherwise, the folder name remaining in // text box might be returned as a nested folder. This flag is - // now enforced by FOS_PICKFOLDERS. + // now enforced by FOS_PICKFOLDERS in the native API. SetOption(FOS.FILEMUSTEXIST, true); // FOS_PICKFOLDERS @@ -278,49 +177,15 @@ private void Initialize() // Private Properties // //--------------------------------------------------- - #region Private Properties - - /// - /// Gets a string containing the full path of the file selected in - /// the file dialog box. - /// - private string CriticalFileName - { - get - { - if (_fileNames == null) // No filename stored internally... - { - return String.Empty; // So we return String.Empty - } - else - { - // Return the first filename in the array if it is non-empty. - if (_fileNames[0].Length > 0) - { - return _fileNames[0]; - } - else - { - return String.Empty; - } - } - } - } - - #endregion Private Properties + //#region Private Properties + //#endregion Private Properties //--------------------------------------------------- // // Private Fields // //--------------------------------------------------- - #region Private Fields - - // This is the array that stores the filename(s) the user selected in the - // dialog box. If Multiselect is not enabled, only the first element - // of this array will be used. - private string[] _fileNames; - - #endregion Private Fields + //#region Private Fields + //#endregion Private Fields } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/SaveFileDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/SaveFileDialog.cs index 175ede6aa0a..d2f3692d2cf 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/SaveFileDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/SaveFileDialog.cs @@ -43,8 +43,7 @@ public sealed class SaveFileDialog : FileDialog /// /// Initializes a new instance of the SaveFileDialog class. /// - public SaveFileDialog() - : base() + public SaveFileDialog() : base() { Initialize(); } @@ -72,14 +71,12 @@ public SaveFileDialog() public Stream OpenFile() { - // Extract the first filename from the FileNamesInternal list. - // We can do this safely because FileNamesInternal never returns - // null - if _fileNames is null, FileNamesInternal returns Array.Empty(); - string filename = FileNamesInternal.Length > 0 ? FileNamesInternal[0] : null; + // Extract the first filename from the FileNames list. + string filename = CriticalFileName; // If we got an empty or null filename, throw an exception to // tell the user we don't have any files to open. - if (String.IsNullOrEmpty(filename)) + if (string.IsNullOrEmpty(filename)) { throw new InvalidOperationException(SR.Get(SRID.FileNameMustNotBeNull)); } @@ -203,8 +200,7 @@ internal override bool PromptUserIfAppropriate(string fileName) return false; } - bool fExist = File.Exists(Path.GetFullPath(fileName)); - + bool fExist = File.Exists(fileName); // If the file does not exist, check if CreatePrompt is // set. If so, display the appropriate message box and act @@ -234,12 +230,6 @@ internal override bool PromptUserIfAppropriate(string fileName) return true; } - private protected override string[] ProcessFiles(IFileDialog dialog) - { - IShellItem item = dialog.GetResult(); - return new[] { item.GetDisplayName(SIGDN.DESKTOPABSOLUTEPARSING) }; - } - private protected override IFileDialog CreateDialog() { return (IFileDialog)Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid(CLSID.FileSaveDialog))); diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs index cf4df4f3599..dcc0c2b45f2 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs @@ -17,7 +17,11 @@ protected CommonItemDialog() { } public System.Collections.Generic.IList CustomPlaces { get { throw null; } set { } } public bool DereferenceLinks { get { throw null; } set { } } public string InitialDirectory { get { throw null; } set { } } + public string FileName { get { throw null; } set { } } + public string[] FileNames { get { throw null; } } public bool RestoreDirectory { get { throw null; } set { } } + public string SafeFileName { get { throw null; } } + public string[] SafeFileNames { get { throw null; } } public string Title { get { throw null; } set { } } public bool ValidateNames { get { throw null; } set { } } public event System.ComponentModel.CancelEventHandler FileOk { add { } remove { } } @@ -31,15 +35,11 @@ public abstract partial class FileDialog : Microsoft.Win32.CommonItemDialog { protected FileDialog() { } public bool AddExtension { get { throw null; } set { } } - public virtual bool CheckFileExists { get { throw null; } set { } } + public bool CheckFileExists { get { throw null; } set { } } public bool CheckPathExists { get { throw null; } set { } } public string DefaultExt { get { throw null; } set { } } - public string FileName { get { throw null; } set { } } - public string[] FileNames { get { throw null; } } public string Filter { get { throw null; } set { } } public int FilterIndex { get { throw null; } set { } } - public string SafeFileName { get { throw null; } } - public string[] SafeFileNames { get { throw null; } } public override void Reset() { } public override string ToString() { throw null; } } @@ -83,8 +83,7 @@ public override void Reset() { } public sealed partial class OpenFolderDialog : Microsoft.Win32.CommonItemDialog { public OpenFolderDialog() { } - public string FolderName { get { throw null; } set { } } - public string SafeFolderName { get { throw null; } } + public bool Multiselect { get { throw null; } set { } } public override void Reset() { } } public sealed partial class SaveFileDialog : Microsoft.Win32.FileDialog From bc3620d1e0c88ce3d60453844943b2eed4070599 Mon Sep 17 00:00:00 2001 From: miloush Date: Mon, 17 Apr 2023 17:47:45 +0200 Subject: [PATCH 03/21] SRID deprecation --- .../PresentationFramework/Microsoft/Win32/OpenFileDialog.cs | 4 ++-- .../PresentationFramework/Microsoft/Win32/SaveFileDialog.cs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs index b33b4211d7f..0b18c53f131 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs @@ -77,7 +77,7 @@ public Stream OpenFile() // tell the user we don't have any files to open. if (string.IsNullOrEmpty(filename)) { - throw new InvalidOperationException(SR.Get(SRID.FileNameMustNotBeNull)); + throw new InvalidOperationException(SR.FileNameMustNotBeNull); } return new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read); @@ -112,7 +112,7 @@ public Stream[] OpenFiles() if (string.IsNullOrEmpty(filename)) { - throw new InvalidOperationException(SR.Get(SRID.FileNameMustNotBeNull)); + throw new InvalidOperationException(SR.FileNameMustNotBeNull); } // Open the file and add it to the list of streams. diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/SaveFileDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/SaveFileDialog.cs index d2f3692d2cf..5ac6bbb87aa 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/SaveFileDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/SaveFileDialog.cs @@ -78,7 +78,7 @@ public Stream OpenFile() // tell the user we don't have any files to open. if (string.IsNullOrEmpty(filename)) { - throw new InvalidOperationException(SR.Get(SRID.FileNameMustNotBeNull)); + throw new InvalidOperationException(SR.FileNameMustNotBeNull); } // Create a new FileStream from the file and return it. @@ -284,7 +284,7 @@ private void Initialize() /// private bool PromptFileCreate(string fileName) { - return MessageBoxWithFocusRestore(SR.Get(SRID.FileDialogCreatePrompt, fileName), + return MessageBoxWithFocusRestore(SR.Format(SR.FileDialogCreatePrompt, fileName), MessageBoxButton.YesNo, MessageBoxImage.Warning); } @@ -296,7 +296,7 @@ private bool PromptFileCreate(string fileName) /// private bool PromptFileOverwrite(string fileName) { - return MessageBoxWithFocusRestore(SR.Get(SRID.FileDialogOverwritePrompt, fileName), + return MessageBoxWithFocusRestore(SR.Format(SR.FileDialogOverwritePrompt, fileName), MessageBoxButton.YesNo, MessageBoxImage.Warning); } From 599890239d59e53f8f69cb012e3c6c6d80c52e1e Mon Sep 17 00:00:00 2001 From: miloush Date: Wed, 19 Apr 2023 15:57:20 +0100 Subject: [PATCH 04/21] API review update - private protected ctros --- .../PresentationFramework/Microsoft/Win32/CommonItemDialog.cs | 2 +- .../src/PresentationFramework/Microsoft/Win32/FileDialog.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs index de277119ec4..ca91daa525d 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs @@ -48,7 +48,7 @@ public abstract class CommonItemDialog : CommonDialog /// In an inherited class, initializes a new instance of /// the System.Windows.CommonItemDialog class. /// - protected CommonItemDialog() + private protected CommonItemDialog() { // Call Initialize to set defaults for fields // and to set defaults for some option flags. diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs index 78de53cecbd..686178ea0fe 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs @@ -46,7 +46,7 @@ public abstract class FileDialog : CommonItemDialog /// In an inherited class, initializes a new instance of /// the System.Windows.FileDialog class. /// - protected FileDialog() + private protected FileDialog() { // Call Initialize to set defaults for fields // and to set defaults for some option flags. From 5f6e1808984320147ee12d1ca1cc880872eacd92 Mon Sep 17 00:00:00 2001 From: miloush Date: Sun, 11 Jun 2023 20:43:34 +0100 Subject: [PATCH 05/21] API review update - FolderNames --- .../Microsoft/Win32/CommonItemDialog.cs | 117 +---------------- .../Microsoft/Win32/FileDialog.cs | 116 +++++++++++++++++ .../Microsoft/Win32/OpenFolderDialog.cs | 118 ++++++++++++++++++ .../ref/PresentationFramework.cs | 12 +- 4 files changed, 243 insertions(+), 120 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs index ca91daa525d..f5a714d1128 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs @@ -78,19 +78,6 @@ public override void Reset() Initialize(); } - /// - /// Returns a string representation of the file dialog with key information - /// for debugging purposes. - /// - // We overload ToString() so that we can provide a useful representation of - // this object for users' debugging purposes. - public override string ToString() - { - StringBuilder sb = new StringBuilder(base.ToString() + ": Title: " + Title + ", FileName: "); - sb.Append(FileName); - return sb.ToString(); - } - #endregion Public Methods //--------------------------------------------------- @@ -100,109 +87,6 @@ public override string ToString() //--------------------------------------------------- #region Public Properties - /// - /// Gets a string containing the filename component of the - /// file selected in the dialog box. - /// - /// Example: if FileName = "c:\windows\explorer.exe" , - /// SafeFileName = "explorer.exe" - /// - public string SafeFileName - { - get - { - // Use the FileName property to avoid directly accessing - // the _fileNames field, then call Path.GetFileName - // to do the actual work of stripping out the file name - // from the path. - string safeFN = Path.GetFileName(CriticalFileName); - - // Check to make sure Path.GetFileName does not return null. - // If it does, set safeFN to String.Empty instead to accomodate - // programmers that fail to check for null when reading strings. - if (safeFN == null) - { - safeFN = String.Empty; - } - - return safeFN; - } - } - - /// - /// Gets a string array containing the filename of each file selected - /// in the dialog box. - /// - public string[] SafeFileNames - { - get - { - // Retrieve the existing filenames into an array, then make - // another array of the same length to hold the safe version. - string[] unsafeFileNames = CloneFileNames(); - string[] safeFileNames = new string[unsafeFileNames.Length]; - - for (int i = 0; i < unsafeFileNames.Length; i++) - { - // Call Path.GetFileName to retrieve only the filename - // component of the current full path. - safeFileNames[i] = Path.GetFileName(unsafeFileNames[i]); - - // Check to make sure Path.GetFileName does not return null. - // If it does, set this filename to String.Empty instead to accomodate - // programmers that fail to check for null when reading strings. - if (safeFileNames[i] == null) - { - safeFileNames[i] = String.Empty; - } - } - - return safeFileNames; - } - } - - // If multiple files are selected, we only return the first filename. - /// - /// Gets or sets a string containing the full path of the file or folder selected in - /// the file dialog box. - /// - public string FileName - { - get - { - return CriticalFileName; - } - set - { - - // Allow users to set a filename to stored in _fileNames. - // If null is passed in, we clear the entire list. - // If we get a string, we clear the entire list and make a new one-element - // array with the new string. - if (value == null) - { - _fileNames = null; - } - else - { - // UNDONE : ChrisAn: This broke the save file dialog. - //string temp = Path.GetFullPath(value); // ensure filename is valid... - _fileNames = new string[] { value }; - } - } - } - - /// - /// Gets the file names of all selected files or folders in the dialog box. - /// - public string[] FileNames - { - get - { - return CloneFileNames(); - } - } - // The actual flag is FOS_NODEREFERENCELINKS (set = do not dereference, unset = deref) - // while we have true = dereference and false=do not dereference. Because we expose // the opposite of the Windows flag as a property to be clearer, we need to negate @@ -532,6 +416,7 @@ private protected string CriticalFileName private protected string[] MutableFileNames { get { return _fileNames; } + set { _fileNames = value; } } /// diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs index 686178ea0fe..8f8447ffdc9 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs @@ -77,6 +77,19 @@ public override void Reset() Initialize(); } + /// + /// Returns a string representation of the file dialog with key information + /// for debugging purposes. + /// + // We overload ToString() so that we can provide a useful representation of + // this object for users' debugging purposes. + public override string ToString() + { + StringBuilder sb = new StringBuilder(base.ToString() + ": Title: " + Title + ", FileName: "); + sb.Append(FileName); + return sb.ToString(); + } + #endregion Public Methods //--------------------------------------------------- @@ -86,6 +99,109 @@ public override void Reset() //--------------------------------------------------- #region Public Properties + /// + /// Gets a string containing the filename component of the + /// file selected in the dialog box. + /// + /// Example: if FileName = "c:\windows\explorer.exe" , + /// SafeFileName = "explorer.exe" + /// + public string SafeFileName + { + get + { + // Use the FileName property to avoid directly accessing + // the _fileNames field, then call Path.GetFileName + // to do the actual work of stripping out the file name + // from the path. + string safeFN = Path.GetFileName(CriticalFileName); + + // Check to make sure Path.GetFileName does not return null. + // If it does, set safeFN to String.Empty instead to accomodate + // programmers that fail to check for null when reading strings. + if (safeFN == null) + { + safeFN = String.Empty; + } + + return safeFN; + } + } + + /// + /// Gets a string array containing the filename of each file selected + /// in the dialog box. + /// + public string[] SafeFileNames + { + get + { + // Retrieve the existing filenames into an array, then make + // another array of the same length to hold the safe version. + string[] unsafeFileNames = CloneFileNames(); + string[] safeFileNames = new string[unsafeFileNames.Length]; + + for (int i = 0; i < unsafeFileNames.Length; i++) + { + // Call Path.GetFileName to retrieve only the filename + // component of the current full path. + safeFileNames[i] = Path.GetFileName(unsafeFileNames[i]); + + // Check to make sure Path.GetFileName does not return null. + // If it does, set this filename to String.Empty instead to accomodate + // programmers that fail to check for null when reading strings. + if (safeFileNames[i] == null) + { + safeFileNames[i] = String.Empty; + } + } + + return safeFileNames; + } + } + + // If multiple files are selected, we only return the first filename. + /// + /// Gets or sets a string containing the full path of the file or folder selected in + /// the file dialog box. + /// + public string FileName + { + get + { + return CriticalFileName; + } + set + { + + // Allow users to set a filename to stored in _fileNames. + // If null is passed in, we clear the entire list. + // If we get a string, we clear the entire list and make a new one-element + // array with the new string. + if (value == null) + { + MutableFileNames = null; + } + else + { + // UNDONE : ChrisAn: This broke the save file dialog. + //string temp = Path.GetFullPath(value); // ensure filename is valid... + MutableFileNames = new string[] { value }; + } + } + } + + /// + /// Gets the file names of all selected files or folders in the dialog box. + /// + public string[] FileNames + { + get + { + return CloneFileNames(); + } + } + // // The behavior governed by this property depends // on whether CheckFileExists is set and whether the diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFolderDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFolderDialog.cs index f0a7db686bf..4be89664fa1 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFolderDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFolderDialog.cs @@ -13,6 +13,8 @@ namespace Microsoft.Win32 { using System; + using System.IO; + using System.Text; using MS.Internal.AppModel; using MS.Internal.Interop; @@ -65,6 +67,19 @@ public override void Reset() Initialize(); } + /// + /// Returns a string representation of the file dialog with key information + /// for debugging purposes. + /// + // We overload ToString() so that we can provide a useful representation of + // this object for users' debugging purposes. + public override string ToString() + { + StringBuilder sb = new StringBuilder(base.ToString() + ": Title: " + Title + ", FolderName: "); + sb.Append(FolderName); + return sb.ToString(); + } + #endregion Public Methods //--------------------------------------------------- @@ -74,6 +89,109 @@ public override void Reset() //--------------------------------------------------- #region Public Properties + /// + /// Gets a string containing the filename component of the + /// folder selected in the dialog box. + /// + /// Example: if FolderName = "c:\windows" , + /// SafeFolderName = "windows" + /// + public string SafeFolderName + { + get + { + // Use the FileName property to avoid directly accessing + // the _fileNames field, then call Path.GetFileName + // to do the actual work of stripping out the file name + // from the path. + string safeFN = Path.GetFileName(CriticalFileName); + + // Check to make sure Path.GetFileName does not return null. + // If it does, set safeFN to String.Empty instead to accomodate + // programmers that fail to check for null when reading strings. + if (safeFN == null) + { + safeFN = String.Empty; + } + + return safeFN; + } + } + + /// + /// Gets a string array containing the name of each folder selected + /// in the dialog box. + /// + public string[] SafeFolderNames + { + get + { + // Retrieve the existing filenames into an array, then make + // another array of the same length to hold the safe version. + string[] unsafeFileNames = CloneFileNames(); + string[] safeFileNames = new string[unsafeFileNames.Length]; + + for (int i = 0; i < unsafeFileNames.Length; i++) + { + // Call Path.GetFileName to retrieve only the filename + // component of the current full path. + safeFileNames[i] = Path.GetFileName(unsafeFileNames[i]); + + // Check to make sure Path.GetFileName does not return null. + // If it does, set this filename to String.Empty instead to accomodate + // programmers that fail to check for null when reading strings. + if (safeFileNames[i] == null) + { + safeFileNames[i] = String.Empty; + } + } + + return safeFileNames; + } + } + + // If multiple folders are selected, we only return the first folder name. + /// + /// Gets or sets a string containing the full path of the file or folder selected in + /// the file dialog box. + /// + public string FolderName + { + get + { + return CriticalFileName; + } + set + { + + // Allow users to set a filename to stored in _fileNames. + // If null is passed in, we clear the entire list. + // If we get a string, we clear the entire list and make a new one-element + // array with the new string. + if (value == null) + { + MutableFileNames = null; + } + else + { + // UNDONE : ChrisAn: This broke the save file dialog. + //string temp = Path.GetFullPath(value); // ensure filename is valid... + MutableFileNames = new string[] { value }; + } + } + } + + /// + /// Gets the file names of all selected files or folders in the dialog box. + /// + public string[] FolderNames + { + get + { + return CloneFileNames(); + } + } + // FOS_ALLOWMULTISELECT // Enables the user to select multiple items in the open dialog. // diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs index dcc0c2b45f2..2074a57f5a9 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs @@ -17,11 +17,7 @@ protected CommonItemDialog() { } public System.Collections.Generic.IList CustomPlaces { get { throw null; } set { } } public bool DereferenceLinks { get { throw null; } set { } } public string InitialDirectory { get { throw null; } set { } } - public string FileName { get { throw null; } set { } } - public string[] FileNames { get { throw null; } } public bool RestoreDirectory { get { throw null; } set { } } - public string SafeFileName { get { throw null; } } - public string[] SafeFileNames { get { throw null; } } public string Title { get { throw null; } set { } } public bool ValidateNames { get { throw null; } set { } } public event System.ComponentModel.CancelEventHandler FileOk { add { } remove { } } @@ -38,9 +34,13 @@ protected FileDialog() { } public bool CheckFileExists { get { throw null; } set { } } public bool CheckPathExists { get { throw null; } set { } } public string DefaultExt { get { throw null; } set { } } + public string FileName { get { throw null; } set { } } + public string[] FileNames { get { throw null; } } public string Filter { get { throw null; } set { } } public int FilterIndex { get { throw null; } set { } } public override void Reset() { } + public string SafeFileName { get { throw null; } } + public string[] SafeFileNames { get { throw null; } } public override string ToString() { throw null; } } public sealed partial class FileDialogCustomPlace @@ -83,8 +83,12 @@ public override void Reset() { } public sealed partial class OpenFolderDialog : Microsoft.Win32.CommonItemDialog { public OpenFolderDialog() { } + public string FolderName { get { throw null; } set { } } + public string[] FolderNames { get { throw null; } } public bool Multiselect { get { throw null; } set { } } public override void Reset() { } + public string SafeFolderName { get { throw null; } } + public string[] SafeFolderNames { get { throw null; } } } public sealed partial class SaveFileDialog : Microsoft.Win32.FileDialog { From 6b8393df5c72c85098cd1f70fc3248883c78abeb Mon Sep 17 00:00:00 2001 From: miloush Date: Mon, 12 Jun 2023 10:40:06 +0100 Subject: [PATCH 06/21] FileOk event disunification --- ...esentationFramework-ref-Net48.baseline.txt | 1 + .../Microsoft/Win32/CommonItemDialog.cs | 39 +++++-------------- .../Microsoft/Win32/FileDialog.cs | 36 +++++++++++++++-- .../Microsoft/Win32/OpenFolderDialog.cs | 32 ++++++++++++++- .../ref/PresentationFramework.cs | 9 +++-- 5 files changed, 77 insertions(+), 40 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/ApiCompat/Baselines/PresentationFramework-ref-Net48.baseline.txt b/src/Microsoft.DotNet.Wpf/ApiCompat/Baselines/PresentationFramework-ref-Net48.baseline.txt index d7cd9a976bf..65f1e37d426 100644 --- a/src/Microsoft.DotNet.Wpf/ApiCompat/Baselines/PresentationFramework-ref-Net48.baseline.txt +++ b/src/Microsoft.DotNet.Wpf/ApiCompat/Baselines/PresentationFramework-ref-Net48.baseline.txt @@ -3,6 +3,7 @@ CannotMakeMemberNonVirtual : Member 'public System.Boolean Microsoft.Win32.FileD CannotMakeMemberNonVirtual : Member 'public System.Boolean Microsoft.Win32.FileDialog.CheckFileExists.get()' is non-virtual in the implementation but is virtual in the contract. CannotMakeMemberNonVirtual : Member 'public void Microsoft.Win32.FileDialog.CheckFileExists.set(System.Boolean)' is non-virtual in the implementation but is virtual in the contract. MembersMustExist : Member 'internal MS.Internal.AppModel.IFileDialog Microsoft.Win32.FileDialog.CreateVistaDialog()' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'protected void Microsoft.Win32.FileDialog.OnFileOk(System.ComponentModel.CancelEventArgs)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'protected System.Int32 Microsoft.Win32.FileDialog.Options.get()' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'internal System.String[] Microsoft.Win32.FileDialog.ProcessVistaFiles(MS.Internal.AppModel.IFileDialog)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'internal System.Boolean Microsoft.Win32.FileDialog.RunFileDialog(MS.Win32.NativeMethods.OPENFILENAME_I)' does not exist in the implementation but it does exist in the contract. diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs index f5a714d1128..48d389b99fc 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs @@ -190,21 +190,6 @@ public bool ValidateNames #endregion Public Properties - //--------------------------------------------------- - // - // Public Events - // - //--------------------------------------------------- - #region Public Events - - /// - /// Occurs when the user clicks on the Open or Save button on a file dialog - /// box. - /// - public event CancelEventHandler FileOk; - - #endregion Public Events - //--------------------------------------------------- // // Protected Methods @@ -213,15 +198,9 @@ public bool ValidateNames #region Protected Methods /// - /// Raises the System.Windows.FileDialog.FileOk event. + /// Handles the IFileDialogEvents.OnFileOk callback. /// - protected void OnFileOk(CancelEventArgs e) - { - if (FileOk != null) - { - FileOk(this, e); - } - } + protected virtual void OnItemOk(CancelEventArgs e) { } // Because this class, FileDialog, is the parent class for both OpenFileDialog // and SaveFileDialog, this function will perform the common setup tasks @@ -241,7 +220,7 @@ protected override bool RunDialog(IntPtr hwndOwner) PrepareDialog(dialog); - using (VistaDialogEvents events = new VistaDialogEvents(dialog, HandleFileOk)) + using (VistaDialogEvents events = new VistaDialogEvents(dialog, HandleItemOk)) { return dialog.Show(hwndOwner).Succeeded; } @@ -374,7 +353,7 @@ private protected virtual void PrepareDialog(IFileDialog dialog) // The FileOk event expects all properties to be set, but if the event is cancelled, they need to be reverted. // This method is called inside a try block, and inheritors can store any data to be reverted in the revertState. - private protected virtual bool TryHandleFileOk(IFileDialog dialog, out object revertState) + private protected virtual bool TryHandleItemOk(IFileDialog dialog, out object revertState) { revertState = null; return true; @@ -382,7 +361,7 @@ private protected virtual bool TryHandleFileOk(IFileDialog dialog, out object re // This method is called inside a finally block when OK event was cancelled. // Inheritors should revert properties to the state before the dialog was shown, so that it can be shown again. - private protected virtual void RevertFileOk(object state) { } + private protected virtual void RevertItemOk(object state) { } #endregion @@ -492,7 +471,7 @@ private void Initialize() CustomPlaces = new List(); } - private bool HandleFileOk(IFileDialog dialog) + private bool HandleItemOk(IFileDialog dialog) { // When this callback occurs, the HWND is visible and we need to // grab it because it is used for various things like looking up the @@ -509,10 +488,10 @@ private bool HandleFileOk(IFileDialog dialog) IShellItem[] shellItems = ResolveResults(dialog); _fileNames = GetParsingNames(shellItems); - if (TryHandleFileOk(dialog, out saveState)) + if (TryHandleItemOk(dialog, out saveState)) { var cancelArgs = new CancelEventArgs(); - OnFileOk(cancelArgs); + OnItemOk(cancelArgs); ok = !cancelArgs.Cancel; } } @@ -520,7 +499,7 @@ private bool HandleFileOk(IFileDialog dialog) { if (!ok) { - RevertFileOk(saveState); + RevertItemOk(saveState); _fileNames = saveFileNames; } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs index 8f8447ffdc9..5f120f90999 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs @@ -21,6 +21,7 @@ namespace Microsoft.Win32 using System; using System.Collections.Generic; + using System.ComponentModel; using System.IO; using System.Text; using System.Windows; @@ -379,6 +380,21 @@ public int FilterIndex #endregion Public Properties + //--------------------------------------------------- + // + // Public Events + // + //--------------------------------------------------- + #region Public Events + + /// + /// Occurs when the user clicks on the Open or Save button on a file dialog + /// box. + /// + public event CancelEventHandler FileOk; + + #endregion Public Events + //--------------------------------------------------- // // Public Events @@ -392,8 +408,20 @@ public int FilterIndex // Protected Methods // //--------------------------------------------------- - // #region Protected Methods - // #endregion Protected Methods + #region Protected Methods + + /// + /// Raises the System.Windows.FileDialog.FileOk event. + /// + protected override void OnItemOk(CancelEventArgs e) + { + if (FileOk != null) + { + FileOk(this, e); + } + } + + #endregion Protected Methods //--------------------------------------------------- // @@ -473,7 +501,7 @@ private protected override void PrepareDialog(IFileDialog dialog) } } - private protected override bool TryHandleFileOk(IFileDialog dialog, out object restoreState) + private protected override bool TryHandleItemOk(IFileDialog dialog, out object restoreState) { restoreState = _filterIndex; uint filterIndexTemp = dialog.GetFileTypeIndex(); @@ -481,7 +509,7 @@ private protected override bool TryHandleFileOk(IFileDialog dialog, out object r return ProcessFileNames(); } - private protected override void RevertFileOk(object state) + private protected override void RevertItemOk(object state) { _filterIndex = (int)state; } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFolderDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFolderDialog.cs index 4be89664fa1..aee406fb325 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFolderDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFolderDialog.cs @@ -13,6 +13,7 @@ namespace Microsoft.Win32 { using System; + using System.ComponentModel; using System.IO; using System.Text; @@ -213,6 +214,21 @@ public bool Multiselect #endregion Public Properties + //--------------------------------------------------- + // + // Public Events + // + //--------------------------------------------------- + #region Public Events + + /// + /// Occurs when the user clicks on the Open or Save button on a file dialog + /// box. + /// + public event CancelEventHandler FolderOk; + + #endregion Public Events + //--------------------------------------------------- // // Public Events @@ -226,8 +242,20 @@ public bool Multiselect // Protected Methods // //--------------------------------------------------- - //#region Protected Methods - //#endregion Protected Methods + #region Protected Methods + + /// + /// Raises the System.Windows.FileDialog.FileOk event. + /// + protected override void OnItemOk(CancelEventArgs e) + { + if (FolderOk != null) + { + FolderOk(this, e); + } + } + + #endregion Protected Methods //--------------------------------------------------- // diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs index 2074a57f5a9..d71be1719d6 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs @@ -20,8 +20,7 @@ protected CommonItemDialog() { } public bool RestoreDirectory { get { throw null; } set { } } public string Title { get { throw null; } set { } } public bool ValidateNames { get { throw null; } set { } } - public event System.ComponentModel.CancelEventHandler FileOk { add { } remove { } } - protected void OnFileOk(System.ComponentModel.CancelEventArgs e) { } + protected void OnItemOk(System.ComponentModel.CancelEventArgs e) { } protected override bool RunDialog(System.IntPtr hwndOwner) { throw null; } public override void Reset() { } public override string ToString() { throw null; } @@ -36,6 +35,7 @@ protected FileDialog() { } public string DefaultExt { get { throw null; } set { } } public string FileName { get { throw null; } set { } } public string[] FileNames { get { throw null; } } + public event System.ComponentModel.CancelEventHandler FileOk { add { } remove { } } public string Filter { get { throw null; } set { } } public int FilterIndex { get { throw null; } set { } } public override void Reset() { } @@ -74,17 +74,18 @@ public sealed partial class OpenFileDialog : Microsoft.Win32.FileDialog { public OpenFileDialog() { } public bool Multiselect { get { throw null; } set { } } - public bool ReadOnlyChecked { get { throw null; } set { } } - public bool ShowReadOnly { get { throw null; } set { } } public System.IO.Stream OpenFile() { throw null; } public System.IO.Stream[] OpenFiles() { throw null; } + public bool ReadOnlyChecked { get { throw null; } set { } } public override void Reset() { } + public bool ShowReadOnly { get { throw null; } set { } } } public sealed partial class OpenFolderDialog : Microsoft.Win32.CommonItemDialog { public OpenFolderDialog() { } public string FolderName { get { throw null; } set { } } public string[] FolderNames { get { throw null; } } + public event System.ComponentModel.CancelEventHandler FolderOk { add { } remove { } } public bool Multiselect { get { throw null; } set { } } public override void Reset() { } public string SafeFolderName { get { throw null; } } From 7b9ed4c00c2d11294b5829546a58a3b88a771537 Mon Sep 17 00:00:00 2001 From: miloush Date: Sun, 2 Jul 2023 19:04:43 +0100 Subject: [PATCH 07/21] comments update --- .../Microsoft/Win32/CommonDialog.cs | 9 +++------ .../Microsoft/Win32/CommonItemDialog.cs | 13 +++++-------- .../Microsoft/Win32/FileDialog.cs | 4 ++-- .../Microsoft/Win32/OpenFileDialog.cs | 4 ++-- .../Microsoft/Win32/OpenFolderDialog.cs | 2 +- 5 files changed, 13 insertions(+), 19 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonDialog.cs index 9dd271c8eb6..b458a2139ba 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonDialog.cs @@ -221,7 +221,7 @@ public object Tag //--------------------------------------------------- #region Protected Methods - // This method is not used by IFileDialog API. Keeping for API compatibility with .NET Framework. + // This method is not used by IFileDialog API. Kept for compatibility as CommonDialog can be derived from. /// /// Defines the common dialog box hook procedure that is overridden to /// add specific functionality to a common dialog box. @@ -247,7 +247,6 @@ protected virtual IntPtr HookProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lP return IntPtr.Zero; } - /// /// /// When overridden in a derived class, displays a particular type of common dialog box. /// @@ -276,12 +275,10 @@ protected virtual void CheckPermissionsToShowDialog() //--------------------------------------------------- #region Internal Methods - // This method is not used by IFileDialog API. Keeping for API compatibility with .NET Framework. + // This method is not used by IFileDialog API. Kept for compatibility (see HookProc). /// /// Centers the given window on the screen. This method is used by HookProc - /// to center the dialog on the screen before it is shown. We can't mark it - /// private because we need to call it from our derived classes like - /// FileDialog. + /// to center the dialog on the screen before it is shown. /// private void MoveToScreenCenter(HandleRef hWnd) { diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs index 48d389b99fc..d17ca0c66e4 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs @@ -94,7 +94,7 @@ public override void Reset() /// /// Gets or sets a value indicating whether the dialog box returns the location /// of the file referenced by the shortcut or whether it returns the location - /// of the shortcut (.lnk). + /// of the shortcut (.lnk). Not all dialogs allow users to select shortcuts. /// public bool DereferenceLinks { @@ -202,13 +202,10 @@ public bool ValidateNames /// protected virtual void OnItemOk(CancelEventArgs e) { } - // Because this class, FileDialog, is the parent class for both OpenFileDialog - // and SaveFileDialog, this function will perform the common setup tasks - // shared between Open and Save, and will then call RunFileDialog, which is - // overridden in both of the derived classes to show the correct dialog. - // Both derived classes know about the COM IFileDialog interfaces and can - // display those if they're available and there aren't any properties set - // that should cause us not to. + // Because this class, CommonItemDialog, is the parent class for OpenFileDialog + // SaveFileDialog and OpenFolderDialog, this function will perform the common setup tasks + // shared between the dialogs, and will then call RunFileDialog, which is + // overridden in the derived classes to show the correct dialog. // /// /// Performs initialization work in preparation for calling RunFileDialog diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs index 5f120f90999..59ad4c1d45f 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs @@ -163,7 +163,7 @@ public string[] SafeFileNames // If multiple files are selected, we only return the first filename. /// - /// Gets or sets a string containing the full path of the file or folder selected in + /// Gets or sets a string containing the full path of the file selected in /// the file dialog box. /// public string FileName @@ -193,7 +193,7 @@ public string FileName } /// - /// Gets the file names of all selected files or folders in the dialog box. + /// Gets the file names of all selected files in the dialog box. /// public string[] FileNames { diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs index 0b18c53f131..c129110f137 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs @@ -174,14 +174,14 @@ public bool Multiselect } } - // OFN_HIDEREADONLY currently not supported #6346 + // ShowReadOnly currently not supported #6346 /// /// Gets or sets a value indicating whether the read-only /// check box is selected. /// public bool ReadOnlyChecked { get; set; } - // OFN_HIDEREADONLY currently not supported #6346 + // ShowReadOnly currently not supported #6346 /// /// Gets or sets a value indicating whether the dialog /// contains a read-only check box. diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFolderDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFolderDialog.cs index aee406fb325..26febe69485 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFolderDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFolderDialog.cs @@ -105,7 +105,7 @@ public string SafeFolderName // the _fileNames field, then call Path.GetFileName // to do the actual work of stripping out the file name // from the path. - string safeFN = Path.GetFileName(CriticalFileName); + string safeFN = Path.GetFileName(CriticalItemName); // Check to make sure Path.GetFileName does not return null. // If it does, set safeFN to String.Empty instead to accomodate From f7d8ffaf92b443c56c2a6b74b36c224d40950d70 Mon Sep 17 00:00:00 2001 From: miloush Date: Sun, 2 Jul 2023 20:39:42 +0100 Subject: [PATCH 08/21] FileNames -> ItemNames --- .../Microsoft/Win32/CommonItemDialog.cs | 30 +++++++++---------- .../Microsoft/Win32/FileDialog.cs | 26 ++++++++-------- .../Microsoft/Win32/OpenFileDialog.cs | 6 ++-- .../Microsoft/Win32/OpenFolderDialog.cs | 30 +++++++++---------- .../Microsoft/Win32/SaveFileDialog.cs | 4 +-- 5 files changed, 48 insertions(+), 48 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs index d17ca0c66e4..da39ec7df5f 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs @@ -320,7 +320,7 @@ private protected virtual void PrepareDialog(IFileDialog dialog) } dialog.SetTitle(Title); - dialog.SetFileName(CriticalFileName); + dialog.SetFileName(CriticalItemName); // Only accept physically backed locations. FOS options = _dialogOptions.Value | FOS.FORCEFILESYSTEM; @@ -374,13 +374,13 @@ private protected virtual void RevertItemOk(object state) { } /// Gets a string containing the full path of the file selected in /// the file dialog box. /// - private protected string CriticalFileName + private protected string CriticalItemName { get { - if (_fileNames?.Length > 0) + if (_itemNames?.Length > 0) { - return _fileNames[0]; + return _itemNames[0]; } else { @@ -389,10 +389,10 @@ private protected string CriticalFileName } } - private protected string[] MutableFileNames + private protected string[] MutableItemNames { - get { return _fileNames; } - set { _fileNames = value; } + get { return _itemNames; } + set { _itemNames = value; } } /// @@ -400,15 +400,15 @@ private protected string[] MutableFileNames /// a clone of the array. We also need to make sure we return a /// string[0] instead of a null if we don't have any filenames. /// - private protected string[] CloneFileNames() + private protected string[] CloneItemNames() { - if (_fileNames == null) + if (_itemNames == null) { return Array.Empty(); } else { - return (string[])_fileNames.Clone(); + return (string[])_itemNames.Clone(); } } @@ -460,7 +460,7 @@ private void Initialize() // // Initialize additional properties // - _fileNames = null; + _itemNames = null; _title.Value = null; _initialDirectory.Value = null; @@ -476,14 +476,14 @@ private bool HandleItemOk(IFileDialog dialog) UnsafeNativeMethods.IOleWindow oleWindow = (UnsafeNativeMethods.IOleWindow)dialog; oleWindow.GetWindow(out _hwndFileDialog); - string[] saveFileNames = _fileNames; + string[] saveItemNames = _itemNames; object saveState = null; bool ok = false; try { IShellItem[] shellItems = ResolveResults(dialog); - _fileNames = GetParsingNames(shellItems); + _itemNames = GetParsingNames(shellItems); if (TryHandleItemOk(dialog, out saveState)) { @@ -497,7 +497,7 @@ private bool HandleItemOk(IFileDialog dialog) if (!ok) { RevertItemOk(saveState); - _fileNames = saveFileNames; + _itemNames = saveItemNames; } } return ok; @@ -683,7 +683,7 @@ void IDisposable.Dispose() // This is the array that stores the item(s) the user selected in the // dialog box. If Multiselect is not enabled, only the first element // of this array will be used. - private string[] _fileNames; + private string[] _itemNames; #endregion Private Fields } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs index 59ad4c1d45f..e875280016a 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs @@ -112,10 +112,10 @@ public string SafeFileName get { // Use the FileName property to avoid directly accessing - // the _fileNames field, then call Path.GetFileName + // the _itemNames field, then call Path.GetFileName // to do the actual work of stripping out the file name // from the path. - string safeFN = Path.GetFileName(CriticalFileName); + string safeFN = Path.GetFileName(CriticalItemName); // Check to make sure Path.GetFileName does not return null. // If it does, set safeFN to String.Empty instead to accomodate @@ -139,7 +139,7 @@ public string[] SafeFileNames { // Retrieve the existing filenames into an array, then make // another array of the same length to hold the safe version. - string[] unsafeFileNames = CloneFileNames(); + string[] unsafeFileNames = CloneItemNames(); string[] safeFileNames = new string[unsafeFileNames.Length]; for (int i = 0; i < unsafeFileNames.Length; i++) @@ -170,24 +170,24 @@ public string FileName { get { - return CriticalFileName; + return CriticalItemName; } set { - // Allow users to set a filename to stored in _fileNames. + // Allow users to set a filename to stored in _itemNames. // If null is passed in, we clear the entire list. // If we get a string, we clear the entire list and make a new one-element // array with the new string. if (value == null) { - MutableFileNames = null; + MutableItemNames = null; } else { // UNDONE : ChrisAn: This broke the save file dialog. //string temp = Path.GetFullPath(value); // ensure filename is valid... - MutableFileNames = new string[] { value }; + MutableItemNames = new string[] { value }; } } } @@ -199,7 +199,7 @@ public string[] FileNames { get { - return CloneFileNames(); + return CloneItemNames(); } } @@ -489,7 +489,7 @@ private protected override void PrepareDialog(IFileDialog dialog) { base.PrepareDialog(dialog); - dialog.SetFileName(CriticalFileName); + dialog.SetFileName(CriticalItemName); dialog.SetDefaultExtension(DefaultExt); @@ -580,9 +580,9 @@ private bool ProcessFileNames() // For each filename: // - Process AddExtension // - Call PromptUserIfAppropriate to display necessary dialog boxes. - for (int i = 0; i < MutableFileNames.Length; i++) + for (int i = 0; i < MutableItemNames.Length; i++) { - string fileName = MutableFileNames[i]; + string fileName = MutableItemNames[i]; // If AddExtension is enabled and we do not already have an extension: if (AddExtension && !Path.HasExtension(fileName)) @@ -629,8 +629,8 @@ private bool ProcessFileNames() break; } } - // Store this filename back in the _fileNames array. - MutableFileNames[i] = fileName; + // Store this filename back in the _itemNames array. + MutableItemNames[i] = fileName; } // Call PromptUserIfAppropriate to show necessary dialog boxes. diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs index c129110f137..c7c00a8662c 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs @@ -71,7 +71,7 @@ public OpenFileDialog() : base() /// public Stream OpenFile() { - string filename = CriticalFileName; + string filename = CriticalItemName; // If we got an empty or null filename, throw an exception to // tell the user we don't have any files to open. @@ -95,9 +95,9 @@ public Stream OpenFile() /// public Stream[] OpenFiles() { - // Cache FileNames to avoid perf issues as per + // Cache ItemNames to avoid perf issues as per // FxCop #CA1817 - string[] cachedFileNames = CloneFileNames(); + string[] cachedFileNames = CloneItemNames(); // Create an array to hold the streams that is exactly // as long as FileNames. diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFolderDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFolderDialog.cs index 26febe69485..25b25925259 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFolderDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFolderDialog.cs @@ -101,8 +101,8 @@ public string SafeFolderName { get { - // Use the FileName property to avoid directly accessing - // the _fileNames field, then call Path.GetFileName + // Use the ItemName property to avoid directly accessing + // the _itemNames field, then call Path.GetFileName // to do the actual work of stripping out the file name // from the path. string safeFN = Path.GetFileName(CriticalItemName); @@ -127,27 +127,27 @@ public string[] SafeFolderNames { get { - // Retrieve the existing filenames into an array, then make + // Retrieve the existing folder names into an array, then make // another array of the same length to hold the safe version. - string[] unsafeFileNames = CloneFileNames(); - string[] safeFileNames = new string[unsafeFileNames.Length]; + string[] unsafeFolderNames = CloneItemNames(); + string[] safeFolderNames = new string[unsafeFolderNames.Length]; - for (int i = 0; i < unsafeFileNames.Length; i++) + for (int i = 0; i < unsafeFolderNames.Length; i++) { // Call Path.GetFileName to retrieve only the filename // component of the current full path. - safeFileNames[i] = Path.GetFileName(unsafeFileNames[i]); + safeFolderNames[i] = Path.GetFileName(unsafeFolderNames[i]); // Check to make sure Path.GetFileName does not return null. // If it does, set this filename to String.Empty instead to accomodate // programmers that fail to check for null when reading strings. - if (safeFileNames[i] == null) + if (safeFolderNames[i] == null) { - safeFileNames[i] = String.Empty; + safeFolderNames[i] = String.Empty; } } - return safeFileNames; + return safeFolderNames; } } @@ -160,24 +160,24 @@ public string FolderName { get { - return CriticalFileName; + return CriticalItemName; } set { - // Allow users to set a filename to stored in _fileNames. + // Allow users to set a filename to stored in _itemNames. // If null is passed in, we clear the entire list. // If we get a string, we clear the entire list and make a new one-element // array with the new string. if (value == null) { - MutableFileNames = null; + MutableItemNames = null; } else { // UNDONE : ChrisAn: This broke the save file dialog. //string temp = Path.GetFullPath(value); // ensure filename is valid... - MutableFileNames = new string[] { value }; + MutableItemNames = new string[] { value }; } } } @@ -189,7 +189,7 @@ public string[] FolderNames { get { - return CloneFileNames(); + return CloneItemNames(); } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/SaveFileDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/SaveFileDialog.cs index 5ac6bbb87aa..898bcc8ba3b 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/SaveFileDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/SaveFileDialog.cs @@ -71,8 +71,8 @@ public SaveFileDialog() : base() public Stream OpenFile() { - // Extract the first filename from the FileNames list. - string filename = CriticalFileName; + // Extract the first filename from the ItemNames list. + string filename = CriticalItemName; // If we got an empty or null filename, throw an exception to // tell the user we don't have any files to open. From 0265f8dd6a16e3e4edc9b26968774cd14fa451f7 Mon Sep 17 00:00:00 2001 From: miloush Date: Sun, 2 Jul 2023 20:54:09 +0100 Subject: [PATCH 09/21] removed duplicate SetFileName call --- .../src/PresentationFramework/Microsoft/Win32/FileDialog.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs index e875280016a..b9d778678c7 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs @@ -489,8 +489,6 @@ private protected override void PrepareDialog(IFileDialog dialog) { base.PrepareDialog(dialog); - dialog.SetFileName(CriticalItemName); - dialog.SetDefaultExtension(DefaultExt); COMDLG_FILTERSPEC[] filterItems = GetFilterItems(Filter); From b7349d5d0270da13f76f3fd164d83ae73a293441 Mon Sep 17 00:00:00 2001 From: miloush Date: Sun, 2 Jul 2023 20:56:13 +0100 Subject: [PATCH 10/21] move FORCEFILESYSTEM into Initialize --- .../Microsoft/Win32/CommonItemDialog.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs index da39ec7df5f..7d4399569e6 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs @@ -322,8 +322,7 @@ private protected virtual void PrepareDialog(IFileDialog dialog) dialog.SetTitle(Title); dialog.SetFileName(CriticalItemName); - // Only accept physically backed locations. - FOS options = _dialogOptions.Value | FOS.FORCEFILESYSTEM; + FOS options = _dialogOptions.Value; dialog.SetOptions(options); IList places = CustomPlaces; @@ -457,6 +456,9 @@ private void Initialize() // - Force no mini mode for the SaveFileDialog. SetOption(FOS.DEFAULTNOMINIMODE, true); + // Only accept physically backed locations. + SetOption(FOS.FORCEFILESYSTEM, true); + // // Initialize additional properties // From 38ea7131d317a8e13027b2ea27981edd721ddf5e Mon Sep 17 00:00:00 2001 From: miloush Date: Tue, 4 Jul 2023 08:29:07 +0100 Subject: [PATCH 11/21] keep RestoreDirectory in FileDialog --- .../Microsoft/Win32/CommonItemDialog.cs | 20 ------------------- .../Microsoft/Win32/FileDialog.cs | 20 +++++++++++++++++++ 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs index 7d4399569e6..de7276435d0 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs @@ -126,26 +126,6 @@ public string InitialDirectory } } - /// - /// Restores the current directory to its original value if the user - /// changed the directory while searching for files. - /// - /// This property is only valid for SaveFileDialog; it has no effect - /// when set on an OpenFileDialog. - /// - public bool RestoreDirectory - { - get - { - return GetOption(FOS.NOCHANGEDIR); - } - set - { - - SetOption(FOS.NOCHANGEDIR, value); - } - } - /// /// Gets or sets a string shown in the title bar of the file dialog. /// If this property is null, a localized default from the operating diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs index b9d778678c7..fbf24dc6707 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs @@ -378,6 +378,26 @@ public int FilterIndex } } + /// + /// Restores the current directory to its original value if the user + /// changed the directory while searching for files. + /// + /// This property is only valid for SaveFileDialog; it has no effect + /// when set on an OpenFileDialog. + /// + public bool RestoreDirectory + { + get + { + return GetOption(FOS.NOCHANGEDIR); + } + set + { + + SetOption(FOS.NOCHANGEDIR, value); + } + } + #endregion Public Properties //--------------------------------------------------- From 7f0484b686266b0bdd9242d4b8dea4dafe4c4b1c Mon Sep 17 00:00:00 2001 From: miloush Date: Thu, 6 Jul 2023 15:35:25 +0100 Subject: [PATCH 12/21] common dialogs ToString() --- .../Microsoft/Win32/CommonItemDialog.cs | 11 +++++++++++ .../Microsoft/Win32/FileDialog.cs | 4 +--- .../Microsoft/Win32/OpenFolderDialog.cs | 4 +--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs index de7276435d0..adc345abe58 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs @@ -78,6 +78,17 @@ public override void Reset() Initialize(); } + /// + /// Returns a string representation of the dialog with key information + /// for debugging purposes. + /// + // We overload ToString() so that we can provide a useful representation of + // this object for users' debugging purposes. + public override string ToString() + { + return base.ToString() + ": Title: " + Title; + } + #endregion Public Methods //--------------------------------------------------- diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs index fbf24dc6707..3ebd07da6b8 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/FileDialog.cs @@ -86,9 +86,7 @@ public override void Reset() // this object for users' debugging purposes. public override string ToString() { - StringBuilder sb = new StringBuilder(base.ToString() + ": Title: " + Title + ", FileName: "); - sb.Append(FileName); - return sb.ToString(); + return base.ToString() + ", FileName: " + FileName; } #endregion Public Methods diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFolderDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFolderDialog.cs index 25b25925259..095a11bff9a 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFolderDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFolderDialog.cs @@ -76,9 +76,7 @@ public override void Reset() // this object for users' debugging purposes. public override string ToString() { - StringBuilder sb = new StringBuilder(base.ToString() + ": Title: " + Title + ", FolderName: "); - sb.Append(FolderName); - return sb.ToString(); + return base.ToString() + ", FolderName: " + FolderName; } #endregion Public Methods From 2a6c686011db161a2182490b87835a5b255c4254 Mon Sep 17 00:00:00 2001 From: miloush Date: Mon, 12 Jun 2023 16:27:08 +0100 Subject: [PATCH 13/21] SaveFileDialog.CreateTestFile --- .../Microsoft/Win32/SaveFileDialog.cs | 18 ++++++++++++++++++ .../ref/PresentationFramework.cs | 1 + 2 files changed, 19 insertions(+) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/SaveFileDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/SaveFileDialog.cs index 898bcc8ba3b..70b09338c25 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/SaveFileDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/SaveFileDialog.cs @@ -134,6 +134,24 @@ public override void Reset() /// public bool CreatePrompt { get; set; } + /// + /// Gets or sets a value indicating whether the dialog box will attempt to create + /// a test file at the selected path (default is true). If this flag is not set, + /// the calling application must handle errors, such as denial of access, + /// discovered when the item is created. + /// + public bool CreateTestFile + { + get + { + return !GetOption(FOS.NOTESTFILECREATE); + } + set + { + SetOption(FOS.NOTESTFILECREATE, !value); + } + } + // Causes our code to generate a message box if the selected file already // exists. The user must confirm whether to overwrite the file. // diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs index d71be1719d6..7866b23c065 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs @@ -95,6 +95,7 @@ public sealed partial class SaveFileDialog : Microsoft.Win32.FileDialog { public SaveFileDialog() { } public bool CreatePrompt { get { throw null; } set { } } + public bool CreateTestFile { get { throw null; } set { } } public bool OverwritePrompt { get { throw null; } set { } } public System.IO.Stream OpenFile() { throw null; } public override void Reset() { } From 0a9b675ad1624c790493e65b2fb506f1f44df600 Mon Sep 17 00:00:00 2001 From: miloush Date: Mon, 12 Jun 2023 17:45:51 +0100 Subject: [PATCH 14/21] OpenFileDialog.ForcePreviewPane --- .../Microsoft/Win32/OpenFileDialog.cs | 19 +++++++++++++++++++ .../ref/PresentationFramework.cs | 1 + 2 files changed, 20 insertions(+) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs index c7c00a8662c..17724461a25 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs @@ -155,6 +155,25 @@ public override void Reset() //--------------------------------------------------- #region Public Properties + // FOS_FORCEPREVIEWPANEON + // Indicates to the Open dialog box that the preview pane should always be displayed. + // + /// + /// Gets or sets an option flag indicating whether the + /// dialog box forces the preview pane on. + /// + public bool ForcePreviewPane + { + get + { + return GetOption(FOS.FORCEPREVIEWPANEON); + } + set + { + SetOption(FOS.FORCEPREVIEWPANEON, value); + } + } + // FOS_ALLOWMULTISELECT // Enables the user to select multiple items in the open dialog. // diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs index 7866b23c065..83d0da79f8d 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs @@ -73,6 +73,7 @@ public static partial class FileDialogCustomPlaces public sealed partial class OpenFileDialog : Microsoft.Win32.FileDialog { public OpenFileDialog() { } + public bool ForcePreviewPane { get; set; } public bool Multiselect { get { throw null; } set { } } public System.IO.Stream OpenFile() { throw null; } public System.IO.Stream[] OpenFiles() { throw null; } From ecad64c15bbb92aff6542b2c8d31e9442eda388f Mon Sep 17 00:00:00 2001 From: miloush Date: Mon, 12 Jun 2023 19:08:26 +0100 Subject: [PATCH 15/21] CommonItemDialog.AddToRecent --- .../Microsoft/Win32/CommonItemDialog.cs | 20 +++++++++++++++++++ .../ref/PresentationFramework.cs | 1 + 2 files changed, 21 insertions(+) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs index adc345abe58..0253cc0ba9f 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs @@ -98,6 +98,26 @@ public override string ToString() //--------------------------------------------------- #region Public Properties + // FOS_DONTADDTORECENT + // Do not add the item being opened or saved to the recent documents list (SHAddToRecentDocs). + // + /// + /// Gets or sets a value indicating whether the dialog box will add the item + /// being opened or saved to the recent documents list. + /// + public bool AddToRecent + { + get + { + return !GetOption(FOS.DONTADDTORECENT); + } + set + { + + SetOption(FOS.DONTADDTORECENT, !value); + } + } + // The actual flag is FOS_NODEREFERENCELINKS (set = do not dereference, unset = deref) - // while we have true = dereference and false=do not dereference. Because we expose // the opposite of the Windows flag as a property to be clearer, we need to negate diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs index 83d0da79f8d..81e8eadc299 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs @@ -14,6 +14,7 @@ protected virtual void CheckPermissionsToShowDialog() { } public abstract partial class CommonItemDialog : Microsoft.Win32.CommonDialog { protected CommonItemDialog() { } + public bool AddToRecent { get { throw null; } set { } } public System.Collections.Generic.IList CustomPlaces { get { throw null; } set { } } public bool DereferenceLinks { get { throw null; } set { } } public string InitialDirectory { get { throw null; } set { } } From 205ac2aaf04b7835a586adce356a7bff3d074d07 Mon Sep 17 00:00:00 2001 From: miloush Date: Mon, 12 Jun 2023 20:51:57 +0100 Subject: [PATCH 16/21] CommonItemDialog.ShowHiddenItems --- .../Microsoft/Win32/CommonItemDialog.cs | 20 +++++++++++++++++++ .../ref/PresentationFramework.cs | 1 + 2 files changed, 21 insertions(+) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs index 0253cc0ba9f..ad0d1008940 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs @@ -157,6 +157,26 @@ public string InitialDirectory } } + // FOS_FORCESHOWHIDDEN + // Include hidden and system items. + // + /// + /// Gets or sets a value indicating whether the dialog box will show + /// Include hidden items regardless of user preferences. + /// + public bool ShowHiddenItems + { + get + { + return GetOption(FOS.FORCESHOWHIDDEN); + } + set + { + + SetOption(FOS.FORCESHOWHIDDEN, value); + } + } + /// /// Gets or sets a string shown in the title bar of the file dialog. /// If this property is null, a localized default from the operating diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs index 81e8eadc299..9c2886632e2 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs @@ -19,6 +19,7 @@ protected CommonItemDialog() { } public bool DereferenceLinks { get { throw null; } set { } } public string InitialDirectory { get { throw null; } set { } } public bool RestoreDirectory { get { throw null; } set { } } + public bool ShowHiddenItems { get; set; } public string Title { get { throw null; } set { } } public bool ValidateNames { get { throw null; } set { } } protected void OnItemOk(System.ComponentModel.CancelEventArgs e) { } From 2824aecd4dd67c1c025cab0c15af1b15245ac297 Mon Sep 17 00:00:00 2001 From: miloush Date: Mon, 12 Jun 2023 21:00:47 +0100 Subject: [PATCH 17/21] CommonItemDialog.ClientGuid --- .../Microsoft/Win32/CommonItemDialog.cs | 10 ++++++++++ .../PresentationFramework/ref/PresentationFramework.cs | 1 + 2 files changed, 11 insertions(+) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs index ad0d1008940..b1682c5703c 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs @@ -118,6 +118,11 @@ public bool AddToRecent } } + /// + /// Gets or sets a Guid to associate with the dialog's persisted state. + /// + public Guid? ClientGuid { get; set; } + // The actual flag is FOS_NODEREFERENCELINKS (set = do not dereference, unset = deref) - // while we have true = dereference and false=do not dereference. Because we expose // the opposite of the Windows flag as a property to be clearer, we need to negate @@ -339,6 +344,11 @@ internal bool MessageBoxWithFocusRestore(string message, private protected virtual void PrepareDialog(IFileDialog dialog) { + if (ClientGuid is Guid guid) + { + dialog.SetClientGuid(ref guid); + } + if (!string.IsNullOrEmpty(InitialDirectory)) { IShellItem initialDirectory = ShellUtil.GetShellItemForPath(InitialDirectory); diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs index 9c2886632e2..59148fa69d1 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs @@ -15,6 +15,7 @@ public abstract partial class CommonItemDialog : Microsoft.Win32.CommonDialog { protected CommonItemDialog() { } public bool AddToRecent { get { throw null; } set { } } + public System.Guid? ClientGuid { get; set; } public System.Collections.Generic.IList CustomPlaces { get { throw null; } set { } } public bool DereferenceLinks { get { throw null; } set { } } public string InitialDirectory { get { throw null; } set { } } From b41384dc19a0c532e6a70f7050bad61d698f2b0e Mon Sep 17 00:00:00 2001 From: miloush Date: Mon, 12 Jun 2023 21:12:16 +0100 Subject: [PATCH 18/21] CommonItemDialog.DefaultDirectory --- .../Microsoft/Win32/CommonItemDialog.cs | 33 ++++++++++++++++++- .../ref/PresentationFramework.cs | 1 + 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs index b1682c5703c..b23932d253c 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs @@ -123,6 +123,24 @@ public bool AddToRecent /// public Guid? ClientGuid { get; set; } + /// + /// Gets or sets the initial directory displayed by the file dialog box + /// if there is not a recently used directory value available. + /// + public string DefaultDirectory + { + get + { + // Avoid returning a null string - return String.Empty instead. + return _defaultDirectory.Value == null ? String.Empty : _defaultDirectory.Value; + } + set + { + + _defaultDirectory.Value = value; + } + } + // The actual flag is FOS_NODEREFERENCELINKS (set = do not dereference, unset = deref) - // while we have true = dereference and false=do not dereference. Because we expose // the opposite of the Windows flag as a property to be clearer, we need to negate @@ -349,13 +367,25 @@ private protected virtual void PrepareDialog(IFileDialog dialog) dialog.SetClientGuid(ref guid); } + if (!string.IsNullOrEmpty(DefaultDirectory)) + { + IShellItem defaultDirectory = ShellUtil.GetShellItemForPath(DefaultDirectory); + if (defaultDirectory != null) + { + dialog.SetDefaultFolder(defaultDirectory); + } + } + if (!string.IsNullOrEmpty(InitialDirectory)) { IShellItem initialDirectory = ShellUtil.GetShellItemForPath(InitialDirectory); if (initialDirectory != null) { // Setting both of these so the dialog doesn't display errors when a remembered folder is missing. - dialog.SetDefaultFolder(initialDirectory); + if (string.IsNullOrEmpty(DefaultDirectory)) + { + dialog.SetDefaultFolder(initialDirectory); + } dialog.SetFolder(initialDirectory); } } @@ -717,6 +747,7 @@ void IDisposable.Dispose() // that control the appearance of the file dialog box. private SecurityCriticalDataForSet _title; // Title bar of the message box private SecurityCriticalDataForSet _initialDirectory; // Starting directory + private SecurityCriticalDataForSet _defaultDirectory; // Starting directory if no recent // We store the handle of the file dialog inside our class // for a variety of purposes (like getting the title of the dialog diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs index 59148fa69d1..c8cbed77512 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs @@ -17,6 +17,7 @@ protected CommonItemDialog() { } public bool AddToRecent { get { throw null; } set { } } public System.Guid? ClientGuid { get; set; } public System.Collections.Generic.IList CustomPlaces { get { throw null; } set { } } + public string DefaultDirectory { get { throw null; } set { } } public bool DereferenceLinks { get { throw null; } set { } } public string InitialDirectory { get { throw null; } set { } } public bool RestoreDirectory { get { throw null; } set { } } From 2fb70e0dc461ee620b55603a097c8ed4b674087f Mon Sep 17 00:00:00 2001 From: miloush Date: Mon, 12 Jun 2023 21:45:30 +0100 Subject: [PATCH 19/21] CommonItemDialog.RootDirectory --- .../MS/Internal/AppModel/ComGuids.cs | 2 + .../MS/Internal/AppModel/ShellProvider.cs | 66 +++++++++++++++++++ .../Microsoft/Win32/CommonItemDialog.cs | 27 ++++++++ .../ref/PresentationFramework.cs | 1 + 4 files changed, 96 insertions(+) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/AppModel/ComGuids.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/AppModel/ComGuids.cs index b6398fa0e06..28839a472fa 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/AppModel/ComGuids.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/AppModel/ComGuids.cs @@ -15,6 +15,8 @@ internal static class IID public const string EnumObjects = "2c1c7e2e-2d0e-4059-831e-1e6f82335c2e"; /// IID_IFileDialog public const string FileDialog = "42f85136-db7e-439c-85f1-e4075d135fc8"; + /// IID_IFileDialog2 + public const string FileDialog2 = "61744fc7-85b5-4791-a9b0-272276309b13"; /// IID_IFileDialogEvents public const string FileDialogEvents = "973510DB-7D7F-452B-8975-74A85828D354"; /// IID_IFileOpenDialog diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/AppModel/ShellProvider.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/AppModel/ShellProvider.cs index fc75ec3c999..16ddad1bebf 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/AppModel/ShellProvider.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/AppModel/ShellProvider.cs @@ -479,6 +479,72 @@ internal interface IFileDialog : IModalWindow void SetFilter([MarshalAs(UnmanagedType.Interface)] object pFilter); } + [ + ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid(IID.FileDialog2), + ] + internal interface IFileDialog2 : IFileDialog + { + #region IFileDialog redeclarations + #region IModalWindow redeclarations + [PreserveSig] + new HRESULT Show(IntPtr parent); + #endregion + + new void SetFileTypes(uint cFileTypes, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] COMDLG_FILTERSPEC[] rgFilterSpec); + + new void SetFileTypeIndex(uint iFileType); + + new uint GetFileTypeIndex(); + + new uint Advise(IFileDialogEvents pfde); + + new void Unadvise(uint dwCookie); + + new void SetOptions(FOS fos); + + new FOS GetOptions(); + + new void SetDefaultFolder(IShellItem psi); + + new void SetFolder(IShellItem psi); + + new IShellItem GetFolder(); + + new IShellItem GetCurrentSelection(); + + new void SetFileName([MarshalAs(UnmanagedType.LPWStr)] string pszName); + + [return: MarshalAs(UnmanagedType.LPWStr)] + new string GetFileName(); + + new void SetTitle([MarshalAs(UnmanagedType.LPWStr)] string pszTitle); + + new void SetOkButtonLabel([MarshalAs(UnmanagedType.LPWStr)] string pszText); + + new void SetFileNameLabel([MarshalAs(UnmanagedType.LPWStr)] string pszLabel); + + new IShellItem GetResult(); + + new void AddPlace(IShellItem psi, FDAP alignment); + + new void SetDefaultExtension([MarshalAs(UnmanagedType.LPWStr)] string pszDefaultExtension); + + new void Close([MarshalAs(UnmanagedType.Error)] int hr); + + new void SetClientGuid([In] ref Guid guid); + + new void ClearClientData(); + + new void SetFilter([MarshalAs(UnmanagedType.Interface)] object pFilter); + #endregion + + void SetCancelButtonLabel([MarshalAs(UnmanagedType.LPWStr)] string pszLabel); + + void SetNavigationRoot(IShellItem psi); + } + [ ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs index b23932d253c..e388c9cee4e 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs @@ -180,6 +180,23 @@ public string InitialDirectory } } + /// + /// Gets or sets the initial directory displayed by the file dialog box. + /// + public string RootDirectory + { + get + { + // Avoid returning a null string - return String.Empty instead. + return _rootDirectory.Value == null ? String.Empty : _rootDirectory.Value; + } + set + { + + _rootDirectory.Value = value; + } + } + // FOS_FORCESHOWHIDDEN // Include hidden and system items. // @@ -390,6 +407,15 @@ private protected virtual void PrepareDialog(IFileDialog dialog) } } + if (!string.IsNullOrEmpty(RootDirectory)) + { + IShellItem rootDirectory = ShellUtil.GetShellItemForPath(RootDirectory); + if (rootDirectory != null && dialog is IFileDialog2 dialog2) + { + dialog2.SetNavigationRoot(rootDirectory); + } + } + dialog.SetTitle(Title); dialog.SetFileName(CriticalItemName); @@ -748,6 +774,7 @@ void IDisposable.Dispose() private SecurityCriticalDataForSet _title; // Title bar of the message box private SecurityCriticalDataForSet _initialDirectory; // Starting directory private SecurityCriticalDataForSet _defaultDirectory; // Starting directory if no recent + private SecurityCriticalDataForSet _rootDirectory; // Topmost directory // We store the handle of the file dialog inside our class // for a variety of purposes (like getting the title of the dialog diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs index c8cbed77512..19b26e67910 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs @@ -21,6 +21,7 @@ protected CommonItemDialog() { } public bool DereferenceLinks { get { throw null; } set { } } public string InitialDirectory { get { throw null; } set { } } public bool RestoreDirectory { get { throw null; } set { } } + public string RootDirectory { get { throw null; } set { } } public bool ShowHiddenItems { get; set; } public string Title { get { throw null; } set { } } public bool ValidateNames { get { throw null; } set { } } From 1eb372dcde1f210b687fb559dec42e47d30a8bcb Mon Sep 17 00:00:00 2001 From: miloush Date: Sun, 2 Jul 2023 20:40:24 +0100 Subject: [PATCH 20/21] Reset new dialog properties --- .../Microsoft/Win32/CommonItemDialog.cs | 3 +++ .../Microsoft/Win32/OpenFileDialog.cs | 15 +-------------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs index e388c9cee4e..4a9ec279068 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonItemDialog.cs @@ -562,9 +562,12 @@ private void Initialize() _itemNames = null; _title.Value = null; _initialDirectory.Value = null; + _defaultDirectory.Value = null; + _rootDirectory.Value = null; // Set this to an empty list so callers can simply add to it. They can also replace it wholesale. CustomPlaces = new List(); + ClientGuid = null; } private bool HandleItemOk(IFileDialog dialog) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs index 17724461a25..1d2af2f29ca 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs @@ -205,17 +205,7 @@ public bool Multiselect /// Gets or sets a value indicating whether the dialog /// contains a read-only check box. /// - public bool ShowReadOnly - { - get - { - return _showReadOnly; - } - set - { - _showReadOnly = false; - } - } + public bool ShowReadOnly { get; set; } #endregion Public Properties @@ -307,9 +297,6 @@ private void Initialize() // //--------------------------------------------------- //#region Private Fields - - private bool _showReadOnly = false; - //#endregion Private Fields } } From e75f487aa39e9defd1af7fb29fb2b26a6a0e49b1 Mon Sep 17 00:00:00 2001 From: dipeshmsft Date: Fri, 7 Jul 2023 15:34:23 +0530 Subject: [PATCH 21/21] Fixed merge commit errors --- .../ApiCompat/Baselines/PresentationFramework-ref.baseline.txt | 2 -- .../PresentationFramework/Microsoft/Win32/OpenFileDialog.cs | 3 --- .../src/PresentationFramework/ref/PresentationFramework.cs | 2 +- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/ApiCompat/Baselines/PresentationFramework-ref.baseline.txt b/src/Microsoft.DotNet.Wpf/ApiCompat/Baselines/PresentationFramework-ref.baseline.txt index 199045d56f6..d4b7ffccb6b 100644 --- a/src/Microsoft.DotNet.Wpf/ApiCompat/Baselines/PresentationFramework-ref.baseline.txt +++ b/src/Microsoft.DotNet.Wpf/ApiCompat/Baselines/PresentationFramework-ref.baseline.txt @@ -1,6 +1,4 @@ Compat issues with assembly PresentationFramework: -MembersMustExist : Member 'private protected MS.Internal.AppModel.IFileDialog Microsoft.Win32.CommonItemDialog.CreateDialog()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'private protected System.String[] Microsoft.Win32.FileDialog.ProcessFiles(MS.Internal.AppModel.IFileDialog)' does not exist in the implementation but it does exist in the contract. CannotChangeAttribute : Attribute 'System.ComponentModel.DesignerSerializationVisibilityAttribute' on 'System.ComponentModel.DesignerProperties.GetIsInDesignMode(System.Windows.DependencyObject)' changed from '[DesignerSerializationVisibilityAttribute(0)]' in the contract to '[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]' in the implementation. CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'System.Windows.AttachedPropertyBrowsableForChildrenAttribute' changed from '[AttributeUsageAttribute(64, AllowMultiple=false)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Method, AllowMultiple=false)]' in the implementation. CannotChangeAttribute : Attribute 'System.ComponentModel.DesignerSerializationVisibilityAttribute' on 'System.Windows.DataTemplate.Triggers' changed from '[DesignerSerializationVisibilityAttribute(2)]' in the contract to '[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Content)]' in the implementation. diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs index a2b9f91d2bd..1d2af2f29ca 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/OpenFileDialog.cs @@ -297,9 +297,6 @@ private void Initialize() // //--------------------------------------------------- //#region Private Fields - - private bool _showReadOnly = false; - //#endregion Private Fields } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs index 0a1071de809..7a77e13c7ba 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/ref/PresentationFramework.cs @@ -24,7 +24,7 @@ private protected CommonItemDialog() { } public bool ShowHiddenItems { get; set; } public string Title { get { throw null; } set { } } public bool ValidateNames { get { throw null; } set { } } - protected void OnItemOk(System.ComponentModel.CancelEventArgs e) { } + protected virtual void OnItemOk(System.ComponentModel.CancelEventArgs e) { } protected override bool RunDialog(System.IntPtr hwndOwner) { throw null; } public override void Reset() { } public override string ToString() { throw null; }