C#
WPF
Xaml
MVVM
PRISM

MVVMでファイルダイアログを使用する

やりたいこと

ViewModelからファイルダイアログを開く。

実装

ファイルダイアログを開くアクショントリガーを作成する。

アクショントリガー
using Microsoft.Win32;
using Prism.Interactivity.InteractionRequest;
using System;
using System.Linq;
using System.Windows;
using System.Windows.Interactivity;

namespace Sample {
    public class PopupCommonDialogAction : TriggerAction<FrameworkElement> {
        public static readonly DependencyProperty FileDialogTypeProperty 
            = DependencyProperty.Register(
                nameof(FileDialogType),
                typeof(Type),
                typeof(PopupCommonDialogAction),
                new PropertyMetadata(null));

        public Type FileDialogType {
            get { return (Type)GetValue(FileDialogTypeProperty); }
            set { SetValue(FileDialogTypeProperty, value); }
        }

        protected override void Invoke(object parameter) {
            var args = parameter as InteractionRequestedEventArgs;
            if (args == null)
                return;

            var confirmation = args.Context as IConfirmation;
            if (confirmation != null)
                ShowDialog(confirmation);

            args.Callback();
        }

        private void ShowDialog(IConfirmation confirmation) {
            var dialog = (CommonDialog)Activator.CreateInstance(FileDialogType);
            var confirmationProperties = confirmation.GetType().GetProperties();
            var dialogProperties = dialog.GetType().GetProperties();

            // ViewModelから渡された値をViewにセット
            foreach (var prop in confirmationProperties) {
                var value = prop.GetValue(confirmation, null);
                if (value == null)
                    continue;

                var dialogProp = dialogProperties
                    .FirstOrDefault(p => p.Name.Equals(prop.Name, StringComparison.OrdinalIgnoreCase));
                if (dialogProp != null && dialogProp.CanWrite)
                    dialogProp.SetValue(dialog, value, null);
            }

            var ret = dialog.ShowDialog();

            // Viewの値をViewModelに返す
            foreach (var prop in dialogProperties) {
                var ctxProp = confirmationProperties
                    .FirstOrDefault(p => p.Name.Equals(prop.Name, StringComparison.OrdinalIgnoreCase));
                if (ctxProp != null && ctxProp.CanWrite)
                    ctxProp.SetValue(confirmation, prop.GetValue(dialog, null), null);
            }

            if (confirmation != null)
                confirmation.Confirmed = ret == true;
        }
    }
}
通知クラス
using Prism.Interactivity.InteractionRequest;

namespace Sample {
    public class FileDialogPropertyNotification : Confirmation {
        //ファイルダイアログと同名のプロパティを定義する
        public string FileName { get; set; }
        public string Filter { get; set; }
        //以下省略
    }
}

使い方

XAML
<Window x:Class="Sample.MainView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        xmlns:prism="http://prismlibrary.com/"
        xmlns:win32="clr-namespace:Microsoft.Win32;assembly=PresentationFramework" 
        xmlns:local="clr-namespace:Sample"
        Title="MainView" Height="300" Width="300">
    <i:Interaction.Triggers>
        <!--セーブファイルダイアログ-->
        <prism:InteractionRequestTrigger SourceObject="{Binding OpenSaveFileDialogRequest, Mode=OneWay}">
            <local:PopupCommonDialogAction FileDialogType="{x:Type win32:SaveFileDialog}"/>
        </prism:InteractionRequestTrigger>
        <!--オープンファイルダイアログ-->
        <prism:InteractionRequestTrigger SourceObject="{Binding OpenOpenFileDialogRequest, Mode=OneWay}">
            <local:PopupCommonDialogAction FileDialogType="{x:Type win32:OpenFileDialog}"/>
        </prism:InteractionRequestTrigger>
    </i:Interaction.Triggers>
    <Grid>
        <TextBox />
    </Grid>
</Window>
ViewModel
using Prism.Interactivity.InteractionRequest;
using System.Windows;

namespace Sample {
    public class MainViewModel {
        public InteractionRequest<FileDialogPropertyNotification> OpenOpenFileDialogRequest { get; }
            = new InteractionRequest<FileDialogPropertyNotification>();

        public InteractionRequest<FileDialogPropertyNotification> OpenSaveFileDialogRequest { get; }
            = new InteractionRequest<FileDialogPropertyNotification>();

        private string OpenOpenFileDialog(string title, string filter, string fileName = "") {
            var notification = new FileDialogPropertyNotification() {
                Title = title,
                Filter = filter,
                FileName = fileName
            };
            OpenOpenFileDialogRequest.Raise(notification);

            return notification.Confirmed ? notification.FileName : string.Empty;
        }

        private string OpenSaveFileDialog(string title, string filter, string fileName = "") {
            var notification = new FileDialogPropertyNotification() {
                Title = title,
                Filter = filter,
                FileName = fileName
            };
            OpenSaveFileDialogRequest.Raise(notification);

            return notification.Confirmed ? notification.FileName : string.Empty;
        }
    }
}