2012-09-05 9 views
5

Próbuję użyć FolderBrowserDialog z mojej aplikacji WPF - nic nadzwyczajnego. Nie obchodzi mnie, że ma na to wyglądać Windows Forms.Jak używać FolderBrowserDialog z aplikacji WPF z MVVM

Znalazłem pytanie z odpowiednią odpowiedzią (How to use a FolderBrowserDialog from a WPF application), z wyjątkiem tego, że używam MVVM.

This była odpowiedzią, którą "zaimplementowałem", z tym, że nie mogę uzyskać obiektu okna i po prostu wywołuję ShowDialog() bez żadnych parametrów.

Problem jest taki:

var dlg = new FolderBrowserDialog(); 
System.Windows.Forms.DialogResult result = dlg.ShowDialog(this.GetIWin32Window()); 

w moim ViewModel tamtejszej this ma GetIWin32Window() metodę żebym kontekst Window.

Jakieś pomysły na to, jak to działa?

Odpowiedz

4

Po pierwsze, można użyć podpisu ShowDialog, który nie wymaga okna.

var dlg = new FolderBrowserDialog(); 
DialogResult result = dlg.ShowDialog(); 

Po drugie, można wysłać główne okno aplikacji jako okno własności.

var dlg = new FolderBrowserDialog(); 
DialogResult result = dlg.ShowDialog(Application.Current.MainWindow.GetIWin32Window()); 

Druga opcja może nie być uważana za bardzo MVVMish.

Zobacz odpowiedź @Dr. ABT w this question, aby uzyskać sposób na wprowadzenie wskaźnika do widoku w swoim ViewModel (nie jestem pewien, czy to dobry pomysł, czy zły pomysł, ale nie pozwolę, aby to mnie powstrzymało) Dzięki tej technice będziesz mieć dostęp w swojej maszynie wirtualnej do odpowiedniego widoku, jeśli naprawdę chcesz, aby ten widok był właścicielem FolderBrowserDialog.

@ChrisDD ma rację w definiowaniu interfejsu i zawijaniu FolderBrowserDialog. To jest jak to robimy:

public interface IFolderBrowserDialog 
    { 
    string Description { get; set; } 
    Environment.SpecialFolder RootFolder { get; set; } 
    string SelectedPath { get; set; } 
    bool ShowNewFolderButton { get; set; } 
    bool? ShowDialog(); 
    bool? ShowDialog(Window owner); 
    } 

    //Decorated for MEF injection 
    [Export(typeof(IFolderBrowserDialog))] 
    [PartCreationPolicy(CreationPolicy.NonShared)] 
    internal class WindowsFormsFolderBrowserDialog : IFolderBrowserDialog 
    { 
    private string _description; 
    private string _selectedPath; 

    [ImportingConstructor] 
    public WindowsFormsFolderBrowserDialog() 
    { 
     RootFolder = System.Environment.SpecialFolder.MyComputer; 
     ShowNewFolderButton = false; 
    } 

    #region IFolderBrowserDialog Members 

    public string Description 
    { 
     get { return _description ?? string.Empty; } 
     set { _description = value; } 
    } 

    public System.Environment.SpecialFolder RootFolder { get; set; } 

    public string SelectedPath 
    { 
     get { return _selectedPath ?? string.Empty; } 
     set { _selectedPath = value; } 
    } 

    public bool ShowNewFolderButton { get; set; } 

    public bool? ShowDialog() 
    { 
     using (var dialog = CreateDialog()) 
     { 
     var result = dialog.ShowDialog() == DialogResult.OK; 
     if (result) SelectedPath = dialog.SelectedPath; 
     return result; 
     } 
    } 

    public bool? ShowDialog(Window owner) 
    { 
     using (var dialog = CreateDialog()) 
     { 
     var result = dialog.ShowDialog(owner.AsWin32Window()) == DialogResult.OK; 
     if (result) SelectedPath = dialog.SelectedPath; 
     return result; 
     } 
    } 
    #endregion 

    private FolderBrowserDialog CreateDialog() 
    { 
     var dialog = new FolderBrowserDialog(); 
     dialog.Description = Description; 
     dialog.RootFolder = RootFolder; 
     dialog.SelectedPath = SelectedPath; 
     dialog.ShowNewFolderButton = ShowNewFolderButton; 
     return dialog; 
    } 
    } 

    internal static class WindowExtensions 
    { 
    public static System.Windows.Forms.IWin32Window AsWin32Window(this Window window) 
    { 
     return new Wpf32Window(window); 
    } 
    } 

    internal class Wpf32Window : System.Windows.Forms.IWin32Window 
    { 
    public Wpf32Window(Window window) 
    { 
     Handle = new WindowInteropHelper(window).Handle; 
    } 

    #region IWin32Window Members 

    public IntPtr Handle { get; private set; } 

    #endregion 
    } 

Następnie wykonujemy VM/command gdzie chcemy użyć IFolderBrowserDialog import FolderBrowser. W aplikacji, IFolderBrowserDialog.ShowDialog wyświetla okno dialogowe. W teście jednostkowym wyśmiewamy IFolderBrowserDialog, abyśmy mogli sprawdzić, czy został on wywołany z poprawnymi parametrami i/lub wysłać wybrany folder z powrotem do sut, aby test mógł być kontynuowany.

0

MVVM sposób:

zdefiniować nowy interfejs dla FolderBrowserDialog. Stwórz nową klasę & implementującą ten interfejs. (Wdrażanie odbywa się przy użyciu rzeczywistej klasy FolderBrowserDialog).

