I tried to follow the logic, described on Jeremy's post here:
https://jeremytammik.github.io/tbc/a/0088_revit_window_handle.htm
First - what is a goal.
The goal is to create a Win Form window, which would start with Revit Application and close with Revit Application. Window is in modeless dialog and displays the output of different actions/commands which are happening while working in Revit Document. Everything works fine for me, except for the part, that with each Revit Command new instance of Form Window is created. I would like to keep only one window which hides and shows, whenever any action is taken.
Below where I came so far - I hope anyone could be able to direct me which path to go....
Win Form Class:
public partial class CmdWindowHandleForm : Form
{
public string LabelText
{
get{ return _labelText.Text; }
set{ _labelText.Text = value; }
}
public CmdWindowHandleForm()
{
InitializeComponent();
}
}
partial class CmdWindowHandleForm
{
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this._labelText = new Label();
this._labelText.Text = string.Empty;
this.Controls.Add(this._labelText);
}
}
Class with WindowHandle, which is used as argument when Form.Show() method is called:
public class WindowHandle : IWin32Window
{
public IntPtr Handle
{
get{ return _handleWindow; }
}
IntPtr _handleWindow;
public WindowHandle(IntPtr handleWindow)
{
_handleWindow = handleWindow;
}
}
And lastly my static class of InfoConsole window, which would run along Revit app:
public static class InfoConsole
{
public static CmdWindowHandleForm Window
{
get{ return _window; }
}
static WindowHandle _hWndRevit = null;
static CmdWindowHandleForm _window = null;
public static void Show(string message)
{
if (_hWndRevit == null)
{
Process process = Process.GetCurrentProcess();
IntPtr h = process.MainWindowHandle;
_hWndRevit = new WindowHandle(h);
}
if(InfoConsole._window == null)
{
_window = new CmdWindowHandleForm();
_window.Show(_hWndRevit as IWin32Window);
}
else
{
_window.Visible = true;
}
_window.AddText(message);
}
}
And while running a command (implementing IExternalCommand - interface required to access Revit DB) method is simply called:
InfoConsole.Show(outputMessage);
Any help much appreciated...
Lukasz
The code you refer to is way out of date. It was published by The Building Coder in February 2009: Revit Window Handle and Modeless Dialogues.
Access to the Revit main window handle changed in 2018: Revit Window Handle and Parenting an Add-In Form.
This question was also raised and discussed in the Revit API discussion forum: Problem with modeless Form Window, which implements IWin32Window. Please refer to that for further, continued discussion and the final solution, still under development as I write this.
I am developing a WPF-Application using PRISM/Unity and the MVVM approach. So far everything is fine, but I think I have a conceptual problem.
Since I need modal dialogs I implemented a dialogservice, which gets injected into the viewmodels and can be used to open up a modal dialog with a corresponding view. This dialog needs to be navigable ( = contains a region), so that I am able to switch views within the dialog. For this the service creates an instance of a window and navigates to the requestd view in a DialogRegion. This also works.
My problem now is, that at some point I need to open a second modal dialog which also needs to be navigable. If I use my dialogservice I would get a new instance of the dialog window and an exception is thrown, since the region was already registered with the regionmanager (It's done in the code behind of the dialog window). This makes sense, since the regionmanager would not be able to distinguish the regions otherwise, but prevents me from opening a second navigable dialog.
What would be a better approach? I can only think of creating a second dialog window with a different name for the region, but that seems to be a very ugly solution...
EDIT 1:
Code for showing a dialog:
dialogService.ShowDialog<MyViewModel>();
Code in the dialog service for showing a dialog:
public void ShowDialog<TDialogViewModel>() where TDialogViewModel : class
{
IModalDialog dialog = ServiceLocator.Current.TryResolve<IModalDialog>();
dialog.Owner = Application.Current.MainWindow;
regionManager.RequestNavigate(typeof(TDialogViewModel), Regions.DialogRegion);
dialog.ShowDialog();
}
Code in the dialog-window:
public DialogWindow(IRegionManager regionManager) : this()
{
RegionManager.SetRegionManager(this, regionManager);
this.regionManager = regionManager;
}
private void Window_Unloaded(object sender, RoutedEventArgs e)
{
regionManager.Regions.Remove(Regions.DialogRegion);
regionManager.Regions.Remove(Regions.DialogStatusRegion);
}
Code in the view for construction, navigation and showing other dialogs
public MyViewModel(IRegionManager regionManager, IModalDialogService dialogService) : this()
{
this.regionManager = regionManager;
this.dialogService = dialogService;
}
void NavigateToSecondView()
{
regionManager.RequestNavigate<MyViewModel2>(Regions.DialogRegion);
}
void ShowDialog2()
{
// This is where a second dialog window with MyViewModel3 should be shown
dialogService.ShowDialog<MyViewModel3>();
}
EDIT 2:
XAML for the Window:
<Window.Resources>
<DataTemplate DataType="{x:Type localModels:MyViewModel}">
<localViewsProject:MyView/>
</DataTemplate>
</Window.Resources>
<Grid>
<ContentControl Grid.Row="0" x:Name="DialogContent" Margin="10,10,10,10"
prismRegions:RegionManager.RegionName="{x:Static navi:Regions.DialogRegion}">
</ContentControl>
</Grid>
EDIT 3 - Code for IModalDialog:
I got the idea for IModalDialog somewhere in the web, but I can't remember where. The idea is to have an interface that is implemented by some window-instances and allows for interaction with the user. My dialog only has one content-region (plus a region for status/progress notifications) that can be filled through prism.
Here some code to explain the idea:
public partial class DialogWindow : Window, IModalDialog
{
internal IRegionManager regionManager;
public DialogWindow()
{
InitializeComponent();
}
public DialogWindow(IRegionManager regionManager)
: this()
{
RegionManager.SetRegionManager(this, regionManager);
this.regionManager = regionManager;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// Load Model, when window is loaded
BaseViewModel model = this.DialogContent.Content as BaseViewModel;
if (model != null)
{
model.LoadData();
}
}
private void Window_Unloaded(object sender, RoutedEventArgs e)
{
regionManager.Regions.Remove(Regions.DialogRegion);
regionManager.Regions.Remove(Regions.DialogStatusRegion);
}
}
So in order to use it I have a static ModalDialogService, which receives a model and some action to be done after the dialog is closed. Something like this:
public void ShowDialog<TDialogViewModel>(Action<TDialogViewModel> initializeAction = null, Action<TDialogViewModel> actionAfterClose = null) where TDialogViewModel : class
{
IModalDialog dialog = ServiceLocator.Current.TryResolve<IModalDialog>();
if (actionAfterClose != null)
{
WeakEventManager<IModalDialog, EventArgs>.AddHandler(
dialog,
"Closed",
(sender, e) => dialogView_Closed(sender, e, typeof(TDialogViewModel), Regions.DialogRegion, actionAfterClose));
}
dialog.Owner = Application.Current.MainWindow;
regionManager.RequestNavigate(typeof(TDialogViewModel), Regions.DialogRegion);
TDialogViewModel model = null;
var region = regionManager.Regions[Regions.DialogRegion];
foreach (var view in region.ActiveViews)
{
if (view is TDialogViewModel)
{
model = (TDialogViewModel)view;
if (initializeAction != null)
{
initializeAction(model);
}
}
}
// Attach an event listener to the closing event in order to prevent closing
// if the form is not in a closeable state.
if (typeof(IConfirmNavigationRequest).IsAssignableFrom(typeof(TDialogViewModel)))
{
WeakEventManager<IModalDialog, CancelEventArgs>.AddHandler(
dialog,
"Closing",
(sender, e) => dialogView_Closing(sender, e, Regions.DialogRegion));
}
// This event can be published by a viewmodel in order to close the dialog
eventAggregator.GetEvent<DialogCloseRequestedEvent>().Subscribe((containingDialog) =>
{
if (containingDialog == model)
{
dialog.Close();
}
});
dialog.ShowDialog();
}
}
Instead of re-registering a Region with the RegionManager. You could reuse the initial Region that is registered and create multiple instances of it. These are known as Scoped Regions (under the section Creating Multiple Instances of a Region). For example:
// You may need to keep track of these scopes
IRegionManager scopedRegionManager = _regionManager.Regions["DialogRegion"].Add(view, viewName, true);
scopedRegionManager.Navigate(viewName);
EDIT
I believe it's as simple as changing the constructor of your DialogWindow to this:
public DialogWindow(IRegionManager regionManager) : this()
{
var scopedRegionManager = regionManager.Regions[Regions.DialogRegion].Add(this, null, true);
this.regionManager = scopedRegionManager;
}
This question already has answers here:
How should the ViewModel close the form?
(25 answers)
Closed 1 year ago.
Im creating a Login using a window control to allow a user to login into a WPF application that I am creating.
So far, I have created a method that checks whether the user has entered in the correct credentials for the username and password in a textbox on the login screen, binding two properties.
I have achieved this by creating a bool method, like so;
public bool CheckLogin()
{
var user = context.Users.Where(i => i.Username == this.Username).SingleOrDefault();
if (user == null)
{
MessageBox.Show("Unable to Login, incorrect credentials.");
return false;
}
else if (this.Username == user.Username || this.Password.ToString() == user.Password)
{
MessageBox.Show("Welcome " + user.Username + ", you have successfully logged in.");
return true;
}
else
{
MessageBox.Show("Unable to Login, incorrect credentials.");
return false;
}
}
public ICommand ShowLoginCommand
{
get
{
if (this.showLoginCommand == null)
{
this.showLoginCommand = new RelayCommand(this.LoginExecute, null);
}
return this.showLoginCommand;
}
}
private void LoginExecute()
{
this.CheckLogin();
}
I also have a command that I bind to my button within the xaml like so;
<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding ShowLoginCommand}" />
When I enter in the username and password it executes the appropriated code, whether it being right, or wrong. But how can I close this window from the ViewModel when both username and password are correct?
I have previously tried using a dialog modal but it didn't quite work out. Furthermore, within my app.xaml, I have done something like the following, which loads the login page first, then once true, loads the actual application.
private void ApplicationStart(object sender, StartupEventArgs e)
{
Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
var dialog = new UserView();
if (dialog.ShowDialog() == true)
{
var mainWindow = new MainWindow();
Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
Current.MainWindow = mainWindow;
mainWindow.Show();
}
else
{
MessageBox.Show("Unable to load application.", "Error", MessageBoxButton.OK);
Current.Shutdown(-1);
}
}
Question: How can I close the Login Window control from the ViewModel?
Thanks in advance.
You can pass the window to your ViewModel using the CommandParameter. See my Example below.
I've implemented an CloseWindow Method which takes a Windows as parameter and closes it. The window is passed to the ViewModel via CommandParameter. Note that you need to define an x:Name for the window which should be close. In my XAML Window i call this method via Command and pass the window itself as a parameter to the ViewModel using CommandParameter.
Command="{Binding CloseWindowCommand, Mode=OneWay}"
CommandParameter="{Binding ElementName=TestWindow}"
ViewModel
public RelayCommand<Window> CloseWindowCommand { get; private set; }
public MainViewModel()
{
this.CloseWindowCommand = new RelayCommand<Window>(this.CloseWindow);
}
private void CloseWindow(Window window)
{
if (window != null)
{
window.Close();
}
}
View
<Window x:Class="ClientLibTestTool.ErrorView"
x:Name="TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:localization="clr-namespace:ClientLibTestTool.ViewLanguages"
DataContext="{Binding Main, Source={StaticResource Locator}}"
Title="{x:Static localization:localization.HeaderErrorView}"
Height="600" Width="800"
ResizeMode="NoResize"
WindowStartupLocation="CenterScreen">
<Grid>
<Button Content="{x:Static localization:localization.ButtonClose}"
Height="30"
Width="100"
Margin="0,0,10,10"
IsCancel="True"
VerticalAlignment="Bottom"
HorizontalAlignment="Right"
Command="{Binding CloseWindowCommand, Mode=OneWay}"
CommandParameter="{Binding ElementName=TestWindow}"/>
</Grid>
</Window>
Note that i'm using the MVVM light framework, but the principal applies to every wpf application.
This solution violates of the MVVM pattern, because the view-model shouldn't know anything about the UI Implementation. If you want to strictly follow the MVVM programming paradigm you have to abstract the type of the view with an interface.
MVVM conform solution (Former EDIT2)
the user Crono mentions a valid point in the comment section:
Passing the Window object to the view model breaks the MVVM pattern
IMHO, because it forces your vm to know what it's being viewed in.
You can fix this by introducing an interface containing a close method.
Interface:
public interface ICloseable
{
void Close();
}
Your refactored ViewModel will look like this:
ViewModel
public RelayCommand<ICloseable> CloseWindowCommand { get; private set; }
public MainViewModel()
{
this.CloseWindowCommand = new RelayCommand<IClosable>(this.CloseWindow);
}
private void CloseWindow(ICloseable window)
{
if (window != null)
{
window.Close();
}
}
You have to reference and implement the ICloseable interface in your view
View (Code behind)
public partial class MainWindow : Window, ICloseable
{
public MainWindow()
{
InitializeComponent();
}
}
Answer to the original question: (former EDIT1)
Your Login Button (Added CommandParameter):
<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding ShowLoginCommand}" CommandParameter="{Binding ElementName=LoginWindow}"/>
Your code:
public RelayCommand<Window> CloseWindowCommand { get; private set; } // the <Window> is important for your solution!
public MainViewModel()
{
//initialize the CloseWindowCommand. Again, mind the <Window>
//you don't have to do this in your constructor but it is good practice, thought
this.CloseWindowCommand = new RelayCommand<Window>(this.CloseWindow);
}
public bool CheckLogin(Window loginWindow) //Added loginWindow Parameter
{
var user = context.Users.Where(i => i.Username == this.Username).SingleOrDefault();
if (user == null)
{
MessageBox.Show("Unable to Login, incorrect credentials.");
return false;
}
else if (this.Username == user.Username || this.Password.ToString() == user.Password)
{
MessageBox.Show("Welcome "+ user.Username + ", you have successfully logged in.");
this.CloseWindow(loginWindow); //Added call to CloseWindow Method
return true;
}
else
{
MessageBox.Show("Unable to Login, incorrect credentials.");
return false;
}
}
//Added CloseWindow Method
private void CloseWindow(Window window)
{
if (window != null)
{
window.Close();
}
}
I usually put an event on the view model when I need to do this and then hook it up to the Window.Close() when binding the view model to the window
public class LoginViewModel
{
public event EventHandler OnRequestClose;
private void Login()
{
// Login logic here
OnRequestClose(this, new EventArgs());
}
}
And when creating the login window
var vm = new LoginViewModel();
var loginWindow = new LoginWindow
{
DataContext = vm
};
vm.OnRequestClose += (s, e) => loginWindow.Close();
loginWindow.ShowDialog();
Staying MVVM, I think using either Behaviors from the Blend SDK (System.Windows.Interactivity) or a custom interaction request from Prism could work really well for this sort of situation.
If going the Behavior route, here's the general idea:
public class CloseWindowBehavior : Behavior<Window>
{
public bool CloseTrigger
{
get { return (bool)GetValue(CloseTriggerProperty); }
set { SetValue(CloseTriggerProperty, value); }
}
public static readonly DependencyProperty CloseTriggerProperty =
DependencyProperty.Register("CloseTrigger", typeof(bool), typeof(CloseWindowBehavior), new PropertyMetadata(false, OnCloseTriggerChanged));
private static void OnCloseTriggerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var behavior = d as CloseWindowBehavior;
if (behavior != null)
{
behavior.OnCloseTriggerChanged();
}
}
private void OnCloseTriggerChanged()
{
// when closetrigger is true, close the window
if (this.CloseTrigger)
{
this.AssociatedObject.Close();
}
}
}
Then in your window, you would just bind the CloseTrigger to a boolean value that would be set when you wanted the window to close.
<Window x:Class="TestApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:local="clr-namespace:TestApp"
Title="MainWindow" Height="350" Width="525">
<i:Interaction.Behaviors>
<local:CloseWindowBehavior CloseTrigger="{Binding CloseTrigger}" />
</i:Interaction.Behaviors>
<Grid>
</Grid>
</Window>
Finally, your DataContext/ViewModel would have a property that you'd set when you wanted the window to close like this:
public class MainWindowViewModel : INotifyPropertyChanged
{
private bool closeTrigger;
/// <summary>
/// Gets or Sets if the main window should be closed
/// </summary>
public bool CloseTrigger
{
get { return this.closeTrigger; }
set
{
this.closeTrigger = value;
RaisePropertyChanged(nameof(CloseTrigger));
}
}
public MainWindowViewModel()
{
// just setting for example, close the window
CloseTrigger = true;
}
protected void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
(set your Window.DataContext = new MainWindowViewModel())
it may be late, but here is my answer
foreach (Window item in Application.Current.Windows)
{
if (item.DataContext == this) item.Close();
}
Well here is something I used in several projects. It may look like a hack, but it works fine.
public class AttachedProperties : DependencyObject //adds a bindable DialogResult to window
{
public static readonly DependencyProperty DialogResultProperty =
DependencyProperty.RegisterAttached("DialogResult", typeof(bool?), typeof(AttachedProperties),
new PropertyMetaData(default(bool?), OnDialogResultChanged));
public bool? DialogResult
{
get { return (bool?)GetValue(DialogResultProperty); }
set { SetValue(DialogResultProperty, value); }
}
private static void OnDialogResultChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var window = d as Window;
if (window == null)
return;
window.DialogResult = (bool?)e.NewValue;
}
}
Now you can bind DialogResult to a VM and set its value of a property. The Window will close, when the value is set.
<!-- Assuming that the VM is bound to the DataContext and the bound VM has a property DialogResult -->
<Window someNs:AttachedProperties.DialogResult={Binding DialogResult} />
This is an abstract of what's running in our production environment
<Window x:Class="AC.Frontend.Controls.DialogControl.Dialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:DialogControl="clr-namespace:AC.Frontend.Controls.DialogControl"
xmlns:hlp="clr-namespace:AC.Frontend.Helper"
MinHeight="150" MinWidth="300" ResizeMode="NoResize" SizeToContent="WidthAndHeight"
WindowStartupLocation="CenterScreen" Title="{Binding Title}"
hlp:AttachedProperties.DialogResult="{Binding DialogResult}" WindowStyle="ToolWindow" ShowInTaskbar="True"
Language="{Binding UiCulture, Source={StaticResource Strings}}">
<!-- A lot more stuff here -->
</Window>
As you can see, I'm declaring the namespace xmlns:hlp="clr-namespace:AC.Frontend.Helper" first and afterwards the binding hlp:AttachedProperties.DialogResult="{Binding DialogResult}".
The AttachedProperty looks like this. It's not the same I posted yesterday, but IMHO it shouldn't have any effect.
public class AttachedProperties
{
#region DialogResult
public static readonly DependencyProperty DialogResultProperty =
DependencyProperty.RegisterAttached("DialogResult", typeof (bool?), typeof (AttachedProperties), new PropertyMetadata(default(bool?), OnDialogResultChanged));
private static void OnDialogResultChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var wnd = d as Window;
if (wnd == null)
return;
wnd.DialogResult = (bool?) e.NewValue;
}
public static bool? GetDialogResult(DependencyObject dp)
{
if (dp == null) throw new ArgumentNullException("dp");
return (bool?)dp.GetValue(DialogResultProperty);
}
public static void SetDialogResult(DependencyObject dp, object value)
{
if (dp == null) throw new ArgumentNullException("dp");
dp.SetValue(DialogResultProperty, value);
}
#endregion
}
Easy way
public interface IRequireViewIdentification
{
Guid ViewID { get; }
}
Implement to ViewModel
public class MyViewVM : IRequireViewIdentification
{
private Guid _viewId;
public Guid ViewID
{
get { return _viewId; }
}
public MyViewVM()
{
_viewId = Guid.NewGuid();
}
}
Add general window manager helper
public static class WindowManager
{
public static void CloseWindow(Guid id)
{
foreach (Window window in Application.Current.Windows)
{
var w_id = window.DataContext as IRequireViewIdentification;
if (w_id != null && w_id.ViewID.Equals(id))
{
window.Close();
}
}
}
}
And close it like this in viewmodel
WindowManager.CloseWindow(ViewID);
How about this ?
ViewModel:
class ViewModel
{
public Action CloseAction { get; set; }
private void Stuff()
{
// Do Stuff
CloseAction(); // closes the window
}
}
In your ViewModel use CloseAction() to close the window just like in the example above.
View:
public View()
{
InitializeComponent();
ViewModel vm = new ViewModel (); // this creates an instance of the ViewModel
this.DataContext = vm; // this sets the newly created ViewModel as the DataContext for the View
if (vm.CloseAction == null)
vm.CloseAction = new Action(() => this.Close());
}
I know this is an old post, probably no one would scroll this far, I know I didn't. So, after hours of trying different stuff, I found this blog and dude killed it. Simplest way to do this, tried it and it works like a charm.
Blog
In the ViewModel:
...
public bool CanClose { get; set; }
private RelayCommand closeCommand;
public ICommand CloseCommand
{
get
{
if(closeCommand == null)
(
closeCommand = new RelayCommand(param => Close(), param => CanClose);
)
}
}
public void Close()
{
this.Close();
}
...
add an Action property to the ViewModel, but define it from the View’s code-behind file. This will let us dynamically define a reference on the ViewModel that points to the View.
On the ViewModel, we’ll simply add:
public Action CloseAction { get; set; }
And on the View, we’ll define it as such:
public View()
{
InitializeComponent() // this draws the View
ViewModel vm = new ViewModel(); // this creates an instance of the ViewModel
this.DataContext = vm; // this sets the newly created ViewModel as the DataContext for the View
if ( vm.CloseAction == null )
vm.CloseAction = new Action(() => this.Close());
}
Here is a simple example using the MVVM Light Messenger instead of an event. The view model sends a close message when a button is clicked:
public MainViewModel()
{
QuitCommand = new RelayCommand(ExecuteQuitCommand);
}
public RelayCommand QuitCommand { get; private set; }
private void ExecuteQuitCommand()
{
Messenger.Default.Send<CloseMessage>(new CloseMessage());
}
Then it is received in the code behind of the window.
public Main()
{
InitializeComponent();
Messenger.Default.Register<CloseMessage>(this, HandleCloseMessage);
}
private void HandleCloseMessage(CloseMessage closeMessage)
{
Close();
}
You can create new Event handler in the ViewModel like this.
public event EventHandler RequestClose;
protected void OnRequestClose()
{
if (RequestClose != null)
RequestClose(this, EventArgs.Empty);
}
Then Define RelayCommand for ExitCommand.
private RelayCommand _CloseCommand;
public ICommand CloseCommand
{
get
{
if(this._CloseCommand==null)
this._CloseCommand=new RelayCommand(CloseClick);
return this._CloseCommand;
}
}
private void CloseClick(object obj)
{
OnRequestClose();
}
Then In XAML file set
<Button Command="{Binding CloseCommand}" />
Set the DataContext in the xaml.cs File and Subscribe to the event we created.
public partial class MainWindow : Window
{
private ViewModel mainViewModel = null;
public MainWindow()
{
InitializeComponent();
mainViewModel = new ViewModel();
this.DataContext = mainViewModel;
mainViewModel.RequestClose += delegate(object sender, EventArgs args) { this.Close(); };
}
}
My proffered way is Declare event in ViewModel and use blend InvokeMethodAction as below.
Sample ViewModel
public class MainWindowViewModel : BindableBase, ICloseable
{
public DelegateCommand SomeCommand { get; private set; }
#region ICloseable Implementation
public event EventHandler CloseRequested;
public void RaiseCloseNotification()
{
var handler = CloseRequested;
if (handler != null)
{
handler.Invoke(this, EventArgs.Empty);
}
}
#endregion
public MainWindowViewModel()
{
SomeCommand = new DelegateCommand(() =>
{
//when you decide to close window
RaiseCloseNotification();
});
}
}
I Closeable interface is as below but don't require to perform this action. ICloseable will help in creating generic view service, so if you construct view and ViewModel by dependency injection then what you can do is
internal interface ICloseable
{
event EventHandler CloseRequested;
}
Use of ICloseable
var viewModel = new MainWindowViewModel();
// As service is generic and don't know whether it can request close event
var window = new Window() { Content = new MainView() };
var closeable = viewModel as ICloseable;
if (closeable != null)
{
closeable.CloseRequested += (s, e) => window.Close();
}
And Below is Xaml, You can use this xaml even if you don't implement interface, it will only need your view model to raise CloseRquested.
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPFRx"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:ViewModels="clr-namespace:WPFRx.ViewModels" x:Name="window" x:Class="WPFRx.MainWindow"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
d:DataContext="{d:DesignInstance {x:Type ViewModels:MainWindowViewModel}}">
<i:Interaction.Triggers>
<i:EventTrigger SourceObject="{Binding Mode=OneWay}" EventName="CloseRequested" >
<ei:CallMethodAction TargetObject="{Binding ElementName=window}" MethodName="Close"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid>
<Button Content="Some Content" Command="{Binding SomeCommand}" Width="100" Height="25"/>
</Grid>
You can use Messenger from MVVMLight toolkit. in your ViewModel send a message like this:
Messenger.Default.Send(new NotificationMessage("Close"));
then in your windows code behind, after InitializeComponent, register for that message like this:
Messenger.Default.Register<NotificationMessage>(this, m=>{
if(m.Notification == "Close")
{
this.Close();
}
});
you can find more about MVVMLight toolkit here:
MVVMLight toolkit on Codeplex
Notice that there is not a "no code-behind at all rule" in MVVM and you can do registering for messages in a view code-behind.
You may treat window as a service (eg. UI service) and pass itself to viewmodel via an interface, as such:
public interface IMainWindowAccess
{
void Close(bool result);
}
public class MainWindow : IMainWindowAccess
{
// (...)
public void Close(bool result)
{
DialogResult = result;
Close();
}
}
public class MainWindowViewModel
{
private IMainWindowAccess access;
public MainWindowViewModel(IMainWindowAccess access)
{
this.access = access;
}
public void DoClose()
{
access.Close(true);
}
}
This solution have most upsides of passing the view itself to viewmodel without having downside of breaking MVVM, because though physically view is passed to viewmodel, the latter still don't know about the former, it sees only some IMainWindowAccess. So for instance if we wanted to migrate this solution to other platform, it would be only a matter of implementing IMainWindowAccess properly for, say, an Activity.
I'm posting the solution here to propose a different approach than events (though it's actually very similar), because it seems a little bit simpler than events to implement (attaching/detaching etc.), but still aligns nicely with MVVM pattern.
It's simple.
You can create your own ViewModel class for Login - LoginViewModel.
You can create view var dialog = new UserView(); inside your LoginViewModel.
And you can set-up Command LoginCommand into button.
<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding LoginCommand}" />
and
<Button Name="btnCancel" IsDefault="True" Content="Login" Command="{Binding CancelCommand}" />
ViewModel class:
public class LoginViewModel
{
Window dialog;
public bool ShowLogin()
{
dialog = new UserView();
dialog.DataContext = this; // set up ViewModel into View
if (dialog.ShowDialog() == true)
{
return true;
}
return false;
}
ICommand _loginCommand
public ICommand LoginCommand
{
get
{
if (_loginCommand == null)
_loginCommand = new RelayCommand(param => this.Login());
return _loginCommand;
}
}
public void CloseLoginView()
{
if (dialog != null)
dialog.Close();
}
public void Login()
{
if(CheckLogin()==true)
{
CloseLoginView();
}
else
{
// write error message
}
}
public bool CheckLogin()
{
// ... check login code
return true;
}
}
This is a way I did it pretty simply:
YourWindow.xaml.cs
//In your constructor
public YourWindow()
{
InitializeComponent();
DataContext = new YourWindowViewModel(this);
}
YourWindowViewModel.cs
private YourWindow window;//so we can kill the window
//In your constructor
public YourWindowViewModel(YourWindow window)
{
this.window = window;
}
//to close the window
public void CloseWindow()
{
window.Close();
}
I don't see anything wrong with the answer you chose, I just thought this might be a more simple way to do it!
In MVVM WPF I usually design my View as a UserControl. And It is just a matter of how you want to display It. If you want It to be in a Window, then you could do a WindowService class:
public class WindowService
{
//...
public void Show_window(object viewModel, int height, int width, string title)
{
var window = new Window
{
Content = viewModel,
Title = title,
Height = height,
Width = width,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
Owner = Application.Current.MainWindow,
Style = (Style)Application.Current.FindResource("Window_style") //even style can be added
};
//If you own custom window style, then you can bind close/minimize/maxmize/restore buttons like this
window.CommandBindings.Add(new CommandBinding(SystemCommands.CloseWindowCommand, OnCloseWindow));
window.CommandBindings.Add(new CommandBinding(SystemCommands.MaximizeWindowCommand, OnMaximizeWindow, OnCanResizeWindow));
window.CommandBindings.Add(new CommandBinding(SystemCommands.MinimizeWindowCommand, OnMinimizeWindow, OnCanMinimizeWindow));
window.CommandBindings.Add(new CommandBinding(SystemCommands.RestoreWindowCommand, OnRestoreWindow, OnCanResizeWindow));
window.ShowDialog();
}
public void Close_window(object viewmodel)
{
//Close window
foreach (Window item in Application.Current.Windows)
{
if (item.Content == viewmodel) item.Close();
}
}
}
Using my approach is simple. Usually you want to close Window when something happens in It. So, when It does, just call Close_window method from corresponding ViewModel - the one which is DataContext of UserControl which is displayed in a Window. Look bottom example:
1.) We open Window from some Viewmodel:
public class MyViewModel // ViewModel where you open window
{
private readonly WindowService _windowservice // or inject/inherit from Base
public MyViewModel()
{
_windowservice = new WindowService();
}
private void Example_method()
{
//...Open window
_windowservice.Show_window(new WindowViewModel(),100,100,"Test window");
}
}
2.) Our Window is allready opened, now we want to close It :
public class WindowViewModel // ViewModel which is your Window content!
{
private readonly WindowService _windowservice // or inject/inherit from Base
public MyViewModel()
{
_windowservice = new WindowService();
}
private void Example_method()
{
//Close window
_windowservice.Close(this); //Pass a reference of viewmodel to method
}
}
This solution is far less elegant that other accepted answers, but for me It works. I use It widely in projects, so far no problems with It. But I'm sure that someone will come and say "That is a violation of MVVM principle".
You can close the current window just by using the following code:
Application.Current.Windows[0].Close();
System.Environment.Exit(0); in view model would work.
I'd like to know what's the best way (read most elegant) to have a single instance of a given Window per application in WPF.
I'm a newcomer to .NET and WPF and what I came up with looks pretty lame.
private static readonly Object MUTEX = new Object();
private static AboutWindow INSTANCE;
public static AboutWindow GetOrCreate() {
lock (MUTEX) {
if (INSTANCE == null) {
INSTANCE = new AboutWindow();
}
INSTANCE.Show();
return INSTANCE;
}
}
private AboutWindow() {
InitializeComponent();
}
private void AboutWindow_Closed(object sender, EventArgs e) {
// the Closed events are handy for me to update values across
// different windows.
lock (MUTEX) {
INSTANCE = null;
}
}
Thing is... this looks like utter crap. There must be some way to achieve the same goal in a much more elegant way, right?
PS: I'm often using the Closed event to change values in other open windows. For instance I have the SettingsWindow with the "Account" button. When I push that button, the AccountWindow pops up. When I close AcountWindow, I want something in the SettingsWindow to change (a label). Hence the constant creation of windows.
Besides, Close is something you always have to deal with because of the X button on the window frame...
there are probably better ways to do this, but here is a relatively simple way....
put a static bool on your window class to flag if its open or not. then, in the load() event set it to true, and on the close event set it to false. Then, in the code that opens the window, check the flag.
here is some pseudo-code to give you an idea...
public class AboutWindow
{
public static bool IsOpen {get;private set;}
onLoadEvent(....)
{
IsOpen = true;
}
onUnloadEvent(...)
{
IsOpen = false;
}
}
public void OpenAbout()
{
if ( AboutWindow.IsOpen ) return;
AboutWindow win = new AboutWindow();
win.Show();
}
If you truly need to enforce a single instance of a window, then a static instance (some flavor of what you have) with a factory creation method is certainly a viable option, much like a single DataContext instance when working with a database.
You could also write your own WindowManager class, although that seems like overkill, and will essentially be the same thing (except the Factory methods would be in a single class).
However, re-reading your post, I wonder if this is a case of missing the forest for the trees. Your mentioning of your SettingsWindow, which in turn calls AccountWindow, makes me think that you should simply be using ShowDialog(). This opens a window modally, meaning that there can be no interaction with the calling window (or any other window in your application). You simply set a property in that dialog, set the DialogResult to true when the OK button is pressed, and read that property in the parent window.
Basically, you just use the ShowDialog like this. I am leaving out a lot of the implementation details, as far as binding vs. hard-coding to controls. Those details aren't as important as just seeing how ShowDialog works.
For simplicity, assume that you have a class called MyAppOptions that, well, reflect the options of your application. I will leave off most of the implementation details of this for simplicity, but it would likely implement INotifyPropertyChanged, have methods and fields and properties, etc.
public class MyAppOptions
{
public MyAppOptions()
{
}
public Boolean MyBooleanOption
{
get;
set;
}
public String MyStringOption
{
get;
set;
}
}
Then, let's make this simple, and assume that you want to show an Options dialog when you press a button on some window. Furthermore, I will assume that there are variables that have been set with your options, which were loaded at startup.
void btnOptions_Click(object sender, RoutedEventArgs e)
{
MyAppOptions options = new MyAppOptions();
options.MyBooleanOption = mSomeBoolean;
options.MyStringOption = mSomeString;
OptionsDialog optionsDialog = new optionsDialog(options);
if (optionsDialog.ShowDialog() == true)
{
// Assume this function saves the options to storage
// and updates the application (binding) appropriately
SetAndSaveOptions(optionsDialog.AppOptions);
}
}
Now assume that the OptionsDialog is a window you've created in your project, and it has a CheckBox on it related to MyBooleanOption and a TextBox for MyStringOption. It also has an Ok button and a Cancel button. The code-behind will likely use Binding, but for now we'll hard code the values.
public class OptionsDialog : Window
{
public OptionsDialog(MyAppOptions options)
{
chkBooleanOption.IsChecked = options.SomeBooleanOption;
txtStringOption.Text = options.SomeStringOption;
btnOK.Click += new RoutedEventHandler(btnOK_Click);
btnCancel.Click += new RoutedEventHandler(btnCancel_Click);
}
public MyAppOptions AppOptions
{
get;
set;
}
void btnOK_Click(object sender, RoutedEventArgs e)
{
this.AppOptions.SomeBooleanOption = (Boolean) chkBooleanOption.IsChecked;
this.AppOptions.SomeStringOption = txtStringOption.Text;
// this is the key step - it will close the dialog and return
// true to ShowDialog
this.DialogResult = true;
}
void btnClose_Click(object sender, RoutedEventArgs e)
{
// this will close the dialog and return false to ShowDialog
// Note that pressing the X button will also return false to ShowDialog
this.DialogResult = false;
}
}
This is a pretty basic example as far as implementation details. Search online for ShowDialog for more details. The important keys to remember are:
ShowDialog opens a window modally,
meaning it is the only window in your
application that can be interacted
with.
Setting DialogResult to true
will close the dialog, which can be
checked for from the calling parent.
Setting DialogResult to false will
also close the dialog, in which case
you skip updating the values in the
calling window.
Pressing the X button
on the window automatically sets the
DialogResult to false
You can have public properties in the dialog window that can be set before doing the ShowDialog, and can get values from after the dialog disappears. It will be available while the dialog is still in scope.
The following extends on the above solution to reshow the window if it is already open. In this case it is a help window.
///<summary>
/// Show help from the resources for a particular control by contextGUID
///</summary>
///<param name="contextGUID"></param>
private void ShowApplicationHelp(string contextGUID = "1")
{
if (HelpWin != null)
{
if (HelpWin.IsOpen)
{
HelpWin.BringToFront();
return;
}
}
HelpWin = new MigratorHelpWindow();
HelpWin.Owner = Application.Current.MainWindow;
HelpWin.ResizeMode = ResizeMode.CanResizeWithGrip;
HelpWin.Icon = new Image()
{
Source =
new BitmapImage(
new Uri(
"pack://application:,,,/ResourceLibrary;component/Resources/Images/Menu/Help.png",
UriKind.RelativeOrAbsolute))
};
HelpWin.Show();
HelpWin.BringToFront();
}
This code is all in a viewmodel (MVVM) associated with the window. It is called by an ICommand hooked to a button on the window (naturally, it shows a question mark!!)
The following property is involved (in this case it is a Telerik RadWindow but it can be any window object, and you can probably also just store the window handle but using this property permits manipulation of the object more smoothly e.g. HelpWin.BringToFront() as in the above example...
...
...
private Telerik.Windows.Controls.RadWindow **HelpWin**
{
get;
set;
}
...
...
In the window itself (WPF window)
///<summary>
/// Flag to indicate the window is open - use to prevent opening this particular help window multiple times...
///</summary>
public static bool IsOpen { get; private set; }
...
...
...
private void HelpWindowLoaded(object sender, RoutedEventArgs e)
{
IsOpen = true;
}
private void HelpWindowUnloaded(object sender, RoutedEventArgs e)
{
IsOpen = false;
}
and in the view Xaml
...
...
DataContext="{Binding Path=OnlineHelpViewModelStatic,Source={StaticResource Locator}}"
RestoreMinimizedLocation="True"
**Loaded="HelpWindowLoaded" Unloaded="HelpWindowUnloaded"** >
Here's an alternative approach that doesn't require a static property to set and update in each of your window:
public static bool IsWindowInstantiated<T>() where T : Window
{
var windows = Application.Current.Windows.Cast<Window>();
var any = windows.Any(s => s is T);
return any;
}
Usage:
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
if (IsWindowInstantiated<SettingsWindow>())
return;
var window = new SettingsWindow();
window.Show();
}
How about using a Singleton?
public class MyWindow : Window {
private static MyWindow instance;
public static MyWindow Instance
{
get
{
if (instance == null)
{
instance = new MyWindow();
}
return instance;
}
}
}
Then just use
MyWindow.Instance.Show() and MyWindow.Instance.Hide()
I found this because I am trying to ensure my users do not open multiple instances of an rtsp stream window. I like Aybe's answer, it works well and is easy to understand.
I have built on it a bit as I wanted to bring the window into focus if it is open.
Here is my code:
public static void OpenWindow<T>() where T: Window
{
var windows = System.Windows.Application.Current.Windows.Cast<Window>();
var any = windows.Any(s => s is T);
if (any)
{
var win = windows.Where(s => s is T).ToList()[0];
if (win.WindowState == WindowState.Minimized)
win.WindowState = WindowState.Normal;
win.Focus();
}
else
{
var win = (Window)Activator.CreateInstance(typeof(T));
win.Show();
}
}
I am also quite new to C# and WPF so I am sure this can be improved even more.
Call it using
OpenWindow<SettingsWindow>();
public static void ShowWindow<T>() where T : Window, new()
{
var existingWindow = Application.Current.Windows.OfType<T>()
.SingleOrDefault();
if (existingWindow == null)
{
new T().Show();
return;
}
existingWindow.WindowState = WindowState.Normal;
existingWindow.Activate();
}
Usage:
ShowWindow<AboutWindow>();
When windows is created then Window.IsLoaded == true. My implementation of singleton windows is:
public partial class MySingletonWindow : Window
{
private static MySingletonWindow _instance = null;
private MySingletonWindow()
{
InitializeComponent();
}
public static MySingletonWindow Show(System.Windows.Window owner = null)
{
// On First call _instance will be null, on subsequent calls _instance will not be null but IsLoaded is false if windows was closed.
if (_instance == null || !_instance.IsLoaded)
_instance = new MySingletonWindow();
_instance.Owner = owner; // Optional owner
_instance.Show(); // Display the window
_instance.Focus(); // Bring it to front
return _instance; // Return instance if user needs it
}
}
Simply show windows using this call:
MySingletonWindow.Show(ParentWindow);
OR
MySingletonWindow.Show();