Skip to content

Commit

Permalink
Added the ability to close an open Window from the side that opened it.
Browse files Browse the repository at this point in the history
  • Loading branch information
nuitsjp committed Nov 18, 2023
1 parent cd6f974 commit 9e3717e
Show file tree
Hide file tree
Showing 12 changed files with 146 additions and 40 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using Kamishibai;
using System.Reactive.Disposables;
using Kamishibai;
using Microsoft.Toolkit.Mvvm.Input;

namespace SampleBrowser.ViewModel.Page;

public class OpenWindowViewModel
public class OpenWindowViewModel : IDisposingAware, IPausingAware
{
private readonly CompositeDisposable _disposable = new();
private readonly IPresentationService _presentationService;

public OpenWindowViewModel(IPresentationService presentationService)
Expand All @@ -22,23 +24,38 @@ public OpenWindowViewModel(IPresentationService presentationService)
public WindowStartupLocation SelectedWindowStartupLocation { get; set; } = WindowStartupLocation.CenterOwner;

public AsyncRelayCommand<object> OpenByTypeCommand =>
new(owner => _presentationService.OpenWindowAsync(typeof(WindowWithoutArgumentsViewModel), owner, new OpenWindowOptions {WindowStartupLocation = SelectedWindowStartupLocation}));
new(owner => _presentationService.OpenWindowAsync(typeof(WindowWithoutArgumentsViewModel), owner, new OpenWindowOptions { WindowStartupLocation = SelectedWindowStartupLocation })
.AddTo(_disposable));

public AsyncRelayCommand<object> OpenByGenericTypeCommand =>
new (owner => _presentationService.OpenWindowAsync<WindowWithoutArgumentsViewModel>(owner, new OpenWindowOptions{WindowStartupLocation = SelectedWindowStartupLocation}));
new (owner => _presentationService.OpenWindowAsync<WindowWithoutArgumentsViewModel>(owner, new OpenWindowOptions{WindowStartupLocation = SelectedWindowStartupLocation})
.AddTo(_disposable));

public string WindowName1 { get; set; } = "Hello, Instance!";

public AsyncRelayCommand<object> OpenByInstanceCommand =>
new(owner => _presentationService.OpenWindowAsync(new WindowWithArgumentsViewModel(WindowName1, _presentationService), owner, new OpenWindowOptions { WindowStartupLocation = SelectedWindowStartupLocation }));
new(owner => _presentationService.OpenWindowAsync(new WindowWithArgumentsViewModel(WindowName1, _presentationService), owner, new OpenWindowOptions { WindowStartupLocation = SelectedWindowStartupLocation })
.AddTo(_disposable));

public string WindowName2 { get; set; } = "Hello, Callback!";

public AsyncRelayCommand<object> OpenWithCallbackCommand =>
new(owner => _presentationService.OpenWindowAsync<WindowWithoutArgumentsViewModel>(viewModel => viewModel.WindowName = WindowName2, owner, new OpenWindowOptions { WindowStartupLocation = SelectedWindowStartupLocation }));
new(owner => _presentationService.OpenWindowAsync<WindowWithoutArgumentsViewModel>(viewModel => viewModel.WindowName = WindowName2, owner, new OpenWindowOptions { WindowStartupLocation = SelectedWindowStartupLocation })
.AddTo(_disposable));

public string WindowName3 { get; set; } = "Hello, Safe Parameters!";

public AsyncRelayCommand<object> OpenWithSafeParameterCommand =>
new(owner => _presentationService.OpenWindowWithArgumentsWindowAsync(WindowName3, owner, new OpenWindowOptions { WindowStartupLocation = SelectedWindowStartupLocation }));
new(owner => _presentationService.OpenWindowWithArgumentsWindowAsync(WindowName3, owner, new OpenWindowOptions { WindowStartupLocation = SelectedWindowStartupLocation })
.AddTo(_disposable));

public void OnDisposing(PreBackwardEventArgs args)
{
_disposable.Dispose();
}

public void OnPausing(PreForwardEventArgs args)
{
_disposable.Dispose();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System.Reactive.Disposables;
using Kamishibai;

namespace SampleBrowser.ViewModel.Page;

public static class WindowHandleExtensions
{
public static async Task AddTo(this Task<IWindowHandle> windowHandleTask, CompositeDisposable disposable)
{
var windowHandle = await windowHandleTask;
disposable.Add(windowHandle);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

<ItemGroup>
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" PrivateAssets="All" />
<PackageReference Include="System.Reactive" Version="5.0.0" />
</ItemGroup>

<ItemGroup>
Expand Down
24 changes: 12 additions & 12 deletions Source/Kamishibai.CodeAnalysis.Test/GenerateOpenWindowTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace TestProject
{
public partial interface IPresentationService : IPresentationServiceBase
{
Task OpenFooWindowAsync(object? owner = null, OpenWindowOptions? options = null);
Task<IWindowHandle> OpenFooWindowAsync(object? owner = null, OpenWindowOptions? options = null);
}
public class PresentationService : PresentationServiceBase, IPresentationService
Expand All @@ -41,7 +41,7 @@ public PresentationService(IServiceProvider serviceProvider, INavigationFramePro
_serviceProvider = serviceProvider;
}
public Task OpenFooWindowAsync(object? owner = null, OpenWindowOptions? options = null)
public Task<IWindowHandle> OpenFooWindowAsync(object? owner = null, OpenWindowOptions? options = null)
{
return OpenWindowAsync(
new Foo(
Expand Down Expand Up @@ -81,7 +81,7 @@ namespace TestProject
{
public partial interface IPresentationService : IPresentationServiceBase
{
Task OpenFooWindowAsync(object? owner = null, OpenWindowOptions? options = null);
Task<IWindowHandle> OpenFooWindowAsync(object? owner = null, OpenWindowOptions? options = null);
}
public class PresentationService : PresentationServiceBase, IPresentationService
Expand All @@ -94,7 +94,7 @@ public PresentationService(IServiceProvider serviceProvider, INavigationFramePro
_serviceProvider = serviceProvider;
}
public Task OpenFooWindowAsync(object? owner = null, OpenWindowOptions? options = null)
public Task<IWindowHandle> OpenFooWindowAsync(object? owner = null, OpenWindowOptions? options = null)
{
return OpenWindowAsync(
new Foo(
Expand Down Expand Up @@ -134,7 +134,7 @@ namespace TestProject
{
public partial interface IPresentationService : IPresentationServiceBase
{
Task OpenFooWindowAsync(object? owner = null, OpenWindowOptions? options = null);
Task<IWindowHandle> OpenFooWindowAsync(object? owner = null, OpenWindowOptions? options = null);
}
public class PresentationService : PresentationServiceBase, IPresentationService
Expand All @@ -147,7 +147,7 @@ public PresentationService(IServiceProvider serviceProvider, INavigationFramePro
_serviceProvider = serviceProvider;
}
public Task OpenFooWindowAsync(object? owner = null, OpenWindowOptions? options = null)
public Task<IWindowHandle> OpenFooWindowAsync(object? owner = null, OpenWindowOptions? options = null)
{
return OpenWindowAsync(
new Foo(
Expand Down Expand Up @@ -194,7 +194,7 @@ namespace TestProject
{
public partial interface IPresentationService : IPresentationServiceBase
{
Task OpenBarWindowAsync(int number, Foo.Argument argument, object? owner = null, OpenWindowOptions? options = null);
Task<IWindowHandle> OpenBarWindowAsync(int number, Foo.Argument argument, object? owner = null, OpenWindowOptions? options = null);
}
public class PresentationService : PresentationServiceBase, IPresentationService
Expand All @@ -207,7 +207,7 @@ public PresentationService(IServiceProvider serviceProvider, INavigationFramePro
_serviceProvider = serviceProvider;
}
public Task OpenBarWindowAsync(int number, Foo.Argument argument, object? owner = null, OpenWindowOptions? options = null)
public Task<IWindowHandle> OpenBarWindowAsync(int number, Foo.Argument argument, object? owner = null, OpenWindowOptions? options = null)
{
return OpenWindowAsync(
new Foo.Bar(
Expand Down Expand Up @@ -256,7 +256,7 @@ namespace TestProject
{
public partial interface IPresentationService : IPresentationServiceBase
{
Task OpenBarWindowAsync(int number, Foo.Argument argument, object? owner = null, OpenWindowOptions? options = null);
Task<IWindowHandle> OpenBarWindowAsync(int number, Foo.Argument argument, object? owner = null, OpenWindowOptions? options = null);
}
public class PresentationService : PresentationServiceBase, IPresentationService
Expand All @@ -269,7 +269,7 @@ public PresentationService(IServiceProvider serviceProvider, INavigationFramePro
_serviceProvider = serviceProvider;
}
public Task OpenBarWindowAsync(int number, Foo.Argument argument, object? owner = null, OpenWindowOptions? options = null)
public Task<IWindowHandle> OpenBarWindowAsync(int number, Foo.Argument argument, object? owner = null, OpenWindowOptions? options = null)
{
return OpenWindowAsync(
new Foo.Bar(
Expand Down Expand Up @@ -318,7 +318,7 @@ namespace TestProject
{
public partial interface IPresentationService : IPresentationServiceBase
{
Task OpenBarWindowAsync(int? number, Foo.Argument argument, object? owner = null, OpenWindowOptions? options = null);
Task<IWindowHandle> OpenBarWindowAsync(int? number, Foo.Argument argument, object? owner = null, OpenWindowOptions? options = null);
}
public class PresentationService : PresentationServiceBase, IPresentationService
Expand All @@ -331,7 +331,7 @@ public PresentationService(IServiceProvider serviceProvider, INavigationFramePro
_serviceProvider = serviceProvider;
}
public Task OpenBarWindowAsync(int? number, Foo.Argument argument, object? owner = null, OpenWindowOptions? options = null)
public Task<IWindowHandle> OpenBarWindowAsync(int? number, Foo.Argument argument, object? owner = null, OpenWindowOptions? options = null)
{
return OpenWindowAsync(
new Foo.Bar(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public override string TransformText()
foreach(var openWindowInfo in OpenWindowInfos)
{

this.Write(" Task Open");
this.Write(" Task<IWindowHandle> Open");
this.Write(this.ToStringHelper.ToStringWithCulture(openWindowInfo.NavigationName));
this.Write("WindowAsync(");
this.Write(this.ToStringHelper.ToStringWithCulture(openWindowInfo.NavigationParameters));
Expand Down Expand Up @@ -96,7 +96,7 @@ public PresentationService(IServiceProvider serviceProvider, INavigationFramePro
foreach(var openWindowInfo in OpenWindowInfos)
{

this.Write(" public Task Open");
this.Write(" public Task<IWindowHandle> Open");
this.Write(this.ToStringHelper.ToStringWithCulture(openWindowInfo.NavigationName));
this.Write("WindowAsync(");
this.Write(this.ToStringHelper.ToStringWithCulture(openWindowInfo.NavigationParameters));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ foreach(var navigationInfo in NavigationInfos)
foreach(var openWindowInfo in OpenWindowInfos)
{
#>
Task Open<#= openWindowInfo.NavigationName #>WindowAsync(<#= openWindowInfo.NavigationParameters #>);
Task<IWindowHandle> Open<#= openWindowInfo.NavigationName #>WindowAsync(<#= openWindowInfo.NavigationParameters #>);
<#
}

Expand Down
61 changes: 61 additions & 0 deletions Source/Kamishibai.View/WindowHandle.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using System.Windows;

namespace Kamishibai;

/// <summary>
/// WindowHandle is a wrapper class for <see cref="Window"/> to implement <see cref="IWindowHandle"/>.
/// </summary>
public class WindowHandle : IWindowHandle
{
/// <summary>
/// Window instance.
/// </summary>
private readonly Window _window;

/// <summary>
/// Window is closed.
/// </summary>
private bool _closed;

/// <summary>
/// Constructor.
/// </summary>
/// <param name="window"></param>
public WindowHandle(Window window)
{
_window = window;
_window.Closed += OnClosed;
}

/// <summary>
/// Window is closed.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnClosed(object sender, EventArgs e)
{
_closed = true;
}


/// <summary>
/// Dispose.
/// </summary>
public void Dispose()
{
Close();
}

/// <summary>
/// Close window.
/// </summary>
public void Close()
{
if (_closed)
{
return;
}

_window.Close();
}
}
14 changes: 8 additions & 6 deletions Source/Kamishibai.View/WindowService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,28 @@ public WindowService(IServiceProvider serviceProvider)
_serviceProvider = serviceProvider;
}

public Task OpenWindowAsync(Type viewModelType, object? owner, OpenWindowOptions options)
public Task<IWindowHandle> OpenWindowAsync(Type viewModelType, object? owner, OpenWindowOptions options)
{
var viewModel = _serviceProvider.GetService(viewModelType)!;
return OpenWindowAsync(viewModelType, owner, viewModel, options);
}

public Task OpenWindowAsync<TViewModel>(object? owner, OpenWindowOptions options)
public Task<IWindowHandle> OpenWindowAsync<TViewModel>(object? owner, OpenWindowOptions options)
=> OpenWindowAsync(typeof(TViewModel), owner, options);

public Task OpenWindowAsync<TViewModel>(TViewModel viewModel, object? owner, OpenWindowOptions options) where TViewModel : notnull
public Task<IWindowHandle> OpenWindowAsync<TViewModel>(TViewModel viewModel, object? owner, OpenWindowOptions options) where TViewModel : notnull
{
return OpenWindowAsync(typeof(TViewModel), owner, viewModel, options);
}

public Task OpenWindowAsync<TViewModel>(Action<TViewModel> init, object? owner, OpenWindowOptions options)
public Task<IWindowHandle> OpenWindowAsync<TViewModel>(Action<TViewModel> init, object? owner, OpenWindowOptions options)
{
var viewModel = (TViewModel)_serviceProvider.GetService(typeof(TViewModel))!;
init(viewModel);
return OpenWindowAsync(typeof(TViewModel), owner, viewModel, options);
}

public async Task OpenWindowAsync(Type viewModelType, object? owner, object viewModel, OpenWindowOptions options)
public async Task<IWindowHandle> OpenWindowAsync(Type viewModelType, object? owner, object viewModel, OpenWindowOptions options)
{
var window = GetWindow(viewModelType);
window.DataContext = viewModel;
Expand All @@ -50,6 +50,8 @@ public async Task OpenWindowAsync(Type viewModelType, object? owner, object view
await NotifyNavigated(postForwardEventArgs);

SetupCloseEvents(window);

return new WindowHandle(window);
}

public Task<bool> OpenDialogAsync(Type viewModelType, object? owner, OpenDialogOptions options)
Expand Down Expand Up @@ -375,4 +377,4 @@ private static IEnumerable<INavigationFrame> FindNavigationFrame(DependencyObjec
}
}
}
}
}
8 changes: 4 additions & 4 deletions Source/Kamishibai/IPresentationServiceBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ public interface IPresentationServiceBase
bool CanGoBack(string frameName = "");
INavigationFrame GetNavigationFrame(string frameName = "");

Task OpenWindowAsync(Type viewModelType, object? owner = null, OpenWindowOptions? options = null);
Task OpenWindowAsync<TViewModel>(object? owner = null, OpenWindowOptions? options = null);
Task OpenWindowAsync<TViewModel>(TViewModel viewModel, object? owner = null, OpenWindowOptions? options = null) where TViewModel : notnull;
Task OpenWindowAsync<TViewModel>(Action<TViewModel> init, object? owner = null, OpenWindowOptions? options = null);
Task<IWindowHandle> OpenWindowAsync(Type viewModelType, object? owner = null, OpenWindowOptions? options = null);
Task<IWindowHandle> OpenWindowAsync<TViewModel>(object? owner = null, OpenWindowOptions? options = null);
Task<IWindowHandle> OpenWindowAsync<TViewModel>(TViewModel viewModel, object? owner = null, OpenWindowOptions? options = null) where TViewModel : notnull;
Task<IWindowHandle> OpenWindowAsync<TViewModel>(Action<TViewModel> init, object? owner = null, OpenWindowOptions? options = null);
Task CloseWindowAsync(object? window = null);

Task<bool> OpenDialogAsync(Type viewModelType, object? owner = null, OpenDialogOptions? options = null);
Expand Down
12 changes: 12 additions & 0 deletions Source/Kamishibai/IWindowHandle.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Kamishibai;

/// <summary>
/// WindowHandle is a wrapper class for Window.
/// </summary>
public interface IWindowHandle : IDisposable
{
/// <summary>
/// Close window.
/// </summary>
void Close();
}
8 changes: 4 additions & 4 deletions Source/Kamishibai/IWindowService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

public interface IWindowService
{
Task OpenWindowAsync(Type viewModelType, object? owner, OpenWindowOptions options);
Task OpenWindowAsync<TViewModel>(object? owner, OpenWindowOptions options);
Task OpenWindowAsync<TViewModel>(TViewModel viewModel, object? owner, OpenWindowOptions options) where TViewModel : notnull;
Task OpenWindowAsync<TViewModel>(Action<TViewModel> init, object? owner, OpenWindowOptions options);
Task<IWindowHandle> OpenWindowAsync(Type viewModelType, object? owner, OpenWindowOptions options);
Task<IWindowHandle> OpenWindowAsync<TViewModel>(object? owner, OpenWindowOptions options);
Task<IWindowHandle> OpenWindowAsync<TViewModel>(TViewModel viewModel, object? owner, OpenWindowOptions options) where TViewModel : notnull;
Task<IWindowHandle> OpenWindowAsync<TViewModel>(Action<TViewModel> init, object? owner, OpenWindowOptions options);
Task<bool> OpenDialogAsync(Type viewModelType, object? owner, OpenDialogOptions options);
Task<bool> OpenDialogAsync<TViewModel>(object? owner, OpenDialogOptions options);
Task<bool> OpenDialogAsync<TViewModel>(TViewModel viewModel, object? owner, OpenDialogOptions options) where TViewModel : notnull;
Expand Down
8 changes: 4 additions & 4 deletions Source/Kamishibai/PresentationServiceBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,16 @@ public Task<bool> GoBackAsync(string frameName = "")
public INavigationFrame GetNavigationFrame(string frameName = "") =>
_navigationFrameProvider.GetNavigationFrame(frameName);

public Task OpenWindowAsync(Type viewModelType, object? owner = null, OpenWindowOptions? options = null)
public Task<IWindowHandle> OpenWindowAsync(Type viewModelType, object? owner = null, OpenWindowOptions? options = null)
=> _windowService.OpenWindowAsync(viewModelType, owner, options ?? new OpenWindowOptions());

public Task OpenWindowAsync<TViewModel>(object? owner = null, OpenWindowOptions? options = null)
public Task<IWindowHandle> OpenWindowAsync<TViewModel>(object? owner = null, OpenWindowOptions? options = null)
=> _windowService.OpenWindowAsync<TViewModel>(owner, options ?? new OpenWindowOptions());

public Task OpenWindowAsync<TViewModel>(TViewModel viewModel, object? owner = null, OpenWindowOptions? options = null) where TViewModel : notnull
public Task<IWindowHandle> OpenWindowAsync<TViewModel>(TViewModel viewModel, object? owner = null, OpenWindowOptions? options = null) where TViewModel : notnull
=> _windowService.OpenWindowAsync(viewModel, owner, options ?? new OpenWindowOptions());

public Task OpenWindowAsync<TViewModel>(Action<TViewModel> init, object? owner = null, OpenWindowOptions? options = null)
public Task<IWindowHandle> OpenWindowAsync<TViewModel>(Action<TViewModel> init, object? owner = null, OpenWindowOptions? options = null)
=> _windowService.OpenWindowAsync(init, owner, options ?? new OpenWindowOptions());

public Task<bool> OpenDialogAsync(Type viewModelType, object? owner = null, OpenDialogOptions? options = null)
Expand Down

0 comments on commit 9e3717e

Please sign in to comment.