in my application I'm opening a Window for an Input form. In my App.xaml I have defined the following:
<DataTemplate DataType="{x:Type ViewModels:EditTicketViewModel}">
<Frame>
<Frame.Content>
<Views:EditTicketView></Views:EditTicketView>
</Frame.Content>
</Frame>
</DataTemplate>
My application also has a Window service for opening windows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DevPortal.Interfaces
{
public interface IWindowService
{
public void ShowWindow(object viewModel, bool showDialog);
}
}
the implementation:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using DevPortal.Interfaces;
using Syncfusion.Windows.Shared;
using Syncfusion.SfSkinManager;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace DevPortal.Services
{
public class WindowService : IWindowService
{
public void ShowWindow(object viewModel, bool showDialog)
{
var window = new ChromelessWindow();
window.ResizeMode = ResizeMode.NoResize;
SfSkinManager.SetTheme(window, new Theme("FluentDark"));
window.Content = viewModel;
window.SizeToContent = SizeToContent.WidthAndHeight;
window.Title = viewModel.GetType().GetProperty("Title").GetValue(viewModel).ToString();
window.ShowIcon = false;
if (showDialog)
{
window.ShowDialog();
} else
{
window.Show();
}
}
}
}
How I open the window (from a viewmodel in the MainView)
[RelayCommand]
private void CreateTicket()
{
App.Current.ServiceProvider.GetService<IWindowService>().ShowWindow(new EditTicketViewModel(), true);
}
What would be the best way to close this window from the ViewModel? Previously i was used to directly create the view, and in the constructor of the view i would subscribe to a close-event in the viewmodel, but that's not really the MVVM-way I guess. Do I need to implement some kind of service? Thanks!
EDIT: I forgott to mention that the View is a page. So i Am creating a window with the viewmodel as content, and the datatemplate of the viewmodel is a Frame containing the page.
CodePudding user response:
You could for example return an IWindow
from your window service:
public class WindowService : IWindowService
{
public IWindow ShowWindow(object viewModel, bool showDialog)
{
var window = new ChromelessWindow();
...
return window;
}
}
...and then simply call Close()
on this one in the view model.
The interface would be as simple as this:
public interface IWindow
{
void Close();
}
Your ChromelessWindow
implements the interface:
public partial class ChromelessWindow : Window, IWindow { ... }
...and the view model only has a dependency on an interface. It still doesn't know anything about a view or actual window. IWindow
is just a name. I can be called anything.
CodePudding user response:
The cleanest way of closing a Window from it's ViewModel is using an attached property.
public static class perWindowHelper
{
public static readonly DependencyProperty CloseWindowProperty = DependencyProperty.RegisterAttached(
"CloseWindow",
typeof(bool?),
typeof(perWindowHelper),
new PropertyMetadata(null, OnCloseWindowChanged));
private static void OnCloseWindowChanged(DependencyObject target, DependencyPropertyChangedEventArgs args)
{
if (!(target is Window view))
{
return;
}
if (view.IsModal())
{
view.DialogResult = args.NewValue as bool?;
}
else
{
view.Close();
}
}
public static void SetCloseWindow(Window target, bool? value)
{
target.SetValue(CloseWindowProperty, value);
}
public static bool IsModal(this Window window)
{
var fieldInfo = typeof(Window).GetField("_showingAsDialog", BindingFlags.Instance | BindingFlags.NonPublic);
return fieldInfo != null && (bool)fieldInfo.GetValue(window);
}
}
In the ViewModel create a ViewClosed property
private bool? _viewClosed;
public bool? ViewClosed
{
get => _viewClosed;
set => Set(nameof(ViewClosed), ref _viewClosed, value);
}
then in the View, bind to it using the attached property
<Window
...
vhelp:perWindowHelper.CloseWindow="{Binding ViewClosed}" >