2012-09-05 15 views
5

Estoy intentando utilizar el FolderBrowserDialog desde mi aplicación WPF - nada lujoso. No me importa mucho que tenga el aspecto de Windows Forms.Cómo utilizar un FolderBrowserDialog desde una aplicación de WPF MVVM con

encontré una pregunta con una respuesta adecuada (How to use a FolderBrowserDialog from a WPF application), excepto que estoy usando MVVM.

This fue la respuesta que "implementé", excepto que no puedo obtener el objeto de ventana y solo estoy llamando al ShowDialog() sin ningún parámetro.

El problema es el siguiente:

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

En mi ViewModel allí la this tiene ningún método GetIWin32Window() para que consiga el contexto de la ventana.

¿Alguna idea sobre cómo hacer que esto funcione?

Respuesta

4

Primero, podría usar la firma ShowDialog que no requiere una ventana.

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

En segundo lugar, podría enviar la ventana principal de la Aplicación como ventana propietaria.

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

La segunda opción podría no considerarse muy MVVMish.

Consulte la respuesta por @Dr. ABT en this question para una forma de insertar un puntero a su vista en su ViewModel (no estoy seguro de si esta es una buena idea o una mala idea, pero no voy a dejar que eso me detenga) Con esta técnica, tendría acceso en su máquina virtual a la vista correspondiente si realmente desea que esa vista sea el propietario de FolderBrowserDialog.

@ChrisDD tiene razón acerca de la definición de una interfaz y envolver FolderBrowserDialog. Así es como lo hacemos:

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 
    } 

Entonces hacemos el VM/Comando donde queremos usar la importación IFolderBrowserDialog FolderBrowser. En la aplicación, IFolderBrowserDialog.ShowDialog muestra el diálogo. En la prueba unitaria, nos burlamos de IFolderBrowserDialog para poder verificar que se invocó con los parámetros correctos y/o enviar de nuevo la carpeta seleccionada al sut para que la prueba pueda continuar.

0

manera MVVM:

definir una nueva interfaz para FolderBrowserDialog. Cree una nueva clase & para implementar esa interfaz. (La implementación se realiza con la clase real FolderBrowserDialog).

De esta forma no vinculará MVVM a una implementación específica y la lógica real se podrá probar posteriormente.

+0

Todavía tiene que pasar una IWin32Window a la FolderBrowserDialog para que se muestre como un diálogo modal adecuado . Él quiere saber cómo programar un ViewModel para poder recuperar el IWin32Window de su vista actual. –

+0

Sí y ese es el detalle de implementación. Como dije, si implementa la interfaz, la implementación puede hacer lo que quiera. Viewmodel no necesita saber nada. Puede obtener la vista actual por Application.Current.Windows.Where (x => x.IsActive = true) –

2

Si usted está decidido a utilizar FolderBrowserDialog, que haría uso de este tipo de diseño.

Primero, cree una DependencyProperty en su Vista para exponer su identificador.

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); } 
} 

Ahora, cuando sus cargas ventana, se puede recuperar el identificador utilizando las extensiones que se ofrecen en la pregunta se ha vinculado a:

// 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(); 
} 

Por lo tanto, va a enlazar una sola manera de fuente utilizando un " Propiedad WindowHandle ". Así que si su modelo de vista tiene una propiedad WindowHandle, se mantiene al día con la IWin32Handle válida para la vista relacionada:

// 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"); 
     } 
    } 
} 

Ésta es una buena solución, porque no estás codificación dura un modelo de vista para ser emparejado con una vista específica. Si usa varias vistas con el mismo modelo de vista, debería funcionar. Si crea una nueva Vista pero no implementa DependencyProperty, solo funcionará con un identificador nulo.

EDIT:

Como nota al margen, han probado que en realidad no proporciona un parámetro IWin32Owner? Para mí, todavía se abre automáticamente como un diálogo modal para la aplicación y bloquea al usuario para que no interactúe con todas las ventanas de la aplicación. ¿Hay algo más que necesite hacer en su lugar?

0

Para manejar cualquier tipo de diálogo dentro del patrón mvvm, debe ir con un tipo de Dialog-Service. En this post encontrará algunos consejos para este enfoque.

Poner cosas de diálogo en un servicio mantiene intacto el patrón mvvm. El servicio se encarga de toda la creación de los diálogos y puede proporcionar los resultados. El modelo de vista simplemente llama a métodos y suscribe eventos provistos por un servicio.

si usa la inyección de dependencia para el servicio (interfaz), obtiene la ventaja de mantener su solución comprobable mediante burlas. O puede reemplazar los formularios folderbrowserdialog si habrá uno wpf.

0

MVVM + WinForms FolderBrowserDialog como un comportamiento

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); 
     } 
    } 
} 

Uso

 <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

Cuestiones relacionadas