// --------------------------------------------------------------------------------------------------------------------
// <copyright file="DataWindow.cs" company="Catel development team">
// Copyright (c) 2008 - 2011 Catel development team. All rights reserved.
// </copyright>
// <summary>
// Mode of the <see cref="DataWindow" />.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using Catel.MVVM.UI;
using Catel.Windows.Controls;
using Catel.MVVM;
namespace Catel.Windows
{
/// <summary>
/// Mode of the <see cref="DataWindow"/>.
/// </summary>
public enum DataWindowMode
{
/// <summary>
/// Window contains OK and Cancel buttons.
/// </summary>
OkCancel,
/// <summary>
/// Window contains OK, Cancel and Apply buttons.
/// </summary>
OkCancelApply,
/// <summary>
/// Window contains Close button.
/// </summary>
Close,
/// <summary>
/// Window contains custom buttons.
/// </summary>
Custom
}
/// <summary>
/// Available default buttons on the data window mode.
/// </summary>
public enum DataWindowDefaultButton
{
/// <summary>
/// OK button.
/// </summary>
OK,
/// <summary>
/// Apply button.
/// </summary>
Apply,
/// <summary>
/// Close button.
/// </summary>
Close,
/// <summary>
/// No button.
/// </summary>
None
}
/// <summary>
/// <see cref="Window"/> class that implements the <see cref="InfoBarMessageControl"/> and
/// the default buttons, according to the <see cref="DataWindowMode"/>.
/// </summary>
public abstract class DataWindow
#if SILVERLIGHT
: ChildWindow
#else
: Window
#endif
{
#region Constants
/// <summary>
/// Offset of the window to the sides of the primary monitor.
/// </summary>
private const int Offset = 50;
#endregion
#region Variables
private readonly Collection<DataWindowButton> _buttons = new Collection<DataWindowButton>();
private readonly Collection<ICommand> _commands = new Collection<ICommand>();
#endregion
#region Constructor & destructor
/// <summary>
/// Initializes a new instance of the <see cref="DataWindow"/> with the <see cref="DataWindowMode.OkCancel"/> mode.
/// </summary>
protected DataWindow()
: this(DataWindowMode.OkCancel) { }
/// <summary>
/// Initializes a new instance of the <see cref="DataWindow"/> class.
/// </summary>
/// <param name="mode"><see cref="DataWindowMode"/>.</param>
protected DataWindow(DataWindowMode mode)
: this(mode, null) { }
/// <summary>
/// Initializes a new instance of the <see cref="DataWindow"/> class.
/// </summary>
/// <param name="mode"><see cref="DataWindowMode"/>.</param>
/// <param name="additionalButtons">The additional buttons.</param>
protected DataWindow(DataWindowMode mode, IEnumerable<DataWindowButton> additionalButtons)
: this(mode, additionalButtons, DataWindowDefaultButton.OK) { }
/// <summary>
/// Initializes a new instance of the <see cref="DataWindow"/> class.
/// </summary>
/// <param name="mode"><see cref="DataWindowMode"/>.</param>
/// <param name="setOwnerAndFocus">if set to <c>true</c>, set the main window as owner window and focus the window.</param>
protected DataWindow(DataWindowMode mode, bool setOwnerAndFocus)
: this(mode, null, setOwnerAndFocus) { }
/// <summary>
/// Initializes a new instance of the <see cref="DataWindow"/> class.
/// </summary>
/// <param name="mode"><see cref="DataWindowMode"/>.</param>
/// <param name="additionalButtons">The additional buttons.</param>
/// <param name="setOwnerAndFocus">if set to <c>true</c>, set the main window as owner window and focus the window.</param>
protected DataWindow(DataWindowMode mode, IEnumerable<DataWindowButton> additionalButtons, bool setOwnerAndFocus)
: this(mode, additionalButtons, DataWindowDefaultButton.OK, setOwnerAndFocus) { }
/// <summary>
/// Initializes a new instance of the <see cref="DataWindow"/> class.
/// </summary>
/// <param name="mode"><see cref="DataWindowMode"/>.</param>
/// <param name="defaultButton">The default button.</param>
protected DataWindow(DataWindowMode mode, DataWindowDefaultButton defaultButton)
: this(mode, null, defaultButton) { }
/// <summary>
/// Initializes a new instance of the <see cref="DataWindow"/> class.
/// </summary>
/// <param name="mode"><see cref="DataWindowMode"/>.</param>
/// <param name="additionalButtons">The additional buttons.</param>
/// <param name="defaultButton">The default button.</param>
protected DataWindow(DataWindowMode mode, IEnumerable<DataWindowButton> additionalButtons, DataWindowDefaultButton defaultButton)
: this(mode, additionalButtons, defaultButton, true) { }
/// <summary>
/// Initializes a new instance of the <see cref="DataWindow"/> class.
/// </summary>
/// <param name="mode"><see cref="DataWindowMode"/>.</param>
/// <param name="defaultButton">The default button.</param>
/// <param name="setOwnerAndFocus">if set to <c>true</c>, set the main window as owner window and focus the window.</param>
protected DataWindow(DataWindowMode mode, DataWindowDefaultButton defaultButton, bool setOwnerAndFocus)
: this(mode, null, defaultButton, setOwnerAndFocus) { }
/// <summary>
/// Initializes a new instance of this class with custom commands.
/// </summary>
/// <param name="mode"><see cref="DataWindowMode"/>.</param>
/// <param name="additionalButtons">The additional buttons.</param>
/// <param name="defaultButton">The default button.</param>
/// <param name="setOwnerAndFocus">if set to <c>true</c>, set the main window as owner window and focus the window.</param>
protected DataWindow(DataWindowMode mode, IEnumerable<DataWindowButton> additionalButtons, DataWindowDefaultButton defaultButton, bool setOwnerAndFocus)
{
if (DesignerProperties.GetIsInDesignMode(this)) return;
// Set window style (WPF doesn't allow styling on root elements of XAML files, too bad)
// For more info, see http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/3059c0e4-c372-4da2-b384-28f271feef05/
#if SILVERLIGHT
Style = Resources[typeof(Window)] as Style;
#else
SetResourceReference(StyleProperty, typeof(Window));
#endif
Mode = mode;
DefaultButton = defaultButton;
#if !SILVERLIGHT
SizeToContent = SizeToContent.WidthAndHeight;
ShowInTaskbar = false;
ResizeMode = ResizeMode.NoResize;
WindowStartupLocation = WindowStartupLocation.CenterOwner;
SnapsToDevicePixels = true;
MaxWidth = SystemParameters.PrimaryScreenWidth - Offset;
MaxHeight = SystemParameters.PrimaryScreenHeight - Offset;
#endif
if (additionalButtons != null)
{
foreach (var button in additionalButtons)
{
_buttons.Add(button);
}
}
CanClose = true;
Loaded += (sender, e) => Initialize();
Closing += OnDataWindowClosing;
Loaded += delegate { OnLoaded(); };
Unloaded += delegate { OnUnloaded(); };
#if !SILVERLIGHT
if (setOwnerAndFocus)
{
this.SetOwnerWindowAndFocus();
}
else
{
this.FocusFirstControl();
}
#endif
}
/// <summary>
/// Initializes a new instance of the <see cref="DataWindow"/> class.
/// </summary>
/// <param name="buttons">The buttons.</param>
protected DataWindow(IEnumerable<DataWindowButton> buttons)
: this(buttons, true) { }
/// <summary>
/// Initializes a new instance of this class with custom commands.
/// </summary>
/// <param name="buttons">The buttons.</param>
/// <param name="setOwnerAndFocus">if set to <c>true</c>, set the main window as owner window and focus the window.</param>
protected DataWindow(IEnumerable<DataWindowButton> buttons, bool setOwnerAndFocus)
: this(DataWindowMode.Custom, buttons, DataWindowDefaultButton.None, setOwnerAndFocus) { }
#endregion
#region Properties
/// <summary>
/// Gets the <see cref="DataWindowMode"/> that this window uses.
/// </summary>
protected DataWindowMode Mode { get; private set; }
/// <summary>
/// Gets or sets a value indicating whether this instance can close.
/// </summary>
/// <value><c>true</c> if this instance can close; otherwise, <c>false</c>.</value>
protected bool CanClose { get; set; }
/// <summary>
/// Gets the commands that are currently available on the data window.
/// </summary>
/// <value>The commands.</value>
protected ReadOnlyCollection<ICommand> Commands { get { return new ReadOnlyCollection<ICommand>(_commands); } }
/// <summary>
/// Gets the default button.
/// </summary>
/// <value>The default button.</value>
protected DataWindowDefaultButton DefaultButton { get; private set; }
/// <summary>
/// Gets a value indicating whether this instance is OK button available.
/// </summary>
/// <value>
/// <c>true</c> if this instance is OK button available; otherwise, <c>false</c>.
/// </value>
private bool IsOKButtonAvailable
{
get
{
if (Mode == DataWindowMode.OkCancel || Mode == DataWindowMode.OkCancelApply)
{
return true;
}
return false;
}
}
/// <summary>
/// Gets a value indicating whether this instance is cancel button available.
/// </summary>
/// <value>
/// <c>true</c> if this instance is cancel button available; otherwise, <c>false</c>.
/// </value>
private bool IsCancelButtonAvailable
{
get
{
if (Mode == DataWindowMode.OkCancel || Mode == DataWindowMode.OkCancelApply)
{
return true;
}
return false;
}
}
/// <summary>
/// Gets a value indicating whether this instance is apply button available.
/// </summary>
/// <value>
/// <c>true</c> if this instance is apply button available; otherwise, <c>false</c>.
/// </value>
private bool IsApplyButtonAvailable
{
get
{
if (Mode == DataWindowMode.OkCancelApply)
{
return true;
}
return false;
}
}
/// <summary>
/// Gets a value indicating whether this instance is close button available.
/// </summary>
/// <value>
/// <c>true</c> if this instance is close button available; otherwise, <c>false</c>.
/// </value>
private bool IsCloseButtonAvailable
{
get
{
if (Mode == DataWindowMode.Close)
{
return true;
}
return false;
}
}
/// <summary>
/// Gets or sets a value indicating whether the window was closed by a 'user'-button.
/// </summary>
/// <value><c>true</c> if closed by button; otherwise, <c>false</c>.</value>
private bool ClosedByButton { get; set; }
/// <summary>
/// Gets the internal grid. This control gives internal classes a change to add additional controls to
/// the dynamically created grid.
/// </summary>
/// <value>The internal grid.</value>
internal Grid InternalGrid { get; private set; }
#endregion
#region Commands
/// <summary>
/// Executes the OK command without a parameter.
/// </summary>
protected void ExecuteOK()
{
ExecuteOK(null);
}
/// <summary>
/// Executes the OK command with a parameter.
/// </summary>
/// <param name="parameter">The parameter.</param>
protected void ExecuteOK(object parameter)
{
if (OK_CanExecute(parameter))
{
OK_Executed(parameter);
}
}
/// <summary>
/// Determines whether the user can execute the OK command.
/// </summary>
/// <param name="parameter">The parameter.</param>
/// <returns><c>true</c> if the command can be executed; otherwise <c>false</c>.</returns>
private bool OK_CanExecute(object parameter)
{
return ValidateData();
}
/// <summary>
/// Handled when the user invokes the OK command.
/// </summary>
/// <param name="parameter">The parameter.</param>
private void OK_Executed(object parameter)
{
if (!ApplyChanges())
{
return;
}
ClosedByButton = true;
DialogResult = true;
}
/// <summary>
/// Executes the Cancel command without a parameter.
/// </summary>
protected void ExecuteCancel()
{
ExecuteCancel(null);
}
/// <summary>
/// Executes the Cancel command with a parameter.
/// </summary>
/// <param name="parameter">The parameter.</param>
protected void ExecuteCancel(object parameter)
{
if (Cancel_CanExecute(parameter))
{
Cancel_Executed(parameter);
}
}
/// <summary>
/// Determines whether the user can execute the Cancel command.
/// </summary>
/// <param name="parameter">The parameter.</param>
/// <returns><c>true</c> if the command can be executed; otherwise <c>false</c>.</returns>
private bool Cancel_CanExecute(object parameter)
{
return true;
}
/// <summary>
/// Handled when the user invokes the Cancel command.
/// </summary>
/// <param name="parameter">The parameter.</param>
private void Cancel_Executed(object parameter)
{
DiscardChanges();
ClosedByButton = true;
DialogResult = false;
}
/// <summary>
/// Executes the Apply command without a parameter.
/// </summary>
protected void ExecuteApply()
{
ExecuteApply(null);
}
/// <summary>
/// Executes the Apply command with a parameter.
/// </summary>
/// <param name="parameter">The parameter.</param>
protected void ExecuteApply(object parameter)
{
if (Apply_CanExecute(parameter))
{
Apply_Executed(parameter);
}
}
/// <summary>
/// Determines whether the user can execute the Apply command.
/// </summary>
/// <param name="parameter">The parameter.</param>
/// <returns><c>true</c> if the command can be executed; otherwise <c>false</c>.</returns>
private bool Apply_CanExecute(object parameter)
{
return ValidateData();
}
/// <summary>
/// Handled when the user invokes the Apply command.
/// </summary>
/// <param name="parameter">The parameter.</param>
private void Apply_Executed(object parameter)
{
ApplyChanges();
}
/// <summary>
/// Executes the Close command without a parameter.
/// </summary>
protected void ExecuteClose()
{
ExecuteClose(null);
}
/// <summary>
/// Executes the Close command with a parameter.
/// </summary>
/// <param name="parameter">The parameter.</param>
protected void ExecuteClose(object parameter)
{
if (Close_CanExecute(parameter))
{
Close_Executed(parameter);
}
}
/// <summary>
/// Determines whether the user can execute the Close command.
/// </summary>
/// <param name="parameter">The parameter.</param>
/// <returns><c>true</c> if the command can be executed; otherwise <c>false</c>.</returns>
private bool Close_CanExecute(object parameter)
{
return CanClose;
}
/// <summary>
/// Handled when the user invokes the Close command.
/// </summary>
/// <param name="parameter">The parameter.</param>
private void Close_Executed(object parameter)
{
Close();
}
#endregion
#region Methods
/// <summary>
/// Invoked when the content of this control has been changed. This method will add the dynamic controls automatically.
/// </summary>
/// <param name="oldContent">Old content.</param>
/// <param name="newContent">New content.</param>
protected override void OnContentChanged(object oldContent, object newContent)
{
base.OnContentChanged(oldContent, newContent);
if (!WrapControlHelper.CanBeWrapped(newContent as FrameworkElement))
{
return;
}
if (IsOKButtonAvailable)
{
var button = new DataWindowButton(Properties.Resources.CommandWindowOK, OK_Executed, OK_CanExecute) { IsDefault = true };
button.IsDefault = (DefaultButton == DataWindowDefaultButton.OK);
_buttons.Add(button);
}
if (IsCancelButtonAvailable)
{
var button = new DataWindowButton(Properties.Resources.CommandWindowCancel, Cancel_Executed, Cancel_CanExecute);
button.IsCancel = true;
_buttons.Add(button);
}
if (IsApplyButtonAvailable)
{
var button = new DataWindowButton(Properties.Resources.CommandWindowApply, Apply_Executed, Apply_CanExecute);
button.IsDefault = (DefaultButton == DataWindowDefaultButton.Apply);
_buttons.Add(button);
}
if (IsCloseButtonAvailable)
{
var button = new DataWindowButton(Properties.Resources.CommandWindowClose, Close_Executed, Close_CanExecute);
button.IsDefault = (DefaultButton == DataWindowDefaultButton.Apply);
_buttons.Add(button);
}
foreach (DataWindowButton button in _buttons)
{
_commands.Add(button.Command);
}
Grid contentGrid = WrapControlHelper.Wrap((FrameworkElement)newContent, WrapOptions.All, _buttons.ToArray(), this);
Grid internalGrid = contentGrid.FindVisualDescendant(obj => (obj is FrameworkElement) && (((FrameworkElement)obj).Name == WrapControlHelper.InternalGridName)) as Grid;
if (internalGrid != null)
{
#if SILVERLIGHT
internalGrid.Style = Application.Current.Resources["WindowGridStyle"] as Style;
#else
internalGrid.SetResourceReference(StyleProperty, "WindowGridStyle");
#endif
InternalGrid = internalGrid;
OnInternalGridChanged();
}
}
/// <summary>
/// Called when the internal grid has changed.
/// </summary>
/// <remarks>
/// This method is only invoked when the grid is set, not when the grid is cleared (which is something that should never happen).
/// </remarks>
protected virtual void OnInternalGridChanged() { }
/// <summary>
/// Called when the <see cref="DataWindow"/> is loaded.
/// </summary>
protected virtual void OnLoaded() { }
/// <summary>
/// Called when the <see cref="DataWindow"/> is unloaded.
/// </summary>
protected virtual void OnUnloaded() { }
/// <summary>
/// Handles the Closing event of the DataWindow control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="args">The <see cref="System.ComponentModel.CancelEventArgs"/> instance containing the event data.</param>
private void OnDataWindowClosing(object sender, CancelEventArgs args)
{
if (!ClosedByButton)
{
DiscardChanges();
}
AutoDisposeHelper.AutoDisposeProperties(this);
}
/// <summary>
/// Initializes the window.
/// </summary>
protected virtual void Initialize()
{ }
/// <summary>
/// Validates the data.
/// </summary>
/// <returns>True if successful, otherwise false.</returns>
protected virtual bool ValidateData()
{
return true;
}
/// <summary>
/// Applies all changes made by this window.
/// </summary>
/// <returns>True if successful, otherwise false.</returns>
protected virtual bool ApplyChanges()
{
return true;
}
/// <summary>
/// Discards all changes made by this window.
/// </summary>
protected virtual void DiscardChanges()
{ }
#endregion
}
/// <summary>
/// <see cref="Window"/> class that implements the <see cref="InfoBarMessageControl"/> and
/// the default buttons, according to the <see cref="DataWindowMode"/>. Also supports MVVM out
/// of the box by using the <see cref="ViewModelBase"/>.
/// </summary>
/// <typeparam name="TViewModel">The type of the view model.</typeparam>
public abstract class DataWindow<TViewModel> : DataWindow, IViewModelContainer where TViewModel : class, IViewModel
{
#region Variables
private readonly IViewModel _viewModel;
private bool _isFirstValidation = true;
private bool _closeInitiatedByViewModel;
#if SILVERLIGHT
private bool _isClosed;
#endif
#endregion
#region Constructor & destructor
#region Constructor without view models
/// <summary>
/// Initializes a new instance of this class with the <see cref="DataWindowMode.OkCancel"/> mode.
/// </summary>
protected DataWindow()
: this(null) { }
/// <summary>
/// Initializes a new instance of the <see cref="DataWindow"/> class.
/// </summary>
/// <param name="mode"><see cref="DataWindowMode"/>.</param>
protected DataWindow(DataWindowMode mode)
: this(null, mode) { }
/// <summary>
/// Initializes a new instance of the <see cref="DataWindow"/> class.
/// </summary>
/// <param name="mode"><see cref="DataWindowMode"/>.</param>
/// <param name="additionalButtons">The additional buttons.</param>
protected DataWindow(DataWindowMode mode, IEnumerable<DataWindowButton> additionalButtons)
: this(null, mode, additionalButtons) { }
/// <summary>
/// Initializes a new instance of the <see cref="DataWindow"/> class.
/// </summary>
/// <param name="mode"><see cref="DataWindowMode"/>.</param>
/// <param name="setOwnerAndFocus">if set to <c>true</c>, set the main window as owner window and focus the window.</param>
protected DataWindow(DataWindowMode mode, bool setOwnerAndFocus)
: this(null, mode, setOwnerAndFocus) { }
/// <summary>
/// Initializes a new instance of the <see cref="DataWindow"/> class.
/// </summary>
/// <param name="mode"><see cref="DataWindowMode"/>.</param>
/// <param name="additionalButtons">The additional buttons.</param>
/// <param name="setOwnerAndFocus">if set to <c>true</c>, set the main window as owner window and focus the window.</param>
protected DataWindow(DataWindowMode mode, IEnumerable<DataWindowButton> additionalButtons, bool setOwnerAndFocus)
: this(null, mode, additionalButtons, setOwnerAndFocus) { }
/// <summary>
/// Initializes a new instance of the <see cref="DataWindow"/> class.
/// </summary>
/// <param name="mode"><see cref="DataWindowMode"/>.</param>
/// <param name="defaultButton">The default button.</param>
protected DataWindow(DataWindowMode mode, DataWindowDefaultButton defaultButton)
: this(null, mode, defaultButton) { }
/// <summary>
/// Initializes a new instance of the <see cref="DataWindow"/> class.
/// </summary>
/// <param name="mode"><see cref="DataWindowMode"/>.</param>
/// <param name="additionalButtons">The additional buttons.</param>
/// <param name="defaultButton">The default button.</param>
protected DataWindow(DataWindowMode mode, IEnumerable<DataWindowButton> additionalButtons, DataWindowDefaultButton defaultButton)
: this(null, mode, additionalButtons, defaultButton) { }
/// <summary>
/// Initializes a new instance of the <see cref="DataWindow"/> class.
/// </summary>
/// <param name="mode"><see cref="DataWindowMode"/>.</param>
/// <param name="defaultButtons">The default buttons.</param>
/// <param name="setOwnerAndFocus">if set to <c>true</c>, set the main window as owner window and focus the window.</param>
protected DataWindow(DataWindowMode mode, DataWindowDefaultButton defaultButtons, bool setOwnerAndFocus)
: this(null, mode, null, defaultButtons, setOwnerAndFocus) { }
/// <summary>
/// Initializes a new instance of the <see cref="DataWindow"/> class.
/// </summary>
/// <param name="mode"><see cref="DataWindowMode"/>.</param>
/// <param name="additionalButtons">The additional buttons.</param>
/// <param name="defaultButton">The default button.</param>
/// <param name="setOwnerAndFocus">if set to <c>true</c>, set the main window as owner window and focus the window.</param>
protected DataWindow(DataWindowMode mode, IEnumerable<DataWindowButton> additionalButtons, DataWindowDefaultButton defaultButton, bool setOwnerAndFocus)
: this(null, mode, additionalButtons, defaultButton, setOwnerAndFocus) { }
#endregion
#region Constructors with view models
/// <summary>
/// Initializes a new instance of this class with the <see cref="DataWindowMode.OkCancel"/> mode.
/// </summary>
/// <param name="viewModel">The view model.</param>
protected DataWindow(IViewModel viewModel)
: this(viewModel, DataWindowMode.OkCancel) { }
/// <summary>
/// Initializes a new instance of the <see cref="DataWindow"/> class.
/// </summary>
/// <param name="viewModel">The view model.</param>
/// <param name="mode"><see cref="DataWindowMode"/>.</param>
protected DataWindow(IViewModel viewModel, DataWindowMode mode)
: this(viewModel, mode, null) { }
/// <summary>
/// Initializes a new instance of the <see cref="DataWindow"/> class.
/// </summary>
/// <param name="viewModel">The view model.</param>
/// <param name="mode"><see cref="DataWindowMode"/>.</param>
/// <param name="additionalButtons">The additional buttons.</param>
protected DataWindow(IViewModel viewModel, DataWindowMode mode, IEnumerable<DataWindowButton> additionalButtons)
: this(viewModel, mode, additionalButtons, DataWindowDefaultButton.OK) { }
/// <summary>
/// Initializes a new instance of the <see cref="DataWindow"/> class.
/// </summary>
/// <param name="viewModel">The view model.</param>
/// <param name="mode"><see cref="DataWindowMode"/>.</param>
/// <param name="setOwnerAndFocus">if set to <c>true</c>, set the main window as owner window and focus the window.</param>
protected DataWindow(IViewModel viewModel, DataWindowMode mode, bool setOwnerAndFocus)
: this(viewModel, mode, null, setOwnerAndFocus) { }
/// <summary>
/// Initializes a new instance of the <see cref="DataWindow"/> class.
/// </summary>
/// <param name="viewModel">The view model.</param>
/// <param name="mode"><see cref="DataWindowMode"/>.</param>
/// <param name="additionalButtons">The additional buttons.</param>
/// <param name="setOwnerAndFocus">if set to <c>true</c>, set the main window as owner window and focus the window.</param>
protected DataWindow(IViewModel viewModel, DataWindowMode mode, IEnumerable<DataWindowButton> additionalButtons, bool setOwnerAndFocus)
: this(viewModel, mode, additionalButtons, DataWindowDefaultButton.OK, setOwnerAndFocus) { }
/// <summary>
/// Initializes a new instance of the <see cref="DataWindow"/> class.
/// </summary>
/// <param name="viewModel">The view model.</param>
/// <param name="mode"><see cref="DataWindowMode"/>.</param>
/// <param name="defaultButton">The default button.</param>
protected DataWindow(IViewModel viewModel, DataWindowMode mode, DataWindowDefaultButton defaultButton)
: this(viewModel, mode, null, defaultButton) { }
/// <summary>
/// Initializes a new instance of the <see cref="DataWindow"/> class.
/// </summary>
/// <param name="viewModel">The view model.</param>
/// <param name="additionalButtons">The additional buttons.</param>
/// <param name="mode"><see cref="DataWindowMode"/>.</param>
/// <param name="defaultButton">The default button.</param>
protected DataWindow(IViewModel viewModel, DataWindowMode mode, IEnumerable<DataWindowButton> additionalButtons, DataWindowDefaultButton defaultButton)
: this(viewModel, mode, additionalButtons, defaultButton, true) { }
/// <summary>
/// Initializes a new instance of the <see cref="DataWindow"/> class.
/// </summary>
/// <param name="viewModel">The view model.</param>
/// <param name="mode"><see cref="DataWindowMode"/>.</param>
/// <param name="defaultButtons">The default buttons.</param>
/// <param name="setOwnerAndFocus">if set to <c>true</c>, set the main window as owner window and focus the window.</param>
protected DataWindow(IViewModel viewModel, DataWindowMode mode, DataWindowDefaultButton defaultButtons, bool setOwnerAndFocus)
: this(viewModel, mode, null, defaultButtons, setOwnerAndFocus) { }
/// <summary>
/// Initializes a new instance of the <see cref="DataWindow"/> class.
/// </summary>
/// <param name="viewModel">The view model.</param>
/// <param name="mode"><see cref="DataWindowMode"/>.</param>
/// <param name="additionalButtons">The additional buttons.</param>
/// <param name="defaultButton">The default button.</param>
/// <param name="setOwnerAndFocus">if set to <c>true</c>, set the main window as owner window and focus the window.</param>
protected DataWindow(IViewModel viewModel, DataWindowMode mode, IEnumerable<DataWindowButton> additionalButtons,
DataWindowDefaultButton defaultButton, bool setOwnerAndFocus)
: base(mode, additionalButtons, defaultButton, setOwnerAndFocus)
{
_viewModel = viewModel ?? (IViewModel)Activator.CreateInstance(typeof(TViewModel), true);
if (_viewModel == null)
{
throw new InvalidViewModelException();
}
DataContext = _viewModel;
_viewModel.PropertyChanged += OnViewModelPropertyChanged;
Loaded += DataWindow_Loaded;
if (ViewModelChanged != null)
{
ViewModelChanged(this, EventArgs.Empty);
}
SetBinding(TitleProperty, new Binding("Title"));
}
#endregion
#endregion
#region Properties
/// <summary>
/// Gets the view model that is contained by the container.
/// </summary>
/// <value>The view model.</value>
IViewModel IViewModelContainer.ViewModel
{
get { return ViewModel; }
}
/// <summary>
/// Gets the view model.
/// </summary>
/// <value>The view model.</value>
public TViewModel ViewModel
{
get { return (TViewModel)_viewModel; }
}
#endregion
#region Events
#if !SILVERLIGHT
/// <summary>
/// Occurs when a property on the container has changed.
/// </summary>
/// <remarks>
/// This event makes it possible to externally subscribe to property changes of a <see cref="DependencyObject"/>
/// (mostly the container of a view model) because the .NET Framework does not allows us to.
/// </remarks>
public event EventHandler<PropertyChangedEventArgs> PropertyChanged;
#endif
/// <summary>
/// Occurs when the <see cref="ViewModel"/> property has changed.
/// </summary>
public event EventHandler<EventArgs> ViewModelChanged;
#endregion
#region Methods
/// <summary>
/// Called when the <see cref="DataWindow"/> is loaded.
/// </summary>
protected override void OnLoaded()
{
ControlToViewModelMappingHelper.InitializeControlToViewModelMappings(this);
}
/// <summary>
/// Called when the <see cref="DataWindow"/> is unloaded.
/// </summary>
protected override void OnUnloaded()
{
ControlToViewModelMappingHelper.UninitializeControlToViewModelMappings(this);
}
/// <summary>
/// Called when the internal grid has changed.
/// </summary>
/// <remarks>
/// This method is only invoked when the grid is set, not when the grid is cleared (which is something that should never happen).
/// </remarks>
protected override void OnInternalGridChanged()
{
if (InternalGrid != null)
{
WarningAndErrorValidator validator = new WarningAndErrorValidator();
InternalGrid.Children.Add(validator);
//validator.SetBinding(WarningAndErrorValidator.SourceProperty, "DataContext");
validator.Source = DataContext;
}
}
/// <summary>
/// Called when a property on the current view model has changed.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="System.ComponentModel.PropertyChangedEventArgs"/> instance containing the event data.</param>
private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
{
foreach (ICommand command in Commands)
{
if (command is ICatelCommand)
{
((ICatelCommand)command).RaiseCanExecuteChanged();
}
}
}
/// <summary>
/// Handles the Loaded event of the DataWindow control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
private void DataWindow_Loaded(object sender, RoutedEventArgs e)
{
_viewModel.Initialize();
_viewModel.Closed += ViewModel_Closed;
// Revalidate since the control already initialized the view model before the control
// was visible, therefore the WPF engine does not show warnings and errors
_viewModel.Validate(true, false);
}
/// <summary>
/// Handles the Closed event of the ViewModel control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
private void ViewModel_Closed(object sender, EventArgs e)
{
_viewModel.PropertyChanged -= OnViewModelPropertyChanged;
_viewModel.Closed -= ViewModel_Closed;
_closeInitiatedByViewModel = true;
#if SILVERLIGHT
// This code is implemented due to a bug in the ChildWindow of silverlight, see:
// http://silverlight.codeplex.com/workitem/7935
// Only handle this once
if (_isClosed)
{
return;
}
#endif
Close();
}
/// <summary>
/// Validates the data.
/// </summary>
/// <returns>True if successful, otherwise false.</returns>
protected override bool ValidateData()
{
if (_viewModel.IsInitialized)
{
_viewModel.Validate(_isFirstValidation, false);
_isFirstValidation = false;
}
return !_viewModel.HasErrors;
}
/// <summary>
/// Discards all changes made by this window.
/// </summary>
protected override void DiscardChanges()
{
_viewModel.Cancel();
}
/// <summary>
/// Applies all changes made by this window.
/// </summary>
/// <returns>True if successful, otherwise false.</returns>
protected override bool ApplyChanges()
{
return _viewModel.Save();
}
#if !SILVERLIGHT
/// <summary>
/// Invoked whenever the effective value of any dependency property on this <see cref="T:System.Windows.FrameworkElement"/> has been updated. The specific dependency property that changed is reported in the arguments parameter. Overrides <see cref="M:System.Windows.DependencyObject.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs)"/>.
/// </summary>
/// <param name="e">The event data that describes the property that changed, as well as old and new values.</param>
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(e.Property.Name));
}
base.OnPropertyChanged(e);
}
#endif
/// <summary>
/// Raises the <see cref="E:System.Windows.Window.Closed"/> event.
/// </summary>
/// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
protected override void OnClosed(EventArgs e)
{
#if SILVERLIGHT
// This code is implemented due to a bug in the ChildWindow of silverlight, see:
// http://silverlight.codeplex.com/workitem/7935
// Only handle this once
if (_isClosed)
{
return;
}
_isClosed = true;
#endif
base.OnClosed(e);
if (!_closeInitiatedByViewModel)
{
_viewModel.Close();
}
if (_viewModel is IDisposable)
{
((IDisposable)_viewModel).Dispose();
}
}
#endregion
}
}