Add a load of missing XML documentation

This commit is contained in:
Antony Male 2014-05-26 18:48:44 +01:00
parent a914d58668
commit 7203b02cdf
31 changed files with 527 additions and 44 deletions

View File

@ -58,15 +58,31 @@ namespace Stylet
/// </summary>
private bool isNotifying = true;
/// <summary>
/// Create a new empty BindableCollection
/// </summary>
public BindableCollection() : base() { }
/// <summary>
/// Create a new BindableCollection with the given members
/// </summary>
/// <param name="collection">The collection from which the elements are copied</param>
public BindableCollection(IEnumerable<T> collection) : base(collection) { }
/// <summary>
/// Raises the System.Collections.ObjectModel.ObservableCollection{T}.PropertyChanged event with the provided arguments.
/// </summary>
/// <param name="e">Arguments of the event being raised.</param>
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (this.isNotifying)
this.PropertyChangedDispatcher(() => base.OnPropertyChanged(e));
}
/// <summary>
/// Raises the System.Collections.ObjectModel.ObservableCollection{T}.CollectionChanged event with the provided arguments.
/// </summary>
/// <param name="e">Arguments of the event being raised.</param>
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (this.isNotifying)

View File

@ -87,6 +87,9 @@ namespace Stylet
IoC.Get<IWindowManager>().ShowWindow(IoC.Get<TRootViewModel>());
}
/// <summary>
/// Add any application resources to the application. Override to add your own, or to avoid Stylet's default resources from being added
/// </summary>
protected virtual void ConfigureResources()
{
if (this.Application == null)

View File

@ -10,6 +10,9 @@ namespace Stylet
{
public partial class Conductor<T>
{
/// <summary>
/// Contains specific Conductor{T} collection types
/// </summary>
public partial class Collection
{
/// <summary>
@ -18,11 +21,18 @@ namespace Stylet
public class AllActive : ConductorBase<T>
{
private BindableCollection<T> items = new BindableCollection<T>();
/// <summary>
/// All items associated with this conductor
/// </summary>
public IObservableCollection<T> Items
{
get { return this.items; }
}
/// <summary>
/// Creates a new Conductor{T}.Collection.AllActive
/// </summary>
public AllActive()
{
this.items.CollectionChanged += (o, e) =>
@ -49,6 +59,10 @@ namespace Stylet
};
}
/// <summary>
/// Active all items in a given collection if appropriate, and set the parent of all items to this
/// </summary>
/// <param name="items">Items to manipulate</param>
protected virtual void ActivateAndSetParent(IEnumerable items)
{
this.SetParent(items);
@ -61,6 +75,9 @@ namespace Stylet
}
}
/// <summary>
/// Activates all items whenever this conductor is activated
/// </summary>
protected override void OnActivate()
{
foreach (var item in this.items.OfType<IActivate>())
@ -69,6 +86,9 @@ namespace Stylet
}
}
/// <summary>
/// Deactivates all items whenever this conductor is deactivated
/// </summary>
protected override void OnDeactivate()
{
foreach (var item in this.items.OfType<IDeactivate>())
@ -77,6 +97,9 @@ namespace Stylet
}
}
/// <summary>
/// Close, and clean up, all items when this conductor is closed
/// </summary>
protected override void OnClose()
{
// We've already been deactivated by this point
@ -142,6 +165,10 @@ namespace Stylet
return this.items;
}
/// <summary>
/// Ensure an item is ready to be activated, by adding it to the items collection, as well as setting it up
/// </summary>
/// <param name="newItem">Item to ensure</param>
protected override void EnsureItem(T newItem)
{
if (!this.items.Contains(newItem))

View File

@ -12,6 +12,10 @@ namespace Stylet
/// <typeparam name="T">Type of item to be conducted</typeparam>
public abstract class ConductorBase<T> : Screen, IConductor<T>, IParent<T>, IChildDelegate where T : class
{
/// <summary>
/// Retrieves the Item or Items associated with this Conductor
/// </summary>
/// <returns>Item or Items associated with this Conductor</returns>
public abstract IEnumerable<T> GetChildren();
/// <summary>
@ -27,7 +31,7 @@ namespace Stylet
public abstract void DeactivateItem(T item);
/// <summary>
/// Deactivate the given item
/// Close the given item
/// </summary>
/// <param name="item">Item to deactivate</param>
public abstract void CloseItem(T item);

View File

@ -97,6 +97,9 @@ namespace Stylet
return this.CanAllItemsCloseAsync(this.history.Concat(new[] { this.ActiveItem }));
}
/// <summary>
/// Ensures that all children are closed when this conductor is closed
/// </summary>
protected override void OnClose()
{
// We've already been deactivated by this point

View File

@ -18,11 +18,18 @@ namespace Stylet
public class OneActive : ConductorBaseWithActiveItem<T>
{
private BindableCollection<T> items = new BindableCollection<T>();
/// <summary>
/// Items owned by this Conductor, one of which is active
/// </summary>
public IObservableCollection<T> Items
{
get { return this.items; }
}
/// <summary>
/// Create a new Conductor{T}.Collections.OneActive instance
/// </summary>
public OneActive()
{
this.items.CollectionChanged += (o, e) =>
@ -52,6 +59,9 @@ namespace Stylet
};
}
/// <summary>
/// Called when the ActiveItem may have been removed from the Items collection. If it has, will change the ActiveItem to something sensible
/// </summary>
protected virtual void ActiveItemMayHaveBeenRemovedFromItems()
{
if (this.items.Contains(this.ActiveItem))
@ -60,6 +70,10 @@ namespace Stylet
this.ChangeActiveItem(this.items.FirstOrDefault(), true);
}
/// <summary>
/// Return all items associated with this conductor
/// </summary>
/// <returns></returns>
public override IEnumerable<T> GetChildren()
{
return this.items;
@ -102,6 +116,10 @@ namespace Stylet
}
}
/// <summary>
/// Close the given item (if and when possible, depending on IGuardClose.CanCloseAsync). This will deactive if it is the active item
/// </summary>
/// <param name="item">Item to close</param>
public override async void CloseItem(T item)
{
if (item == null || !await this.CanCloseItem(item))
@ -120,6 +138,10 @@ namespace Stylet
this.items.Remove(item);
}
/// <summary>
/// Given a list of items, and and item which is going to be removed, choose a new item to be the next ActiveItem
/// </summary>
/// <returns>The next item to activate, or default(T) if no such item exists</returns>
protected virtual T DetermineNextItemToActivate(T itemToRemove)
{
if (itemToRemove == null)
@ -153,6 +175,9 @@ namespace Stylet
return this.CanAllItemsCloseAsync(this.items);
}
/// <summary>
/// Ensures that all items are closed when this conductor is closed
/// </summary>
protected override void OnClose()
{
// We've already been deactivated by this point
@ -161,6 +186,10 @@ namespace Stylet
this.items.Clear();
}
/// <summary>
/// Ensure an item is ready to be activated
/// </summary>
/// <param name="newItem"></param>
protected override void EnsureItem(T newItem)
{
if (!this.items.Contains(newItem))

View File

@ -21,6 +21,10 @@ namespace Stylet
/// <typeparam name="TMessageType">Message type to handle. Can be a base class of the messsage type(s) to handle</typeparam>
public interface IHandle<TMessageType> : IHandle
{
/// <summary>
/// Called whenever a message of type TMessageType is posted
/// </summary>
/// <param name="message">Message which was posted</param>
void Handle(TMessageType message);
}
@ -49,11 +53,18 @@ namespace Stylet
void PublishWithDispatcher(object message, Action<Action> dispatcher);
}
/// <summary>
/// Default implementation of IEventAggregator
/// </summary>
public class EventAggregator : IEventAggregator
{
private readonly List<Handler> handlers = new List<Handler>();
private readonly object handlersLock = new object();
/// <summary>
/// Register an instance as wanting to receive events. Implement IHandle{T} for each event type you want to receive.
/// </summary>
/// <param name="handler">Instance that will be registered with the EventAggregator</param>
public void Subscribe(IHandle handler)
{
lock (this.handlersLock)
@ -66,6 +77,10 @@ namespace Stylet
}
}
/// <summary>
/// Unregister as wanting to receive events. The instance will no longer receive events after this is called.
/// </summary>
/// <param name="handler">Instance to unregister</param>
public void Unsubscribe(IHandle handler)
{
lock (this.handlersLock)
@ -76,6 +91,11 @@ namespace Stylet
}
}
/// <summary>
/// Publish an event to all subscribers, using the specified dispatcher
/// </summary>
/// <param name="message">Event to publish</param>
/// <param name="dispatcher">Dispatcher to use to call each subscriber's handle method(s)</param>
public void PublishWithDispatcher(object message, Action<Action> dispatcher)
{
lock (this.handlersLock)

View File

@ -58,6 +58,9 @@ namespace Stylet
}
}
/// <summary>
/// Static class providing methods to easily run an action on the UI thread in various ways, and some other things
/// </summary>
public static class Execute
{
/// <summary>
@ -167,6 +170,9 @@ namespace Stylet
}
}
/// <summary>
/// Determing if we're currently running in design mode
/// </summary>
public static bool InDesignMode
{
get

View File

@ -25,6 +25,9 @@ namespace Stylet
/// <typeparam name="T">Type of the active item</typeparam>
public interface IHaveActiveItem<T>
{
/// <summary>
/// Only item which is currently active. This normally corresponds to the item being displayed
/// </summary>
T ActiveItem { get; set; }
}
@ -33,6 +36,11 @@ namespace Stylet
/// </summary>
public interface IChildDelegate
{
/// <summary>
/// Called by the child to request that is be closed
/// </summary>
/// <param name="item">Child object, which is passed by the child itself</param>
/// <param name="dialogResult">DialogResult to use to close, if any</param>
void CloseItem(object item, bool? dialogResult = null);
}

View File

@ -114,15 +114,23 @@ namespace Stylet
{
}
/// <summary>
/// EventArgs associated with the IActivate.Activated event
/// </summary>
public class ActivationEventArgs : EventArgs
{
}
/// <summary>
/// EventArgs associated with the IDeactivate.Deactivated event
/// </summary>
public class DeactivationEventArgs : EventArgs
{
}
/// <summary>
/// EventArgs associated with the IClose.Closed event
/// </summary>
public class CloseEventArgs : EventArgs
{
}

View File

@ -32,31 +32,57 @@ namespace Stylet
set { SetAndNotify(ref this._value, value); }
}
/// <summary>
/// Create a new LabelledValue, without setting Label or Value
/// </summary>
public LabelledValue()
{
}
/// <summary>
/// Create a new LabelledValue, with the given label and value
/// </summary>
/// <param name="label">Label to use. This value is displayed in your view</param>
/// <param name="value">Value to use. This is used by your ViewModel</param>
public LabelledValue(string label, T value)
{
this._label = label;
this._value = value;
}
/// <summary>
/// Indicates whether the current object is equal to another object of the same type.
/// </summary>
/// <param name="other">An object to compare with this object</param>
/// <returns>true if the current object is equal to the other parameter; otherwise, false.</returns>
public bool Equals(LabelledValue<T> other)
{
return other == null ? false : this.Label == other.Label && EqualityComparer<T>.Default.Equals(this.Value, other.Value);
}
/// <summary>
/// Indicates whether the current object is equal to another object of any type
/// </summary>
/// <param name="obj">An object to compare with this object</param>
/// <returns>true if the current object is of the same type as the other object, and equal to the other parameter; otherwise, false.</returns>
public override bool Equals(object obj)
{
return this.Equals(obj as LabelledValue<T>);
}
/// <summary>
/// Returns a hash code for the this object
/// </summary>
/// <returns>A hash code for this object.</returns>
public override int GetHashCode()
{
return new { this.Label, this.Value }.GetHashCode();
}
/// <summary>
/// Return the Label associated with this object
/// </summary>
/// <returns>The Label associated with this object</returns>
public override string ToString()
{
return this.Label;

View File

@ -17,7 +17,7 @@ namespace Stylet
/// <summary>
/// Create a new LambdaComparer{T}
/// </summary>
/// <param name="comparer">Comparer, which takes two {T} instances and returns true if they are equal</param>
/// <param name="comparer">Comparer, which takes two T instances and returns true if they are equal</param>
public LambdaComparer(Func<T, T, bool> comparer)
{
if (comparer == null)
@ -25,11 +25,22 @@ namespace Stylet
this.comparer = comparer;
}
/// <summary>
/// Determines whether the specified objects are equal
/// </summary>
/// <param name="x">The first object of type T to compare.</param>
/// <param name="y">The second object of type T to compare.</param>
/// <returns>true if the specified objects are equal; otherwise, false.</returns>
public bool Equals(T x, T y)
{
return this.comparer(x, y);
}
/// <summary>
/// Returns a hash code for the specified object
/// </summary>
/// <param name="obj">The System.Object for which a hash code is to be returned.</param>
/// <returns>A hash code for the specified object.</returns>
public int GetHashCode(T obj)
{
return obj.GetHashCode();

View File

@ -13,6 +13,9 @@ using System.Windows.Media.Imaging;
namespace Stylet
{
/// <summary>
/// Class holding extension method(s) on IWindowManager, used to show a MessageBox
/// </summary>
public static class MessageBoxWindowManagerExtensions
{
/// <summary>
@ -77,6 +80,9 @@ namespace Stylet
/// </summary>
public static IDictionary<MessageBoxResult, string> ButtonLabels { get; set; }
/// <summary>
/// Mapping of MessageBoxButton values to the buttons which should be displayed
/// </summary>
public static IDictionary<MessageBoxButton, MessageBoxResult[]> ButtonToResults { get; set; }
/// <summary>
@ -229,6 +235,9 @@ namespace Stylet
/// </summary>
public virtual MessageBoxResult ClickedButton { get; protected set; }
/// <summary>
/// When the View loads, play a sound if appropriate
/// </summary>
protected override void OnViewLoaded()
{
// There might not be a sound, or it might be null
@ -238,6 +247,10 @@ namespace Stylet
sound.Play();
}
/// <summary>
/// Called when MessageBoxView when the user clicks a button
/// </summary>
/// <param name="button">Button which was clicked</param>
public void ButtonClicked(MessageBoxResult button)
{
this.ClickedButton = button;

View File

@ -40,7 +40,7 @@ namespace Stylet
/// <summary>
/// Raise a PropertyChanged notification from the property in the given expression, e.g. NotifyOfPropertyChange(() => this.Property)
/// </summary
/// </summary>
/// <param name="property">Expression describing the property to raise a PropertyChanged notification for</param>
protected virtual void NotifyOfPropertyChange<TProperty>(Expression<Func<TProperty>> property)
{

View File

@ -14,9 +14,15 @@ namespace Stylet
/// </summary>
public interface IEventBinding
{
/// <summary>
/// Unbind this event binding, so that it will no longer receive events
/// </summary>
void Unbind();
}
/// <summary>
/// Class holding extension methods on INotifyPropertyChanged, to allow strong/weak binding
/// </summary>
public static class PropertyChangedExtensions
{
internal class StrongPropertyChangedBinding : IEventBinding

View File

@ -16,7 +16,15 @@ namespace Stylet
/// </summary>
public class Screen : ValidatingModelBase, IScreen
{
/// <summary>
/// Create a new Screen instance (without setting up a validator)
/// </summary>
public Screen() : this(null) { }
/// <summary>
/// Create a new screen instance, which can validate properties using the given validator
/// </summary>
/// <param name="validator">Validator to use</param>
public Screen(IModelValidator validator) : base(validator)
{
this.DisplayName = this.GetType().FullName;
@ -57,6 +65,10 @@ namespace Stylet
#region IHaveDisplayName
private string _displayName;
/// <summary>
/// Name associated with this ViewModel. Shown e.g. in a window's title bar, or as a tab's displayName
/// </summary>
public virtual string DisplayName
{
get { return this._displayName; }
@ -67,6 +79,9 @@ namespace Stylet
#region IActivate
/// <summary>
/// Fired whenever the Screen is activated
/// </summary>
public event EventHandler<ActivationEventArgs> Activated;
private bool hasBeenActivatedEver;
@ -114,6 +129,9 @@ namespace Stylet
#region IDeactivate
/// <summary>
/// Fired whenever the Screen is deactivated
/// </summary>
public event EventHandler<DeactivationEventArgs> Deactivated;
[SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "Stylet.Screen.#Stylet.IDeactivate.Deactivate()", Justification = "As this is a framework type, don't want to make it too easy for users to call this method")]
@ -140,6 +158,9 @@ namespace Stylet
#region IClose
/// <summary>
/// Called whenever this Screen is closed
/// </summary>
public event EventHandler<CloseEventArgs> Closed;
[SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "As this is a framework type, don't want to make it too easy for users to call this method")]
@ -166,6 +187,9 @@ namespace Stylet
#region IViewAware
/// <summary>
/// View attached to this ViewModel, if any. Using this should be a last resort
/// </summary>
public UIElement View { get; private set; }
[SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "As this is a framework type, don't want to make it too easy for users to call this method")]

View File

@ -8,8 +8,17 @@ using System.Threading.Tasks;
namespace Stylet
{
// Don't name ConductorExtensions, otherwise it's too obvious when someone types 'Conductor'
/// <summary>
/// Extension methods used by the Conductor classes
/// </summary>
public static class StyletConductorExtensions
{
/// <summary>
/// For each item in a list, set the parent to the current conductor
/// </summary>
/// <typeparam name="T">Type of conductor</typeparam>
/// <param name="parent">Parent to set the items' parent to</param>
/// <param name="items">Items to manipulate</param>
public static void SetParent<T>(this IConductor<T> parent, IEnumerable items)
{
foreach (var child in items.OfType<IChild>())
@ -19,8 +28,11 @@ namespace Stylet
}
/// <summary>
/// Close an item, and clean it up a bit
/// Close an item, and clear its parent if it's set to the current parent
/// </summary>
/// <typeparam name="T">Type of conductor</typeparam>
/// <param name="parent">Parent</param>
/// <param name="item">Item to close and clean up</param>
public static void CloseAndCleanUp<T>(this IConductor<T> parent, T item)
{
ScreenExtensions.TryClose(item);
@ -30,6 +42,12 @@ namespace Stylet
itemAsChild.Parent = null;
}
/// <summary>
/// For each item in a list, close it, and if its parent is set to the given parent, clear that parent
/// </summary>
/// <typeparam name="T">Type of conductor</typeparam>
/// <param name="parent">Parent</param>
/// <param name="items">List of items to close and clean up</param>
public static void CloseAndCleanUp<T>(this IConductor<T> parent, IEnumerable items)
{
foreach (var item in items.OfType<T>())

View File

@ -16,6 +16,7 @@ namespace StyletIoC
{
/// <summary>
/// Bind the specified service to itself - if you self-bind MyClass, and request an instance of MyClass, you'll get an instance of MyClass.
/// </summary>
/// <returns></returns>
IInScopeOrWithKey ToSelf();
@ -50,14 +51,9 @@ namespace StyletIoC
IInScopeOrWithKey ToAllImplementations(params Assembly[] assemblies);
}
public interface IInScopeOrWithKey : IInScope
{
/// <summary>
/// Associate a key with this binding. Requests for the service will have to specify this key to retrieve the result of this binding
/// </summary>
/// <param name="key">Key to associate with this binding</param>
IInScope WithKey(string key);
}
/// <summary>
/// Fluent interface on which WithKey can be called
/// </summary>
public interface IWithKey
{
/// <summary>
@ -66,6 +62,10 @@ namespace StyletIoC
/// <param name="key">Key to associate with this binding</param>
void WithKey(string key);
}
/// <summary>
/// Fluent interface on which InSingletonScope can be called
/// </summary>
public interface IInScope
{
/// <summary>
@ -74,6 +74,18 @@ namespace StyletIoC
void InSingletonScope();
}
/// <summary>
/// Fluent interface on which InSingletonScope or WithKey can be called
/// </summary>
public interface IInScopeOrWithKey : IInScope
{
/// <summary>
/// Associate a key with this binding. Requests for the service will have to specify this key to retrieve the result of this binding
/// </summary>
/// <param name="key">Key to associate with this binding</param>
IInScope WithKey(string key);
}
internal class BuilderBindTo : IBindTo
{
public Type ServiceType { get; private set; }

View File

@ -12,6 +12,9 @@ using System.Threading.Tasks;
namespace StyletIoC
{
/// <summary>
/// Describes an IoC container, specifically StyletIoC
/// </summary>
public interface IContainer
{
/// <summary>
@ -165,7 +168,7 @@ namespace StyletIoC
/// <summary>
/// Fetch instances of all types which implement the specified service
/// </summary>
/// <typeparam name="T">Type of the service to fetch implementations for</typeparam>
/// <param name="type">Type of the service to fetch implementations for</param>
/// <param name="key">Key that implementations of the service to fetch were registered with, defaults to null</param>
/// <returns>All implementations of the requested service, with the requested key</returns>
public IEnumerable<object> GetAll(Type type, string key = null)
@ -605,47 +608,77 @@ namespace StyletIoC
}
}
/// <summary>
/// Interface to be implemented by objects if they want to be notified when property injection has occurred
/// </summary>
public interface IInjectionAware
{
/// <summary>
/// Called by StyletIoC when property injection has occurred
/// </summary>
void ParametersInjected();
}
public class StyletIoCException : Exception
/// <summary>
/// Base class for all exceptions describing StyletIoC-specific problems?
/// </summary>
public abstract class StyletIoCException : Exception
{
public StyletIoCException(string message) : base(message) { }
public StyletIoCException(string message, Exception innerException) : base(message, innerException) { }
internal StyletIoCException(string message) : base(message) { }
internal StyletIoCException(string message, Exception innerException) : base(message, innerException) { }
}
/// <summary>
/// A problem occured with a registration process (failed to register, failed to find a registration, etc)
/// </summary>
public class StyletIoCRegistrationException : StyletIoCException
{
public StyletIoCRegistrationException(string message) : base(message) { }
public StyletIoCRegistrationException(string message, Exception innerException) : base(message, innerException) { }
internal StyletIoCRegistrationException(string message) : base(message) { }
internal StyletIoCRegistrationException(string message, Exception innerException) : base(message, innerException) { }
}
/// <summary>
/// StyletIoC was unable to find a callable constructor for a type
/// </summary>
public class StyletIoCFindConstructorException : StyletIoCException
{
public StyletIoCFindConstructorException(string message) : base(message) { }
internal StyletIoCFindConstructorException(string message) : base(message) { }
}
/// <summary>
/// StyletIoC was unable to create an abstract factory
/// </summary>
public class StyletIoCCreateFactoryException : StyletIoCException
{
public StyletIoCCreateFactoryException(string message) : base(message) { }
public StyletIoCCreateFactoryException(string message, Exception innerException) : base(message, innerException) { }
internal StyletIoCCreateFactoryException(string message) : base(message) { }
internal StyletIoCCreateFactoryException(string message, Exception innerException) : base(message, innerException) { }
}
/// <summary>
/// Attribute which can be used to mark the constructor to use, properties to inject, which key to use to resolve an injected property, and others. See the docs
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
public sealed class InjectAttribute : Attribute
{
/// <summary>
/// Create a new InjectAttribute
/// </summary>
public InjectAttribute()
{
}
/// <summary>
/// Create a new InjectAttribute, which has the specified key
/// </summary>
/// <param name="key"></param>
public InjectAttribute(string key)
{
this.Key = key;
}
// This is a named argument
/// <summary>
/// Key to use to resolve the relevant dependency
/// </summary>
public string Key { get; set; }
}
}

View File

@ -213,6 +213,10 @@ namespace Stylet
return newErrors == null || newErrors.Length == 0;
}
/// <summary>
/// Raise a PropertyChanged notification for the named property, and validate that property if this.validation is set and this.autoValidate is true
/// </summary>
/// <param name="propertyName"></param>
protected override async void OnPropertyChanged(string propertyName)
{
base.OnPropertyChanged(propertyName);

View File

@ -17,14 +17,18 @@ namespace Stylet
public interface IViewManager
{
/// <summary>
/// Called by the View.Model attached property when the ViewModel its bound to changes
/// Called by View whenever its current View.Model changes. Will locate and instantiate the correct view, and set it as the target's Content
/// </summary>
/// <param name="targetLocation">Thing which View.Model was changed on. Will have its Content set</param>
/// <param name="oldValue">Previous value of View.Model</param>
/// <param name="newValue">New value of View.Model</param>
void OnModelChanged(DependencyObject targetLocation, object oldValue, object newValue);
/// <summary>
/// Given an instance of a ViewModel, locate the correct view for it, and instantiate it
/// Given a ViewModel instance, locate its View type (using LocateViewForModel), instantiates and initializes it
/// </summary>
/// <param name="model">ViewModel to locate the view for</param>
/// <returns>An instance of the correct view</returns>
/// <param name="model">ViewModel to locate and instantiate the View for</param>
/// <returns>Instantiated and setup view</returns>
UIElement CreateAndSetupViewForModel(object model);
/// <summary>
@ -35,8 +39,17 @@ namespace Stylet
void BindViewToModel(UIElement view, object viewModel);
}
/// <summary>
/// Default implementation of ViewManager. Responsible for locating, creating, and settings up Views. Also owns the View.Model and View.ActionTarget attached properties
/// </summary>
public class ViewManager : IViewManager
{
/// <summary>
/// Called by View whenever its current View.Model changes. Will locate and instantiate the correct view, and set it as the target's Content
/// </summary>
/// <param name="targetLocation">Thing which View.Model was changed on. Will have its Content set</param>
/// <param name="oldValue">Previous value of View.Model</param>
/// <param name="newValue">New value of View.Model</param>
public virtual void OnModelChanged(DependencyObject targetLocation, object oldValue, object newValue)
{
if (oldValue == newValue)
@ -64,6 +77,11 @@ namespace Stylet
}
}
/// <summary>
/// Given the expected name for a view, locate its type (or throw an exception if a suitable type couldn't be found)
/// </summary>
/// <param name="viewName">View name to locate the type for</param>
/// <returns>Type for that view name</returns>
public virtual Type ViewTypeForViewName(string viewName)
{
// TODO: This might need some more thinking
@ -74,6 +92,11 @@ namespace Stylet
return viewType;
}
/// <summary>
/// Given the type of a model, locate the type of its View (or throw an exception)
/// </summary>
/// <param name="modelType">Model to find the view for</param>
/// <returns>Type of the ViewModel's View</returns>
public virtual Type LocateViewForModel(Type modelType)
{
var viewName = Regex.Replace(modelType.FullName, @"ViewModel", "View");
@ -82,6 +105,11 @@ namespace Stylet
return viewType;
}
/// <summary>
/// Given an instance of a ViewModel and an instance of its View, bind the two together
/// </summary>
/// <param name="view">View to bind to the ViewModel</param>
/// <param name="viewModel">ViewModel to bind the View to</param>
public virtual void BindViewToModel(UIElement view, object viewModel)
{
View.SetActionTarget(view, viewModel);
@ -94,6 +122,12 @@ namespace Stylet
if (viewModelAsViewAware != null)
viewModelAsViewAware.AttachView(view);
}
/// <summary>
/// Given a ViewModel instance, locate its View type (using LocateViewForModel), instantiates and initializes it, and binds it to the ViewModel (using BindViewToModel)
/// </summary>
/// <param name="model">ViewModel to locate and instantiate the View for</param>
/// <returns>Instantiated and setup view</returns>
public virtual UIElement CreateAndSetupViewForModel(object model)
{
var viewType = this.LocateViewForModel(model.GetType());

View File

@ -9,8 +9,20 @@ using System.Threading.Tasks;
namespace Stylet
{
/// <summary>
/// A manager capable of creating a weak event subscription for INotifyPropertyChanged events from a source to a subscriber. Manager MUST be owned by the subscriber.
/// </summary>
public interface IWeakEventManager
{
/// <summary>
/// Create a weak event subscription from the source, to the given handler
/// </summary>
/// <typeparam name="TSource">Type of the source</typeparam>
/// <typeparam name="TProperty">Type of the property to subscribe to on the source</typeparam>
/// <param name="source">Source object, whic implements INotifyPropertyChanged, to subscribe to</param>
/// <param name="selector">Describes which property to observe, e.g. (x => x.SomeProperty)</param>
/// <param name="handler">Callback to be called whenever the property changes. Is passed the new value of the property</param>
/// <returns>An event binding, which can be used to unregister the subscription</returns>
IEventBinding BindWeak<TSource, TProperty>(TSource source, Expression<Func<TSource, TProperty>> selector, Action<TProperty> handler)
where TSource : class, INotifyPropertyChanged;
}
@ -54,11 +66,23 @@ namespace Stylet
}
}
/// <summary>
/// Default implementation of IWeakEventManager: a manager capable of creating a weak event subscription for INotifyPropertyChanged events from a source to a subscriber. Manager MUST be owned by the subscriber.
/// </summary>
public class WeakEventManager : IWeakEventManager
{
private object bindingsLock = new object();
private List<IEventBinding> bindings = new List<IEventBinding>();
/// <summary>
/// Create a weak event subscription from the source, to the given handler
/// </summary>
/// <typeparam name="TSource">Type of the source</typeparam>
/// <typeparam name="TProperty">Type of the property to subscribe to on the source</typeparam>
/// <param name="source">Source object, whic implements INotifyPropertyChanged, to subscribe to</param>
/// <param name="selector">Describes which property to observe, e.g. (x => x.SomeProperty)</param>
/// <param name="handler">Callback to be called whenever the property changes. Is passed the new value of the property</param>
/// <returns>An event binding, which can be used to unregister the subscription</returns>
public IEventBinding BindWeak<TSource, TProperty>(TSource source, Expression<Func<TSource, TProperty>> selector, Action<TProperty> handler)
where TSource : class, INotifyPropertyChanged
{

View File

@ -10,34 +10,69 @@ using System.Windows.Navigation;
namespace Stylet
{
/// <summary>
/// Manager capable of taking a ViewModel instance, instantiating its View and showing it as a dialog or window
/// </summary>
public interface IWindowManager
{
/// <summary>
/// Given a ViewModel, show its corresponding View as a window
/// </summary>
/// <param name="viewModel">ViewModel to show the View for</param>
void ShowWindow(object viewModel);
/// <summary>
/// Given a ViewModel, show its corresponding View as a Dialog
/// </summary>
/// <param name="viewModel">ViewModel to show the View for</param>
/// <returns>DialogResult of the View</returns>
bool? ShowDialog(object viewModel);
}
/// <summary>
/// Default implementation of IWindowManager, is capable of showing a ViewModel's View as a dialog or a window
/// </summary>
public class WindowManager : IWindowManager
{
private IViewManager viewManager;
/// <summary>
/// Create a new WindowManager instance, using the given IViewManager
/// </summary>
/// <param name="viewManager">IViewManager to use when creating views</param>
public WindowManager(IViewManager viewManager)
{
this.viewManager = viewManager;
}
/// <summary>
/// Given a ViewModel, show its corresponding View as a window
/// </summary>
/// <param name="viewModel">ViewModel to show the View for</param>
public void ShowWindow(object viewModel)
{
this.CreateWindow(viewModel, false).Show();
}
/// <summary>
/// Given a ViewModel, show its corresponding View as a Dialog
/// </summary>
/// <param name="viewModel">ViewModel to show the View for</param>
/// <returns>DialogResult of the View</returns>
public bool? ShowDialog(object viewModel)
{
return this.CreateWindow(viewModel, true).ShowDialog();
}
/// <summary>
/// Given a ViewModel, create its View, ensure that it's a Window, and set it up
/// </summary>
/// <param name="viewModel">ViewModel to create the window for</param>
/// <param name="isDialog">True if the window will be used as a dialog</param>
/// <returns>Window which was created and set up</returns>
protected virtual Window CreateWindow(object viewModel, bool isDialog)
{
var view = this.viewManager.CreateViewForModel(viewModel);
var view = this.viewManager.CreateAndSetupViewForModel(viewModel);
var window = view as Window;
if (window == null)
throw new ArgumentException(String.Format("Tried to show {0} as a window, but it isn't a Window", view == null ? "(null)" : view.GetType().Name));

View File

@ -10,10 +10,24 @@ using System.Windows.Markup;
namespace Stylet.Xaml
{
/// <summary>
/// What to do if the given target is null, or if the given action doesn't exist on the target
/// </summary>
public enum ActionUnavailableBehaviour
{
/// <summary>
/// Enable the control anyway. Clicking/etc the control won't do anything
/// </summary>
Enable,
/// <summary>
/// Disable the control. This is only valid for commands, not events
/// </summary>
Disable,
/// <summary>
/// Throw an exception
/// </summary>
Throw
};
@ -46,6 +60,11 @@ namespace Stylet.Xaml
this.Method = method;
}
/// <summary>
/// When implemented in a derived class, returns an object that is provided as the value of the target property for this markup extension.
/// </summary>
/// <param name="serviceProvider">A service provider helper that can provide services for the markup extension.</param>
/// <returns>The object value to set on the property where the extension is applied.</returns>
public override object ProvideValue(IServiceProvider serviceProvider)
{
var valueService = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));

View File

@ -14,6 +14,9 @@ namespace Stylet.Xaml
/// </summary>
public class BoolToVisibilityConverter : DependencyObject, IValueConverter
{
/// <summary>
/// Singleton instance of this converter. Usage e.g. Converter="{x:Static s:BoolToVisibilityConverter.Instance}"
/// </summary>
public static readonly BoolToVisibilityConverter Instance = new BoolToVisibilityConverter();
/// <summary>
@ -25,7 +28,9 @@ namespace Stylet.Xaml
set { SetValue(TrueVisibilityProperty, value); }
}
// Using a DependencyProperty as the backing store for TrueVisibility. This enables animation, styling, binding, etc...
/// <summary>
/// Property specifying the visibility to return when the parameter is true
/// </summary>
public static readonly DependencyProperty TrueVisibilityProperty =
DependencyProperty.Register("TrueVisibility", typeof(Visibility), typeof(BoolToVisibilityConverter), new PropertyMetadata(Visibility.Visible));
@ -38,11 +43,16 @@ namespace Stylet.Xaml
set { SetValue(FalseVisibilityProperty, value); }
}
// Using a DependencyProperty as the backing store for FalseVisibility. This enables animation, styling, binding, etc...
/// <summary>
/// Property specifying the visibility to return when the parameter is false
/// </summary>
public static readonly DependencyProperty FalseVisibilityProperty =
DependencyProperty.Register("FalseVisibility", typeof(Visibility), typeof(BoolToVisibilityConverter), new PropertyMetadata(Visibility.Collapsed));
/// <summary>
/// Perform the conversion
/// </summary>
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool result;
@ -61,6 +71,9 @@ namespace Stylet.Xaml
return result ? this.TrueVisibility : this.FalseVisibility;
}
/// <summary>
/// Perform the inverse conversion. Only valid if the value is bool
/// </summary>
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (targetType != typeof(bool))

View File

@ -53,6 +53,8 @@ namespace Stylet.Xaml
/// </summary>
/// <param name="subject">View to grab the View.ActionTarget from</param>
/// <param name="methodName">Method name. the MyMethod in Buttom Command="{s:Action MyMethod}".</param>
/// <param name="targetNullBehaviour">Behaviour for it the relevant View.ActionTarget is null</param>
/// <param name="actionNonExistentBehaviour">Behaviour for if the action doesn't exist on the View.ActionTarget</param>
public CommandAction(DependencyObject subject, string methodName, ActionUnavailableBehaviour targetNullBehaviour, ActionUnavailableBehaviour actionNonExistentBehaviour)
{
this.Subject = subject;
@ -139,6 +141,11 @@ namespace Stylet.Xaml
handler(this, EventArgs.Empty);
}
/// <summary>
/// Defines the method that determines whether the command can execute in its current state.
/// </summary>
/// <param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null.</param>
/// <returns>true if this command can be executed; otherwise, false.</returns>
public bool CanExecute(object parameter)
{
// It's enabled only if both the targetNull and actionNonExistent tests pass
@ -162,8 +169,15 @@ namespace Stylet.Xaml
return this.guardPropertyGetter();
}
/// <summary>
/// Occurs when changes occur that affect whether or not the command should execute.
/// </summary>
public event EventHandler CanExecuteChanged;
/// <summary>
/// The method to be called when the command is invoked.
/// </summary>
/// <param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null.</param>
public void Execute(object parameter)
{
// Any throwing would have been handled prior to this

View File

@ -14,6 +14,9 @@ namespace Stylet.Xaml
/// </summary>
public class DebugConverter : DependencyObject, IValueConverter
{
/// <summary>
/// Singleton instance of this DebugConverter. Usage e.g. Converter={x:Static s:DebugConverter.Instance}"
/// </summary>
public static readonly DebugConverter Instance;
/// <summary>
@ -25,7 +28,9 @@ namespace Stylet.Xaml
set { SetValue(NameProperty, value); }
}
// Using a DependencyProperty as the backing store for Name. This enables animation, styling, binding, etc...
/// <summary>
/// Property specifying the category to use with Debug.WriteLine
/// </summary>
public static readonly DependencyProperty NameProperty =
DependencyProperty.Register("Name", typeof(string), typeof(DebugConverter), new PropertyMetadata("DebugConverter"));
@ -39,7 +44,9 @@ namespace Stylet.Xaml
set { SetValue(LoggerProperty, value); }
}
// Using a DependencyProperty as the backing store for Logger. This enables animation, styling, binding, etc...
/// <summary>
/// Property specifying an action, which when called will log an entry.
/// </summary>
public static readonly DependencyProperty LoggerProperty =
DependencyProperty.Register("Logger", typeof(Action<string, string>), typeof(DebugConverter), new PropertyMetadata(null));
@ -51,13 +58,18 @@ namespace Stylet.Xaml
Instance = new DebugConverter();
}
/// <summary>
/// Create a new DebugConverter instance
/// </summary>
public DebugConverter()
{
if (this.Logger == null)
this.Logger = (msg, name) => Debug.WriteLine(msg, name);
}
/// <summary>
/// Perform the conversion
/// </summary>
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (parameter == null)
@ -68,6 +80,9 @@ namespace Stylet.Xaml
return value;
}
/// <summary>
/// Perform the reverse conversion
/// </summary>
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (parameter == null)

View File

@ -13,10 +13,13 @@ namespace Stylet.Xaml
/// </summary>
public class EqualityConverter : DependencyObject, IMultiValueConverter
{
/// <summary>
/// Singleton instance of this converter. Usage: Converter="{x:Static s:EqualityConverter.Instance}"
/// </summary>
public static readonly EqualityConverter Instance = new EqualityConverter();
/// <summary>
/// True false, instead of true, if call values are equal
/// Return false, instead of true, if call values are equal
/// </summary>
public bool Invert
{
@ -24,9 +27,15 @@ namespace Stylet.Xaml
set { SetValue(InvertProperty, value); }
}
/// <summary>
/// Property specifying whether the output should be inverted
/// </summary>
public static readonly DependencyProperty InvertProperty =
DependencyProperty.Register("Invert", typeof(bool), typeof(EqualityConverter), new PropertyMetadata(false));
/// <summary>
/// Perform the conversion
/// </summary>
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (values == null || values.Length == 0)
@ -36,6 +45,9 @@ namespace Stylet.Xaml
return this.Invert ? !result : result;
}
/// <summary>
/// Perform the reverse convesion. Not implemented.
/// </summary>
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();

View File

@ -36,7 +36,7 @@ namespace Stylet.Xaml
private object target;
private ActionUnavailableBehaviour nullTargetBehaviour;
private ActionUnavailableBehaviour targetNullBehaviour;
private ActionUnavailableBehaviour actionNonExistentBehaviour;
/// <summary>
@ -45,9 +45,11 @@ namespace Stylet.Xaml
/// <param name="subject">View whose View.ActionTarget we watch</param>
/// <param name="targetProperty">Property on the WPF element we're returning a delegate for</param>
/// <param name="methodName">The MyMethod in {s:Action MyMethod}, this is what we call when the event's fired</param>
public EventAction(DependencyObject subject, EventInfo targetProperty, string methodName, ActionUnavailableBehaviour nullTargetBehaviour, ActionUnavailableBehaviour actionNonExistentBehaviour)
/// <param name="targetNullBehaviour">Behaviour for it the relevant View.ActionTarget is null</param>
/// <param name="actionNonExistentBehaviour">Behaviour for if the action doesn't exist on the View.ActionTarget</param>
public EventAction(DependencyObject subject, EventInfo targetProperty, string methodName, ActionUnavailableBehaviour targetNullBehaviour, ActionUnavailableBehaviour actionNonExistentBehaviour)
{
if (nullTargetBehaviour == ActionUnavailableBehaviour.Disable)
if (targetNullBehaviour == ActionUnavailableBehaviour.Disable)
throw new ArgumentException("Setting NullTarget = Disable is unsupported when used on an Event");
if (actionNonExistentBehaviour == ActionUnavailableBehaviour.Disable)
throw new ArgumentException("Setting ActionNotFound = Disable is unsupported when used on an Event");
@ -55,7 +57,7 @@ namespace Stylet.Xaml
this.subject = subject;
this.targetProperty = targetProperty;
this.methodName = methodName;
this.nullTargetBehaviour = nullTargetBehaviour;
this.targetNullBehaviour = targetNullBehaviour;
this.actionNonExistentBehaviour = actionNonExistentBehaviour;
this.UpdateMethod();
@ -71,7 +73,7 @@ namespace Stylet.Xaml
if (newTarget == null)
{
if (this.nullTargetBehaviour == ActionUnavailableBehaviour.Throw)
if (this.targetNullBehaviour == ActionUnavailableBehaviour.Throw)
throw new ArgumentException(String.Format("Method {0} has a target set which is null", this.methodName));
}
else
@ -98,12 +100,9 @@ namespace Stylet.Xaml
/// <summary>
/// Return a delegate which can be added to the targetProperty
/// </summary>
public Delegate GetDelegate()
public RoutedEventHandler GetDelegate()
{
var methodInfo = this.GetType().GetMethod("InvokeCommand", BindingFlags.NonPublic | BindingFlags.Instance);
var parameterType = this.targetProperty.EventHandlerType;
return Delegate.CreateDelegate(parameterType, this, methodInfo);
return new RoutedEventHandler(this.InvokeCommand);
}
private void InvokeCommand(object sender, RoutedEventArgs e)

View File

@ -16,8 +16,14 @@ namespace Stylet.Xaml
/// </summary>
public class IconToBitmapSourceConverter : IValueConverter
{
/// <summary>
/// Singleton instance of this converter. Usage e.g. Converter="{x:Static s:IconToBitmapSourceConverter.Instance}"
/// </summary>
public static IconToBitmapSourceConverter Instance = new IconToBitmapSourceConverter();
/// <summary>
/// Converts a value
/// </summary>
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var icon = value as Icon;
@ -27,6 +33,9 @@ namespace Stylet.Xaml
return bs;
}
/// <summary>
/// Converts a value back. Not implemented.
/// </summary>
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();

View File

@ -9,38 +9,76 @@ using System.Windows.Markup;
namespace Stylet.Xaml
{
/// <summary>
/// Holds attached properties relating to various bits of the View which are used by Stylet
/// </summary>
public class View : DependencyObject
{
private static readonly ContentPropertyAttribute defaultContentProperty = new ContentPropertyAttribute("Content");
/// <summary>
/// IViewManager to be used. This should be set by the Bootstrapper.
/// </summary>
public static IViewManager ViewManager;
/// <summary>
/// Get the ActionTarget associated with the given object
/// </summary>
/// <param name="obj">Object to fetch the ActionTarget for</param>
/// <returns>ActionTarget associated with the given object</returns>
public static object GetActionTarget(DependencyObject obj)
{
return (object)obj.GetValue(ActionTargetProperty);
}
/// <summary>
/// Set the ActionTarget associated with the given object
/// </summary>
/// <param name="obj">Object to set the ActionTarget for</param>
/// <param name="value">Value to set the ActionTarget to</param>
public static void SetActionTarget(DependencyObject obj, object value)
{
obj.SetValue(ActionTargetProperty, value);
}
/// <summary>
/// The object's ActionTarget. This is used to determine what object to call Actions on by the ActionExtension markup extension.
/// </summary>
public static readonly DependencyProperty ActionTargetProperty =
DependencyProperty.RegisterAttached("ActionTarget", typeof(object), typeof(View), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits));
/// <summary>
/// Fetch the ViewModel currently associated with a given object
/// </summary>
/// <param name="obj">Object to fetch the ViewModel for</param>
/// <returns>ViewModel currently associated with the given object</returns>
public static object GetModel(DependencyObject obj)
{
return (object)obj.GetValue(ModelProperty);
}
/// <summary>
/// Set the ViewModel currently associated with a given object
/// </summary>
/// <param name="obj">Object to set the ViewModel for</param>
/// <param name="value">ViewModel to set</param>
public static void SetModel(DependencyObject obj, object value)
{
obj.SetValue(ModelProperty, value);
}
/// <summary>
/// Property specifying the ViewModel currently associated with a given object
/// </summary>
public static readonly DependencyProperty ModelProperty =
DependencyProperty.RegisterAttached("Model", typeof(object), typeof(View), new PropertyMetadata(null, (d, e) => ViewManager.OnModelChanged(d, e.OldValue, e.NewValue) ));
/// <summary>
/// Helper to set the Content property of a given object to a particular View
/// </summary>
/// <param name="targetLocation">Object to set the Content property on</param>
/// <param name="view">View to set as the object's Content</param>
public static void SetContentProperty(DependencyObject targetLocation, UIElement view)
{
var type = targetLocation.GetType();