// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. using System; using System.ComponentModel; using System.Globalization; using System.Threading; using System.Windows; using System.Windows.Media; namespace WPinternals { public class PathChangedEventArgs: EventArgs { public PathChangedEventArgs(string NewPath) : base() { this.NewPath = NewPath; } public string NewPath; } public delegate void PathChangedEventHandler( Object sender, PathChangedEventArgs e ); /// /// Interaction logic for FilePickerControl.xaml /// public partial class FilePickerBase : System.Windows.Controls.UserControl, INotifyPropertyChanged { private SynchronizationContext UIContext; public event PropertyChangedEventHandler PropertyChanged = delegate { }; public event PathChangedEventHandler PathChanged = delegate { }; protected void OnPropertyChanged(string propertyName) { if (this.PropertyChanged != null) { if (SynchronizationContext.Current == UIContext) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); else { UIContext.Post((s) => PropertyChanged(this, new PropertyChangedEventArgs(propertyName)), null); } } } public FilePickerBase() { UIContext = SynchronizationContext.Current; InitializeComponent(); } public bool AllowNull { get { return (bool)GetValue(AllowNullProperty); } set { SetValue(AllowNullProperty, value); Resize(); } } public static readonly DependencyProperty AllowNullProperty = DependencyProperty.Register("AllowNull", typeof(bool), typeof(FilePickerBase), new UIPropertyMetadata(false)); public string Caption { get { return (string)GetValue(CaptionProperty); } set { SetValue(CaptionProperty, value); Resize(); } } public static readonly DependencyProperty CaptionProperty = DependencyProperty.Register("Caption", typeof(string), typeof(FilePickerBase), new UIPropertyMetadata(null)); public string Path { get { return (string)GetValue(PathProperty); } set { if ((string)GetValue(PathProperty) != value) { SetValue(PathProperty, value); Resize(); PathChanged(this, new PathChangedEventArgs(value)); } } } public static readonly DependencyProperty PathProperty = DependencyProperty.Register("Path", typeof(string), typeof(FilePickerBase), new UIPropertyMetadata(null)); public string SelectionText { get { return (string)GetValue(SelectionTextProperty); } set { SetValue(SelectionTextProperty, value); Resize(); } } public static readonly DependencyProperty SelectionTextProperty = DependencyProperty.Register("SelectionText", typeof(string), typeof(FilePickerBase), new UIPropertyMetadata("")); private void SizeChangedHandler(object sender, SizeChangedEventArgs e) { Resize(); } protected override Size MeasureOverride(Size availableSize) { var resultSize = new Size(availableSize.Width, 0); FormattedText formatted = new FormattedText( "TEST", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface(PathTextBlock.FontFamily, PathTextBlock.FontStyle, PathTextBlock.FontWeight, PathTextBlock.FontStretch), FontSize, Foreground ); resultSize.Height = formatted.Height; return resultSize; } private void Resize() { if (!IsLoaded) return; CaptionTextBlock.Text = Caption; FormattedText formatted = new FormattedText( CaptionTextBlock.Text, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface(CaptionTextBlock.FontFamily, CaptionTextBlock.FontStyle, CaptionTextBlock.FontWeight, CaptionTextBlock.FontStretch), FontSize, Foreground ); double CaptionWidth = formatted.Width; if (CaptionWidth > 0) CaptionWidth += 10; bool SelectVisible = (Path == null); bool ChangeVisible = (Path != null); bool ClearVisible = ((Path != null) && (AllowNull)); double NewWidth = ActualWidth - CaptionWidth; if (SelectVisible) NewWidth -= SelectLink.ActualWidth; if (ChangeVisible) NewWidth -= (ChangeLink.ActualWidth + 10); if (ClearVisible) NewWidth -= (ClearLink.ActualWidth + 10); SetText(NewWidth); // Calculate the new ActualWidth // We can't use PathTextBlock.ActualWidth yet, because LayoutUpdated event has not yet been triggered formatted = new FormattedText( PathTextBlock.Text, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface(PathTextBlock.FontFamily, PathTextBlock.FontStyle, PathTextBlock.FontWeight, PathTextBlock.FontStretch), FontSize, Foreground ); if (NewWidth < 0) PathTextBlock.Width = 0; else if (formatted.Width > NewWidth) PathTextBlock.Width = NewWidth; else PathTextBlock.Width = formatted.Width; PathTextBlock.Margin = new Thickness(CaptionWidth, 0, 0, 0); double Pos = PathTextBlock.Width + CaptionWidth; if (SelectVisible) { SelectLink.Visibility = Visibility.Visible; SelectLink.Margin = new Thickness(Pos, 0, 0, 0); Pos += SelectLink.ActualWidth; } else SelectLink.Visibility = Visibility.Collapsed; if (ChangeVisible) { ChangeLink.Visibility = Visibility.Visible; ChangeLink.Margin = new Thickness(Pos + 10, 0, 0, 0); Pos += ChangeLink.ActualWidth + 10; } else ChangeLink.Visibility = Visibility.Collapsed; if (ClearVisible) { ClearLink.Visibility = Visibility.Visible; ClearLink.Margin = new Thickness(Pos + 10, 0, 0, 0); Pos += ClearLink.ActualWidth + 10; } else ClearLink.Visibility = Visibility.Collapsed; } private void SetText(double MaxWidth) { string Text = Path; if (System.IO.Path.IsPathRooted(Text)) { // It is a valid path string filename = ""; try { filename = System.IO.Path.GetFileName(Text); } catch { } string directory = ""; try { directory = System.IO.Path.GetDirectoryName(Text); } catch { } FormattedText formatted; bool widthOK = false; bool changedWidth = false; do { formatted = new FormattedText( "{0}...\\{1}".FormatWith(directory, filename), CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface(PathTextBlock.FontFamily, PathTextBlock.FontStyle, PathTextBlock.FontWeight, PathTextBlock.FontStretch), FontSize, Foreground ); widthOK = formatted.Width < MaxWidth; if (!widthOK) { changedWidth = true; if (directory.Length > 0) directory = directory.Substring(0, directory.Length - 1); if (directory.Length == 0) { Text = "...\\" + filename; break; } } } while (!widthOK); if (changedWidth && (directory.Length > 0)) { Text = "{0}...\\{1}".FormatWith(directory, filename); } } PathTextBlock.Text = Text; } private void LoadedHandler(object sender, RoutedEventArgs e) { Resize(); } private void SelectLink_Click(object sender, RoutedEventArgs e) { Select(); Resize(); } private void ChangeLink_Click(object sender, RoutedEventArgs e) { Select(); Resize(); } private void ClearLink_Click(object sender, RoutedEventArgs e) { Path = null; Resize(); } protected virtual void Select() { } } public class FilePicker : FilePickerBase { public FilePicker() : base() { if (SelectionText == "") SelectionText = "Select file..."; } protected override void Select() { Nullable result; if (SaveDialog) { Microsoft.Win32.SaveFileDialog savedlg = new Microsoft.Win32.SaveFileDialog(); if (Path != null) savedlg.FileName = Path; else savedlg.FileName = DefaultFileName; // Show open file dialog box result = savedlg.ShowDialog(); // Process open file dialog box results if (result == true) { // Open document Path = savedlg.FileName; } } else { // Select file Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog(); if (Path != null) dlg.FileName = Path; else dlg.FileName = DefaultFileName; // Show open file dialog box result = dlg.ShowDialog(); // Process open file dialog box results if (result == true) { // Open document Path = dlg.FileName; } } } public bool SaveDialog { get { return (bool)GetValue(SaveDialogProperty); } set { SetValue(SaveDialogProperty, value); } } public static readonly DependencyProperty SaveDialogProperty = DependencyProperty.Register("SaveDialog", typeof(bool), typeof(FilePicker), new UIPropertyMetadata(false)); public string DefaultFileName { get { return (string)GetValue(DefaultFileNameProperty); } set { SetValue(DefaultFileNameProperty, value); } } public static readonly DependencyProperty DefaultFileNameProperty = DependencyProperty.Register("DefaultFileName", typeof(string), typeof(FilePicker), new UIPropertyMetadata("")); } public class FolderPicker : FilePickerBase { public FolderPicker() : base() { if (SelectionText == "") SelectionText = "Select folder..."; } protected override void Select() { // Select folder FolderSelectDialog dlg = new FolderSelectDialog(); if (Path != null) dlg.InitialDirectory = Path; if (dlg.ShowDialog()) Path = dlg.FileName; } } static class Extensions { public static string FormatWith(this string s, params object[] args) { return string.Format(s, args); } } }