Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 29 additions & 3 deletions src/Prism.Core/Dialogs/IDialogAware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,54 @@ namespace Prism.Dialogs;
/// <summary>
/// Provides a way for objects involved in Dialogs to be notified of Dialog activities.
/// </summary>
public interface IDialogAware
/// <remarks>
/// <para>
/// <see cref="IDialogAware"/> is the primary interface that Dialog ViewModels should implement.
/// It allows ViewModels to participate in the dialog lifecycle and control when dialogs can be closed.
/// </para>
/// <para>
/// Dialog services will automatically call the methods on this interface at appropriate points
/// in the dialog lifecycle (when opened, when closed, before closing to check if closing is allowed).
/// </para>
/// </remarks>
public interface IDialogAware
{
/// <summary>
/// Evaluates whether the Dialog is in a state that would allow the Dialog to Close
/// </summary>
/// <returns><c>true</c> if the Dialog can close</returns>
/// <returns><see langword="true"/> if the Dialog can close; otherwise, <see langword="false"/></returns>
/// <remarks>
/// This method is called before the dialog is closed. If it returns <see langword="false"/>, the close operation is cancelled.
/// Use this to prevent users from closing a dialog while operations are in progress or when validation fails.
/// </remarks>
bool CanCloseDialog();

/// <summary>
/// Provides a callback to clean up resources or finalize tasks when the Dialog has been closed
/// </summary>
/// <remarks>
/// This method is called after the dialog window has closed. Use it to clean up any resources
/// or perform final actions related to the dialog.
/// </remarks>
void OnDialogClosed();

/// <summary>
/// Initializes the state of the Dialog with provided DialogParameters
/// </summary>
/// <param name="parameters"></param>
/// <param name="parameters">Dialog parameters passed when showing the dialog</param>
/// <remarks>
/// This method is called after the dialog is displayed but before it is shown to the user.
/// Use it to initialize the dialog's state based on the provided parameters.
/// </remarks>
void OnDialogOpened(IDialogParameters parameters);

/// <summary>
/// The <see cref="DialogCloseListener"/> will be set by the <see cref="IDialogService"/> and can be called to
/// invoke the close of the Dialog.
/// </summary>
/// <remarks>
/// This listener is set by the dialog service and allows the ViewModel to request that the dialog be closed,
/// optionally with a result that indicates the outcome of the dialog (OK, Cancel, etc.).
/// </remarks>
DialogCloseListener RequestClose { get; }
}
20 changes: 20 additions & 0 deletions src/Prism.Core/IActiveAware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,37 @@ namespace Prism
/// Interface that defines if the object instance is active
/// and notifies when the activity changes.
/// </summary>
/// <remarks>
/// <para>
/// The <see cref="IActiveAware"/> interface is used to track the active state of objects within Prism,
/// particularly in regions. When a view or module becomes active or inactive, implementations of this interface
/// can respond to the state change through the <see cref="IsActiveChanged"/> event.
/// </para>
/// <para>
/// This is commonly used for ViewModels that need to load or unload data based on whether their corresponding
/// view is currently being displayed in a region.
/// </para>
/// </remarks>
public interface IActiveAware
{
/// <summary>
/// Gets or sets a value indicating whether the object is active.
/// </summary>
/// <value><see langword="true" /> if the object is active; otherwise <see langword="false" />.</value>
/// <remarks>
/// When this property is set to <see langword="true" />, the object is considered active and should perform
/// any necessary initialization or load operations. When set to <see langword="false" />, the object should
/// perform cleanup operations.
/// </remarks>
bool IsActive { get; set; }

/// <summary>
/// Notifies that the value for <see cref="IsActive"/> property has changed.
/// </summary>
/// <remarks>
/// This event is raised whenever the <see cref="IsActive"/> property value changes, allowing listeners
/// to respond to activation and deactivation events.
/// </remarks>
event EventHandler IsActiveChanged;
}
}
26 changes: 26 additions & 0 deletions src/Prism.Core/Mvvm/BindableBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ namespace Prism.Mvvm
/// <summary>
/// Implementation of <see cref="INotifyPropertyChanged"/> to simplify models.
/// </summary>
/// <remarks>
/// <para>
/// <see cref="BindableBase"/> is a base class designed for ViewModel classes in MVVM applications.
/// It provides a convenient implementation of <see cref="INotifyPropertyChanged"/> that reduces boilerplate code
/// when creating ViewModels that need to notify views of property changes.
/// </para>
/// <para>
/// The class provides helper methods like <see cref="SetProperty{T}(ref T, T, string)"/> that automatically
/// handle equality comparison and notification, ensuring that listeners are only notified when values actually change.
/// </para>
/// </remarks>
public abstract class BindableBase : INotifyPropertyChanged
{
/// <summary>
Expand All @@ -26,6 +37,10 @@ public abstract class BindableBase : INotifyPropertyChanged
/// support CallerMemberName.</param>
/// <returns>True if the value was changed, false if the existing value matched the
/// desired value.</returns>
/// <remarks>
/// This method uses <see cref="EqualityComparer{T}.Default"/> to compare the current storage value with the new value.
/// Only if they differ will the property be updated and listeners notified.
/// </remarks>
protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string? propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(storage, value)) return false;
Expand All @@ -49,6 +64,10 @@ protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName]
/// <param name="onChanged">Action that is called after the property value has been changed.</param>
/// <returns>True if the value was changed, false if the existing value matched the
/// desired value.</returns>
/// <remarks>
/// This method is useful when you need to perform additional logic when a property changes,
/// such as recalculating a derived property or triggering a command.
/// </remarks>
protected virtual bool SetProperty<T>(ref T storage, T value, Action? onChanged,
[CallerMemberName] string? propertyName = null)
{
Expand All @@ -67,6 +86,10 @@ protected virtual bool SetProperty<T>(ref T storage, T value, Action? onChanged,
/// <param name="propertyName">Name of the property used to notify listeners. This
/// value is optional and can be provided automatically when invoked from compilers
/// that support <see cref="CallerMemberNameAttribute"/>.</param>
/// <remarks>
/// This method is typically called automatically by <see cref="SetProperty{T}(ref T, T, string)"/>,
/// but can also be called directly to notify of changes to computed properties.
/// </remarks>
protected void RaisePropertyChanged([CallerMemberName] string? propertyName = null)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
Expand All @@ -76,6 +99,9 @@ protected void RaisePropertyChanged([CallerMemberName] string? propertyName = nu
/// Raises this object's PropertyChanged event.
/// </summary>
/// <param name="args">The PropertyChangedEventArgs</param>
/// <remarks>
/// Override this method to intercept property change notifications before they are broadcast to subscribers.
/// </remarks>
protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
{
PropertyChanged?.Invoke(this, args);
Expand Down
28 changes: 26 additions & 2 deletions src/Prism.Core/Mvvm/ErrorsContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ namespace Prism.Mvvm
/// <summary>
/// Manages validation errors for an object, notifying when the error state changes.
/// </summary>
/// <remarks>
/// <para>
/// <see cref="ErrorsContainer{T}"/> is a utility class for ViewModels that need to manage and track validation errors.
/// It implements the <see cref="INotifyDataErrorInfo"/> pattern, maintaining a collection of errors per property
/// and notifying when the error state changes.
/// </para>
/// <para>
/// This class is typically used with data validation frameworks to maintain error state and notify the UI when validation fails.
/// </para>
/// </remarks>
/// <typeparam name="T">The type of the error object.</typeparam>
public class ErrorsContainer<T>
{
Expand All @@ -24,7 +34,8 @@ public class ErrorsContainer<T>
/// <summary>
/// Initializes a new instance of the <see cref="ErrorsContainer{T}"/> class.
/// </summary>
/// <param name="raiseErrorsChanged">The action that is invoked when errors are added for an object.</param>
/// <param name="raiseErrorsChanged">The action that is invoked when errors are added or removed for a property.</param>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="raiseErrorsChanged"/> is <see langword="null"/>.</exception>
public ErrorsContainer(Action<string> raiseErrorsChanged)
{
if (raiseErrorsChanged == null)
Expand All @@ -39,6 +50,10 @@ public ErrorsContainer(Action<string> raiseErrorsChanged)
/// <summary>
/// Gets a value indicating whether the object has validation errors.
/// </summary>
/// <value><see langword="true"/> if there are any errors; otherwise, <see langword="false"/>.</value>
/// <remarks>
/// This property returns <see langword="true"/> only if there is at least one error for any property.
/// </remarks>
public bool HasErrors
{
get
Expand All @@ -51,13 +66,19 @@ public bool HasErrors
/// Returns all the errors in the container.
/// </summary>
/// <returns>The dictionary of errors per property.</returns>
/// <remarks>
/// The returned dictionary maps property names to lists of errors for those properties.
/// </remarks>
public Dictionary<string, List<T>> GetErrors() => validationResults;

/// <summary>
/// Gets the validation errors for a specified property.
/// </summary>
/// <param name="propertyName">The name of the property.</param>
/// <param name="propertyName">The name of the property. If <see langword="null"/> or empty, returns errors for the object itself.</param>
/// <returns>The validation errors of type <typeparamref name="T"/> for the property.</returns>
/// <remarks>
/// If the property has no errors, an empty enumerable is returned.
/// </remarks>
public IEnumerable<T> GetErrors(string? propertyName)
{
var localPropertyName = propertyName ?? string.Empty;
Expand All @@ -74,6 +95,9 @@ public IEnumerable<T> GetErrors(string? propertyName)
/// <summary>
/// Clears all errors.
/// </summary>
/// <remarks>
/// This removes all errors from all properties and raises the ErrorsChanged event for each property that had errors.
/// </remarks>
public void ClearErrors()
{
foreach (var key in this.validationResults.Keys.ToArray())
Expand Down
39 changes: 33 additions & 6 deletions src/Wpf/Prism.Wpf/Dialogs/DialogService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,14 @@ namespace Prism.Dialogs
/// Implements <see cref="IDialogService"/> to show modal and non-modal dialogs.
/// </summary>
/// <remarks>
/// The dialog's ViewModel must implement IDialogAware.
/// <para>
/// The <see cref="DialogService"/> is responsible for managing the lifecycle of dialog windows in WPF applications.
/// It handles dialog creation, initialization, showing/hiding, and event coordination between the view and view model.
/// </para>
/// <para>
/// The dialog's ViewModel must implement <see cref="IDialogAware"/> to participate in the dialog lifecycle.
/// Dialog views must also implement <see cref="IDialogWindow"/> or inherit from it.
/// </para>
/// </remarks>
public class DialogService : IDialogService
{
Expand All @@ -16,7 +23,7 @@ public class DialogService : IDialogService
/// <summary>
/// Initializes a new instance of the <see cref="DialogService"/> class.
/// </summary>
/// <param name="containerExtension">The <see cref="IContainerExtension" /></param>
/// <param name="containerExtension">The <see cref="IContainerExtension" /> used to resolve dialog views and windows.</param>
public DialogService(IContainerExtension containerExtension)
{
_containerExtension = containerExtension;
Expand All @@ -25,9 +32,13 @@ public DialogService(IContainerExtension containerExtension)
/// <summary>
/// Shows a modal dialog.
/// </summary>
/// <param name="name">The name of the dialog to show.</param>
/// <param name="parameters">The parameters to pass to the dialog.</param>
/// <param name="name">The name of the dialog to show. This name must be registered in the container.</param>
/// <param name="parameters">The parameters to pass to the dialog's ViewModel.</param>
/// <param name="callback">The action to perform when the dialog is closed.</param>
/// <remarks>
/// The dialog can be shown as either modal or non-modal based on the <see cref="KnownDialogParameters.ShowNonModal"/> parameter.
/// By default, dialogs are shown as modal (blocking the parent window).
/// </remarks>
public void ShowDialog(string name, IDialogParameters parameters, DialogCallback callback)
{
parameters ??= new DialogParameters();
Expand All @@ -45,7 +56,10 @@ public void ShowDialog(string name, IDialogParameters parameters, DialogCallback
/// Shows the dialog window.
/// </summary>
/// <param name="dialogWindow">The dialog window to show.</param>
/// <param name="isModal">If true; dialog is shown as a modal</param>
/// <param name="isModal">If <see langword="true"/>, the dialog is shown as a modal window that blocks interaction with the parent; otherwise, it is shown as a modeless window.</param>
/// <remarks>
/// This method can be overridden to customize how the dialog window is displayed.
/// </remarks>
protected virtual void ShowDialogWindow(IDialogWindow dialogWindow, bool isModal)
{
if (isModal)
Expand All @@ -57,8 +71,11 @@ protected virtual void ShowDialogWindow(IDialogWindow dialogWindow, bool isModal
/// <summary>
/// Create a new <see cref="IDialogWindow"/>.
/// </summary>
/// <param name="name">The name of the hosting window registered with the IContainerRegistry.</param>
/// <param name="name">The name of the hosting window registered with the IContainerRegistry. If <see langword="null"/> or empty, the default dialog window is used.</param>
/// <returns>The created <see cref="IDialogWindow"/>.</returns>
/// <remarks>
/// This method can be overridden to customize dialog window creation.
/// </remarks>
protected virtual IDialogWindow CreateDialogWindow(string name)
{
if (string.IsNullOrWhiteSpace(name))
Expand All @@ -73,6 +90,9 @@ protected virtual IDialogWindow CreateDialogWindow(string name)
/// <param name="dialogName">The name of the dialog to show.</param>
/// <param name="window">The hosting window.</param>
/// <param name="parameters">The parameters to pass to the dialog.</param>
/// <remarks>
/// This method resolves the dialog content from the container, auto-wires the view model, and initializes the dialog.
/// </remarks>
protected virtual void ConfigureDialogWindowContent(string dialogName, IDialogWindow window, IDialogParameters parameters)
{
var content = _containerExtension.Resolve<object>(dialogName);
Expand All @@ -94,6 +114,9 @@ protected virtual void ConfigureDialogWindowContent(string dialogName, IDialogWi
/// </summary>
/// <param name="dialogWindow">The hosting window.</param>
/// <param name="callback">The action to perform when the dialog is closed.</param>
/// <remarks>
/// This method sets up event handlers for the dialog lifecycle including Loaded, Closing, and Closed events.
/// </remarks>
protected virtual void ConfigureDialogWindowEvents(IDialogWindow dialogWindow, DialogCallback callback)
{
Action<IDialogResult> requestCloseHandler = (r) =>
Expand Down Expand Up @@ -143,12 +166,16 @@ protected virtual void ConfigureDialogWindowEvents(IDialogWindow dialogWindow, D
/// <param name="window">The hosting window.</param>
/// <param name="dialogContent">The dialog to show.</param>
/// <param name="viewModel">The dialog's ViewModel.</param>
/// <remarks>
/// This method sets up the window style, title, and content based on the dialog view and view model configuration.
/// </remarks>
protected virtual void ConfigureDialogWindowProperties(IDialogWindow window, FrameworkElement dialogContent, IDialogAware viewModel)
{
var windowStyle = Dialog.GetWindowStyle(dialogContent);
if (windowStyle != null)
window.Style = windowStyle;


window.Content = dialogContent;
window.DataContext = viewModel; //we want the host window and the dialog to share the same data context

Expand Down