はじめに
Prismを使ってプログレスバーの付いたダイアログを作りました。
進捗の値0~100をダイアログに送り、100のときにダイアログを閉じることができるという仕様にしました。
環境
.Net Core 3.1
Prism.Wpf 7.2.0.1422
実装
View
<UserControl x:Class="Module1.Views.ProgressDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Module1.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
Height="100"
Width="200"
mc:Ignorable="d"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
xmlns:viewModels="clr-namespace:Module1.ViewModels"
d:DataContext="{d:DesignInstance viewModels:ProgressDialogViewModel}">
<prism:Dialog.WindowStyle>
<Style TargetType="Window">
<Setter Property="ResizeMode" Value="NoResize"/>
<Setter Property="ShowInTaskbar" Value="False"/>
<Setter Property="SizeToContent" Value="WidthAndHeight"/>
<Setter Property="WindowStyle" Value="None"/>
<Setter Property="BorderThickness" Value="1"/>
</Style>
</prism:Dialog.WindowStyle>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<WrapPanel Margin="10,15">
<Label Width="30" HorizontalContentAlignment="Right" Content="{Binding Progress}"/>
<ProgressBar Height="15" Width="130" Value="{Binding Progress}" />
</WrapPanel>
<Button Grid.Row="1" Height="20" Width="100" VerticalAlignment="Bottom" Margin="0,12" Content="閉じる" Command="{Binding CloseCommand}"/>
</Grid>
</UserControl>
ダイアログ用にプログレスバーと閉じるボタンが付いたViewを作成します。
prism:Dialog.WindowStyle
タグでダイアログとして呼び出された際のWindowStyleを制御しています。今回はできるだけダイアログを閉じられたくなかったのでWindowStyle
にNone
を指定しウィンドウの閉じるボタンを非表示にしています。
ViewModel
public class ProgressDialogViewModel : BindableBase, IDialogAware
{
private int progress;
public event Action<IDialogResult> RequestClose;
public int Progress
{
get { return progress; }
set { SetProperty(ref progress, value); }
}
public DelegateCommand CloseCommand { get; }
public string Title => "Dialog1";
public ProgressDialogViewModel(ProgressDialogModel model)
{
this.Progress = model.Progress;
model.PropertyChanged += (_, e) =>
{
if (e.PropertyName == "Progress")
{
this.Progress = model.Progress;
}
};
this.CloseCommand =
new DelegateCommand
(
() => this.CloseDialog(),
() => this.Progress >= 100
)
.ObservesProperty(() => this.Progress);
}
public bool CanCloseDialog() => true;
public void OnDialogClosed() => this.Progress = 0;
public void OnDialogOpened(IDialogParameters parameters) { }
private void CloseDialog()
=> RequestClose?.Invoke(new DialogResult(ButtonResult.OK));
}
ViewModelではViewのプログレスバーにバインドする項目としてProgress
プロパティを定義しています。
CloseCommad
ではダイアログを閉じるときの動作を担当しています。使用可否条件としてProgress
の値が100以上であることを指定しています。
ダイアログとして利用するためにIDialogAware
を実装しています。
Model
public class ProgressDialogModel : BindableBase
{
private int progress;
public int Progress
{
get { return progress; }
set { SetProperty(ref progress, value); }
}
public ProgressDialogModel(IEventAggregator eventAggregator)
{
this.Progress = 0;
eventAggregator
.GetEvent<ProgressMessage>()
.Subscribe(x => this.Progress = x);
}
}
public class ProgressMessage : PubSubEvent<int>
{
}
ModelではViewModelとバインドする項目としてProgress
プロパティを定義しています。
外部からの値受け取りはIEventAggregator
を介して行うものとしProgressMessage
が発行されたときにProgress
の値が変化するようにしています。
Module
public class Module1Module : IModule
{
public void OnInitialized(IContainerProvider containerProvider)
{
_ = containerProvider.Resolve<ProgressDialogModel>();
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterSingleton<ProgressDialogModel>();
containerRegistry.RegisterDialog<ProgressDialog>();
}
}
Moduleでは他のプロジェクトでダイアログを利用できるようDIコンテナにViewとModelを登録しています。ModelはシングルトンとしてDIコンテナに登録しました。
ダイアログ呼び出し部分
public DelegateCommand ShowDialogCommand { get; }
public MainWindowViewModel(IDialogService dialogService, IEventAggregator eventAggregator)
{
this.ShowDialogCommand =
new DelegateCommand(() =>
{
_ = Task.Run(async () =>
{
eventAggregator.GetEvent<ProgressMessage>().Publish(0);
await Task.Delay(1000);
eventAggregator.GetEvent<ProgressMessage>().Publish(33);
await Task.Delay(1000);
eventAggregator.GetEvent<ProgressMessage>().Publish(66);
await Task.Delay(1000);
eventAggregator.GetEvent<ProgressMessage>().Publish(100);
})
.ContinueWith(x => x.Exception /* エラー処理 */, TaskContinuationOptions.OnlyOnFaulted);
dialogService.ShowDialog(nameof(ProgressDialog), new DialogParameters(), null);
});
}
ダイアログのプロジェクトとは別の画面を作り、そのViewModelにShowDialogCommand
というダイアログの動作確認をするためのコマンドを用意しました。
ShowDialogCommand
では、非同期処理呼び出しとダイアログ呼び出しの2つを実行しています。
非同期処理では処理中にIEventAggregator
を介してProgressMessage
を発行しModelに進捗を表す数値を送っています。ここでは1秒間隔でModelの進捗の値を変更しています。ContinueWith
の第一引数部分には非同期処理でエラーになった際の処理を書く予定です。
ダイアログ呼び出しはIDialogService
のShowDialog
を使って定義したダイアログを呼び出しています。
おわりに
プログレスバーの付いたダイアログを作ることができました。他の画面から呼び出せることとプログレスバーの値を処理の進み具合によって制御できることを確認できました。