W ten sposób nie wiążesz MVVM z konkretną implementacją, a rzeczywistą logikę można później przetestować.

+0

On musi jeszcze zdać IWin32Window do FolderBrowserDialog na to, aby pokazać, jak właściwego okna modalnego . Chce wiedzieć, jak zaprogramować ViewModel, aby móc pobrać IWin32Window z bieżącego widoku. –

+0

Tak i to jest szczegół implementacji. Tak jak powiedziałem, jeśli implementujesz interfejs, implementacja może zrobić, co tylko chce. Viewmodel nie musi nic wiedzieć. Bieżący widok można uzyskać przy użyciu aplikacji Application.Current.Windows.Where (x => x.IsActive = true) –

2

Jeśli chcesz używać FolderBrowserDialog, użyłbym tego rodzaju projektu.

Najpierw utwórz DependencyProperty w swoim widoku, aby odsłonić uchwyt.

public static readonly DependencyProperty WindowHandleProperty = 
    DependencyProperty.Register("WindowHandle", typeof(System.Windows.Forms.IWin32Window), typeof(MainWindow), new PropertyMetadata(null)); 

// MainWindow.cs 
public System.Windows.Forms.IWin32Window WindowHandle 
{ 
    get { return (System.Windows.Forms.IWin32Window)GetValue(WindowHandleProperty); } 
    set { SetValue(WindowHandleProperty, value); } 
} 

Teraz, kiedy okno ładunki można pobrać uchwyt przy użyciu rozszerzeń przewidzianych w pytaniu ty związane z:

// MainWindow.cs 
void MainWindow_Loaded(object sender, RoutedEventArgs e) 
{ 
    var binding = new Binding(); 
    binding.Path = new PropertyPath("WindowHandle"); 
    binding.Mode = BindingMode.OneWayToSource; 
    SetBinding(WindowHandleProperty, binding); 

    WindowHandle = this.GetIWin32Window(); 
} 

tak, to są wiążące w jedną stronę do źródła przy użyciu " "Właściwość WindowHandle". Więc jeśli ViewModel ma właściwość windowhandle, będzie uaktualniane z obowiązującym IWin32Handle dla powiązanego widzenia:

// ViewModel.cs 
private System.Windows.Forms.IWin32Window _windowHandle; 
public System.Windows.Forms.IWin32Window WindowHandle 
{ 
    get 
    { 
     return _windowHandle; 
    } 
    set 
    { 
     if (_windowHandle != value) 
     { 
      _windowHandle = value; 
      RaisePropertyChanged("WindowHandle"); 
     } 
    } 
} 

Jest to dobre rozwiązanie, ponieważ nie jesteś ciężko kodowania jeden ViewModel być sparowany z jednym określonym widokiem. Jeśli używasz wielu widoków z tym samym ViewModel, to po prostu powinno działać. Jeśli utworzysz nowy widok, ale nie wdrożysz DependencyProperty, będzie on działał tylko z uchwytem pustym.

EDIT:

Na marginesie, czy faktycznie testowany właśnie nie zapewniając parametr IWin32Owner? Dla mnie nadal automatycznie otwiera się jako modalne okno dialogowe dla aplikacji i blokuje użytkownikowi interakcję ze wszystkimi oknami aplikacji. Czy jest coś innego, czego potrzebujesz?

0

Aby obsłużyć dowolny rodzaj dialogów w ramach wzorca mvvm, powinieneś wybrać rodzaj usługi Dialog. W dokumencie this post znajdziesz wskazówki, jak je zastosować.

Umieszczenie rzeczy dialogowych w usłudze powoduje, że wzór mvvm pozostaje nietknięty. Usługa zajmuje się tworzeniem okien dialogowych i może dostarczać wyniki. Model widoku tylko wywołuje metody i subskrybuje zdarzenia dostarczane przez usługę.

jeśli korzystasz z wtrysku zależności dla usługi (interfejsu), masz tę zaletę, że testujesz rozwiązanie przez kpiny. Lub możesz zastąpić formularze folderbrowserdialog, jeśli będzie tam wpf.

0

MVVM + WinForms FolderBrowserDialog jako zachowanie

public class FolderDialogBehavior : Behavior<Button> 
{ 
    public string SetterName { get; set; } 

    protected override void OnAttached() 
    { 
     base.OnAttached(); 
     AssociatedObject.Click += OnClick; 
    } 

    protected override void OnDetaching() 
    { 
     AssociatedObject.Click -= OnClick; 
    } 

    private void OnClick(object sender, RoutedEventArgs e) 
    { 
     var dialog = new FolderBrowserDialog(); 
     var result = dialog.ShowDialog(); 
     if (result == DialogResult.OK && AssociatedObject.DataContext != null) 
     { 
      var propertyInfo = AssociatedObject.DataContext.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public) 
      .Where(p => p.CanRead && p.CanWrite) 
      .Where(p => p.Name.Equals(SetterName)) 
      .First(); 

      propertyInfo.SetValue(AssociatedObject.DataContext, dialog.SelectedPath, null); 
     } 
    } 
} 

Wykorzystanie

 <Button Grid.Column="3" Content="..."> 
      <Interactivity:Interaction.Behaviors> 
       <Behavior:FolderDialogBehavior SetterName="SomeFolderPathPropertyName"/> 
      </Interactivity:Interaction.Behaviors> 
    </Button> 

blogpost: http://kostylizm.blogspot.ru/2014/03/wpf-mvvm-and-winforms-folder-dialog-how.html

Powiązane problemy