diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 8b22a6e..bf7ad5d 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,6 +1,14 @@ Stylet Changelog ================ +v0.9.4 +------ + + - Fix BindableCollection issues + - ActionExtension has configurable behaviour if target/action are null/not found + - ActionExtension works with things like Hyperlinks + - Misc tweaks and fixes + v0.9.3 ------ diff --git a/NuGet/Stylet.nuspec b/NuGet/Stylet.nuspec index 03cd843..cc3ecc7 100644 --- a/NuGet/Stylet.nuspec +++ b/NuGet/Stylet.nuspec @@ -2,7 +2,7 @@ Stylet - 0.9.3 + 0.9.4 Stylet Antony Male Antony Male diff --git a/README.md b/README.md index 6b129ff..2ddf678 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,57 @@ StyletIoC ========= -This project is still in development. +Introduction +------------ -[The wiki](https://github.com/canton7/Stylet/wiki/_pages) is the official (incomplete) documentation source. +Blah blah + + +Installation +------------ + +You can either grab Stylet through NuGet, or build it from source yourself. +Stylet does rely on .NET 4.5 (Visual Studio 2012 or higher). + +### NuGet + +[Stylet is available on NuGet](https://www.nuget.org/packages/Stylet). + +Either open the package console and type: + +``` +PM> Install-Package Stylet +``` + +Or right-click your project -> Manage NuGet Packages... -> Online -> search for Stylet in the top right. + +Don't forget to right-click your solution, and click "Enable NuGet package restore"! + +I also publish symbols on [SymbolSource](http://www.symbolsource.org/Public), so you can use the NuGet package but still have access to Stylet's source when debugging. If you haven't yet set up Visual Studio to use SymbolSource, do that now: + +In Visual Studio, go to Debug -> Options and Settings, and make the following changes: + + - Under General, turn **off** "Enable Just My Code" + - Under General, turn **on** "Enable source server support". You may have to Ok a security warning. + - Under Symbols, add "http://srv.symbolsource.org/pdb/Public" to the list. + +### Source + +I maintain a subtree split of just the Stylet project, [called Stylet-Core](https://github.com/canton7/Stylet-Core). +Head over there, clone/download the repo, and add the .csproj to your solution. + + +Documentation +------------- + +[The wiki is the official documentation source](https://github.com/canton7/Stylet/wiki). +There's a lot of documentation there (it was longer than my dissertation last time I checked), and it's being added to all the time. +Go check it out! + + +Contributing +------------ + +Contributions are always welcome. +If you've got a problem or a question, [raise an issue](https://github.com/canton7/Stylet/issues). +If you've got code you want to contribute, create a feature branch off the `develop` branch, add your changes there, and submit it as a pull request. \ No newline at end of file diff --git a/Samples/Stylet.Samples.Hello/ShellView.xaml b/Samples/Stylet.Samples.Hello/ShellView.xaml index e944ad2..5823506 100644 --- a/Samples/Stylet.Samples.Hello/ShellView.xaml +++ b/Samples/Stylet.Samples.Hello/ShellView.xaml @@ -1,7 +1,7 @@  diff --git a/Samples/Stylet.Samples.HelloDialog/Dialog1View.xaml b/Samples/Stylet.Samples.HelloDialog/Dialog1View.xaml index 713f4d2..0db8334 100644 --- a/Samples/Stylet.Samples.HelloDialog/Dialog1View.xaml +++ b/Samples/Stylet.Samples.HelloDialog/Dialog1View.xaml @@ -1,7 +1,7 @@  diff --git a/Samples/Stylet.Samples.HelloDialog/ShellView.xaml b/Samples/Stylet.Samples.HelloDialog/ShellView.xaml index 8adad9f..78e6434 100644 --- a/Samples/Stylet.Samples.HelloDialog/ShellView.xaml +++ b/Samples/Stylet.Samples.HelloDialog/ShellView.xaml @@ -1,7 +1,7 @@  diff --git a/Samples/Stylet.Samples.MasterDetail/ShellView.xaml b/Samples/Stylet.Samples.MasterDetail/ShellView.xaml index c4f1075..a584448 100644 --- a/Samples/Stylet.Samples.MasterDetail/ShellView.xaml +++ b/Samples/Stylet.Samples.MasterDetail/ShellView.xaml @@ -1,7 +1,7 @@  diff --git a/Samples/Stylet.Samples.RedditBrowser/Pages/PostCommentsView.xaml b/Samples/Stylet.Samples.RedditBrowser/Pages/PostCommentsView.xaml index 5f99960..1d64272 100644 --- a/Samples/Stylet.Samples.RedditBrowser/Pages/PostCommentsView.xaml +++ b/Samples/Stylet.Samples.RedditBrowser/Pages/PostCommentsView.xaml @@ -4,7 +4,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:redditapi="clr-namespace:Stylet.Samples.RedditBrowser.RedditApi" - xmlns:s="http://github.com/canton7/Stylet" + xmlns:s="https://github.com/canton7/Stylet" xmlns:uc="clr-namespace:Stylet.Samples.RedditBrowser.UserControls" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> diff --git a/Samples/Stylet.Samples.RedditBrowser/Pages/PostsView.xaml b/Samples/Stylet.Samples.RedditBrowser/Pages/PostsView.xaml index 9b49bad..7e45eef 100644 --- a/Samples/Stylet.Samples.RedditBrowser/Pages/PostsView.xaml +++ b/Samples/Stylet.Samples.RedditBrowser/Pages/PostsView.xaml @@ -3,7 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:s="http://github.com/canton7/Stylet" + xmlns:s="https://github.com/canton7/Stylet" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> diff --git a/Samples/Stylet.Samples.RedditBrowser/Pages/ShellView.xaml b/Samples/Stylet.Samples.RedditBrowser/Pages/ShellView.xaml index 996c9fe..278a753 100644 --- a/Samples/Stylet.Samples.RedditBrowser/Pages/ShellView.xaml +++ b/Samples/Stylet.Samples.RedditBrowser/Pages/ShellView.xaml @@ -1,7 +1,7 @@  diff --git a/Samples/Stylet.Samples.RedditBrowser/Pages/SubredditView.xaml b/Samples/Stylet.Samples.RedditBrowser/Pages/SubredditView.xaml index da432ac..fdd611d 100644 --- a/Samples/Stylet.Samples.RedditBrowser/Pages/SubredditView.xaml +++ b/Samples/Stylet.Samples.RedditBrowser/Pages/SubredditView.xaml @@ -3,7 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:s="http://github.com/canton7/Stylet" + xmlns:s="https://github.com/canton7/Stylet" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> diff --git a/Samples/Stylet.Samples.RedditBrowser/Pages/TaskbarView.xaml b/Samples/Stylet.Samples.RedditBrowser/Pages/TaskbarView.xaml index d2b5cf9..53c78ed 100644 --- a/Samples/Stylet.Samples.RedditBrowser/Pages/TaskbarView.xaml +++ b/Samples/Stylet.Samples.RedditBrowser/Pages/TaskbarView.xaml @@ -3,7 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:s="http://github.com/canton7/Stylet" + xmlns:s="https://github.com/canton7/Stylet" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> diff --git a/Samples/Stylet.Samples.TabNavigation/ShellView.xaml b/Samples/Stylet.Samples.TabNavigation/ShellView.xaml index 63cf5af..748926b 100644 --- a/Samples/Stylet.Samples.TabNavigation/ShellView.xaml +++ b/Samples/Stylet.Samples.TabNavigation/ShellView.xaml @@ -1,7 +1,7 @@  diff --git a/Stylet/BindableCollection.cs b/Stylet/BindableCollection.cs index 72f4295..4ba09ea 100644 --- a/Stylet/BindableCollection.cs +++ b/Stylet/BindableCollection.cs @@ -82,7 +82,8 @@ namespace Stylet this.isNotifying = previousNotificationSetting; this.OnPropertyChanged(new PropertyChangedEventArgs("Count")); this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); - this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, items.ToList())); + // Can't add with a range, or it throws an exception + this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } /// @@ -104,7 +105,8 @@ namespace Stylet this.isNotifying = previousNotificationSetting; this.OnPropertyChanged(new PropertyChangedEventArgs("Count")); this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); - this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, items.ToList())); + // Can't remove with a range, or it throws an exception + this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } /// diff --git a/Stylet/Properties/AssemblyInfo.cs b/Stylet/Properties/AssemblyInfo.cs index 1890254..e00489c 100644 --- a/Stylet/Properties/AssemblyInfo.cs +++ b/Stylet/Properties/AssemblyInfo.cs @@ -23,7 +23,7 @@ using System.Windows.Markup; // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("a557a739-6b61-44d2-a431-889bc11aac9e")] -[assembly: XmlnsDefinition("http://github.com/canton7/Stylet", "Stylet")] +[assembly: XmlnsDefinition("https://github.com/canton7/Stylet", "Stylet")] // Version information for an assembly consists of the following four values: // @@ -35,5 +35,5 @@ using System.Windows.Markup; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -//[assembly: AssemblyVersion("1.0.0.0")] -//[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("0.9.4.0")] +[assembly: AssemblyFileVersion("0.9.4.0")] diff --git a/Stylet/PropertyChangedBase.cs b/Stylet/PropertyChangedBase.cs index 637ad6a..9384935 100644 --- a/Stylet/PropertyChangedBase.cs +++ b/Stylet/PropertyChangedBase.cs @@ -18,6 +18,7 @@ namespace Stylet /// /// Dispatcher to use to dispatch PropertyChanged events. Defaults to Execute.DefaultPropertyChangedDispatcher /// + [System.Xml.Serialization.XmlIgnore] public virtual Action PropertyChangedDispatcher { get { return this._propertyChangedDispatcher; } diff --git a/Stylet/PropertyChangedExtensions.cs b/Stylet/PropertyChangedExtensions.cs index 9f7a0c2..56c9f17 100644 --- a/Stylet/PropertyChangedExtensions.cs +++ b/Stylet/PropertyChangedExtensions.cs @@ -12,14 +12,14 @@ namespace Stylet /// /// A binding to a PropertyChanged event, which can be used to unbind the binding /// - public interface IPropertyChangedBinding + public interface IEventBinding { void Unbind(); } public static class PropertyChangedExtensions { - internal class StrongPropertyChangedBinding : IPropertyChangedBinding + internal class StrongPropertyChangedBinding : IEventBinding { private WeakReference inpc; private PropertyChangedEventHandler handler; @@ -48,7 +48,7 @@ namespace Stylet /// MemberExpression selecting the property to observe for changes (e.g x => x.PropertyName) /// Handler called whenever that property changed /// Something which can be used to undo the binding. You can discard it if you want - public static IPropertyChangedBinding Bind(this TBindTo target, Expression> targetSelector, Action handler) where TBindTo : class, INotifyPropertyChanged + public static IEventBinding Bind(this TBindTo target, Expression> targetSelector, Action handler) where TBindTo : class, INotifyPropertyChanged { var propertyName = targetSelector.NameForProperty(); var propertyAccess = targetSelector.Compile(); diff --git a/Stylet/Screen.cs b/Stylet/Screen.cs index 52febf7..f7605c7 100644 --- a/Stylet/Screen.cs +++ b/Stylet/Screen.cs @@ -32,7 +32,7 @@ namespace Stylet /// Expression for selecting the property to observe, e.g. x => x.PropertyName /// Handler to be called when that property changes /// A resource which can be used to undo the binding - protected IPropertyChangedBinding BindWeak(TSource source, Expression> selector, Action handler) + protected IEventBinding BindWeak(TSource source, Expression> selector, Action handler) where TSource : class, INotifyPropertyChanged { return this.weakEventManager.BindWeak(source, selector, handler); diff --git a/Stylet/WeakEventManager.cs b/Stylet/WeakEventManager.cs index 0fb3293..dd6c390 100644 --- a/Stylet/WeakEventManager.cs +++ b/Stylet/WeakEventManager.cs @@ -10,20 +10,20 @@ namespace Stylet { public interface IWeakEventManager { - IPropertyChangedBinding BindWeak(TSource source, Expression> selector, Action handler) + IEventBinding BindWeak(TSource source, Expression> selector, Action handler) where TSource : class, INotifyPropertyChanged; } - internal class WeakPropertyBinding : IPropertyChangedBinding where TSource : class, INotifyPropertyChanged + internal class WeakPropertyBinding : IEventBinding where TSource : class, INotifyPropertyChanged { // Make sure we don't end up retaining the source private readonly WeakReference source; private readonly string propertyName; private readonly Func valueSelector; private readonly Action handler; - private readonly Action remover; + private readonly Action remover; - public WeakPropertyBinding(TSource source, Expression> selector, Action handler, Action remover) + public WeakPropertyBinding(TSource source, Expression> selector, Action handler, Action remover) { this.source = new WeakReference(source); this.propertyName = selector.NameForProperty(); @@ -55,9 +55,9 @@ namespace Stylet public class WeakEventManager : IWeakEventManager { private object bindingsLock = new object(); - private List bindings = new List(); + private List bindings = new List(); - public IPropertyChangedBinding BindWeak(TSource source, Expression> selector, Action handler) + public IEventBinding BindWeak(TSource source, Expression> selector, Action handler) where TSource : class, INotifyPropertyChanged { // So, the handler's target might point to the class that owns us, or it might point to a compiler-generated class @@ -82,7 +82,7 @@ namespace Stylet return binding; } - internal void Remove(IPropertyChangedBinding binding) + internal void Remove(IEventBinding binding) { lock (this.bindingsLock) { diff --git a/Stylet/Xaml/ActionExtension.cs b/Stylet/Xaml/ActionExtension.cs index b5062cd..87bfdd6 100644 --- a/Stylet/Xaml/ActionExtension.cs +++ b/Stylet/Xaml/ActionExtension.cs @@ -10,6 +10,13 @@ using System.Windows.Markup; namespace Stylet { + public enum ActionUnavailableBehaviour + { + Enable, + Disable, + Throw + }; + /// /// MarkupExtension used for binding Commands and Events to methods on the View.ActionTarget /// @@ -20,6 +27,16 @@ namespace Stylet /// public string Method { get; set; } + /// + /// Behaviour if the View.ActionTarget is nulil + /// + public ActionUnavailableBehaviour? NullTarget { get; set; } + + /// + /// Behaviour if the action itself isn't found on the View.ActionTarget + /// + public ActionUnavailableBehaviour? ActionNotFound { get; set; } + /// /// Create a new ActionExtension /// @@ -35,19 +52,19 @@ namespace Stylet // Seems this is the case when we're in a template. We'll get called again properly in a second. // http://social.msdn.microsoft.com/Forums/vstudio/en-US/a9ead3d5-a4e4-4f9c-b507-b7a7d530c6a9/gaining-access-to-target-object-instead-of-shareddp-in-custom-markupextensions-providevalue-method?forum=wpf - if (!(valueService.TargetObject is FrameworkElement)) + if (!(valueService.TargetObject is DependencyObject)) return this; var propertyAsDependencyProperty = valueService.TargetProperty as DependencyProperty; if (propertyAsDependencyProperty != null && propertyAsDependencyProperty.PropertyType == typeof(ICommand)) { - return new CommandAction((FrameworkElement)valueService.TargetObject, this.Method); + return new CommandAction((DependencyObject)valueService.TargetObject, this.Method, this.NullTarget.GetValueOrDefault(ActionUnavailableBehaviour.Disable), this.ActionNotFound.GetValueOrDefault(ActionUnavailableBehaviour.Throw)); } var propertyAsEventInfo = valueService.TargetProperty as EventInfo; if (propertyAsEventInfo != null) { - var ec = new EventAction((FrameworkElement)valueService.TargetObject, propertyAsEventInfo, this.Method); + var ec = new EventAction((DependencyObject)valueService.TargetObject, propertyAsEventInfo, this.Method, this.NullTarget.GetValueOrDefault(ActionUnavailableBehaviour.Throw), this.ActionNotFound.GetValueOrDefault(ActionUnavailableBehaviour.Throw)); return ec.GetDelegate(); } diff --git a/Stylet/Xaml/CommandAction.cs b/Stylet/Xaml/CommandAction.cs index 4b5c885..b9153a5 100644 --- a/Stylet/Xaml/CommandAction.cs +++ b/Stylet/Xaml/CommandAction.cs @@ -26,7 +26,7 @@ namespace Stylet /// /// View to grab the View.ActionTarget from /// - public FrameworkElement Subject { get; private set; } + public DependencyObject Subject { get; private set; } /// /// Method name. E.g. if someone's gone Buttom Command="{s:Action MyMethod}", this is MyMethod. @@ -45,15 +45,20 @@ namespace Stylet private object target; + private ActionUnavailableBehaviour targetNullBehaviour; + private ActionUnavailableBehaviour actionNonExistentBehaviour; + /// /// Create a new ActionCommand /// /// View to grab the View.ActionTarget from /// Method name. the MyMethod in Buttom Command="{s:Action MyMethod}". - public CommandAction(FrameworkElement subject, string methodName) + public CommandAction(DependencyObject subject, string methodName, ActionUnavailableBehaviour targetNullBehaviour, ActionUnavailableBehaviour actionNonExistentBehaviour) { this.Subject = subject; this.MethodName = methodName; + this.targetNullBehaviour = targetNullBehaviour; + this.actionNonExistentBehaviour = actionNonExistentBehaviour; this.UpdateGuardAndMethod(); @@ -72,7 +77,13 @@ namespace Stylet MethodInfo targetMethodInfo = null; this.guardPropertyGetter = null; - if (newTarget != null) + if (newTarget == null) + { + // If it's Enable or Disable we don't do anything - CanExecute will handle this + if (this.targetNullBehaviour == ActionUnavailableBehaviour.Throw) + throw new Exception(String.Format("Method {0} has a target set which is null", this.MethodName)); + } + else { var newTargetType = newTarget.GetType(); @@ -86,11 +97,16 @@ namespace Stylet targetMethodInfo = newTargetType.GetMethod(this.MethodName); if (targetMethodInfo == null) - throw new ArgumentException(String.Format("Unable to find method {0} on {1}", this.MethodName, newTargetType.Name)); - - var methodParameters = targetMethodInfo.GetParameters(); - if (methodParameters.Length > 1) - throw new ArgumentException(String.Format("Method {0} on {1} must have zero or one parameters", this.MethodName, newTargetType.Name)); + { + if (this.actionNonExistentBehaviour == ActionUnavailableBehaviour.Throw) + throw new ArgumentException(String.Format("Unable to find method {0} on {1}", this.MethodName, newTargetType.Name)); + } + else + { + var methodParameters = targetMethodInfo.GetParameters(); + if (methodParameters.Length > 1) + throw new ArgumentException(String.Format("Method {0} on {1} must have zero or one parameters", this.MethodName, newTargetType.Name)); + } } var oldTarget = this.target as INotifyPropertyChanged; @@ -125,9 +141,21 @@ namespace Stylet public bool CanExecute(object parameter) { - if (this.target == null) + // It's enabled only if both the targetNull and actionNonExistent tests pass + + // Throw is handled when the target is set + if (this.target == null && this.targetNullBehaviour == ActionUnavailableBehaviour.Disable) return false; + // Throw is handled when the target is set + if (this.targetMethodInfo == null) + { + if (this.actionNonExistentBehaviour == ActionUnavailableBehaviour.Disable) + return false; + else + return true; + } + if (this.guardPropertyGetter == null) return true; @@ -138,10 +166,11 @@ namespace Stylet public void Execute(object parameter) { - // This is not going to be called very often, so don't bother to generate a delegate, in the way that we do for the method guard - if (this.target == null) - throw new ArgumentException("Target not set"); + // Any throwing would have been handled prior to this + if (this.target == null || this.targetMethodInfo == null) + return; + // This is not going to be called very often, so don't bother to generate a delegate, in the way that we do for the method guard var parameters = this.targetMethodInfo.GetParameters().Length == 1 ? new[] { parameter } : null; this.targetMethodInfo.Invoke(this.target, parameters); } diff --git a/Stylet/Xaml/EventAction.cs b/Stylet/Xaml/EventAction.cs index ddb80e4..7a28de3 100644 --- a/Stylet/Xaml/EventAction.cs +++ b/Stylet/Xaml/EventAction.cs @@ -17,7 +17,7 @@ namespace Stylet /// /// View whose View.ActionTarget we watch /// - private FrameworkElement subject; + private DependencyObject subject; /// /// Property on the WPF element we're returning a delegate for @@ -36,17 +36,27 @@ namespace Stylet private object target; + private ActionUnavailableBehaviour nullTargetBehaviour; + private ActionUnavailableBehaviour actionNonExistentBehaviour; + /// /// Create a new EventAction /// /// View whose View.ActionTarget we watch /// Property on the WPF element we're returning a delegate for /// The MyMethod in {s:Action MyMethod}, this is what we call when the event's fired - public EventAction(FrameworkElement subject, EventInfo targetProperty, string methodName) + public EventAction(DependencyObject subject, EventInfo targetProperty, string methodName, ActionUnavailableBehaviour nullTargetBehaviour, ActionUnavailableBehaviour actionNonExistentBehaviour) { + if (nullTargetBehaviour == 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"); + this.subject = subject; this.targetProperty = targetProperty; this.methodName = methodName; + this.nullTargetBehaviour = nullTargetBehaviour; + this.actionNonExistentBehaviour = actionNonExistentBehaviour; // Observe the View.ActionTarget for changes, and re-bind the guard property and MethodInfo if it changes DependencyPropertyDescriptor.FromProperty(View.ActionTargetProperty, typeof(View)).AddValueChanged(this.subject, (o, e) => this.UpdateMethod()); @@ -57,16 +67,26 @@ namespace Stylet var newTarget = View.GetActionTarget(this.subject); MethodInfo targetMethodInfo = null; - if (newTarget != null) + if (newTarget == null) + { + if (this.nullTargetBehaviour == ActionUnavailableBehaviour.Throw) + throw new Exception(String.Format("Method {0} has a target set which is null", this.methodName)); + } + else { var newTargetType = newTarget.GetType(); targetMethodInfo = newTargetType.GetMethod(this.methodName); if (targetMethodInfo == null) - throw new ArgumentException(String.Format("Unable to find method {0} on {1}", this.methodName, newTargetType.Name)); - - var methodParameters = targetMethodInfo.GetParameters(); - if (methodParameters.Length > 1 || (methodParameters.Length == 1 && !methodParameters[0].ParameterType.IsAssignableFrom(typeof(RoutedEventArgs)))) - throw new ArgumentException(String.Format("Method {0} on {1} must have zero parameters, or a single parameter accepting a RoutedEventArgs", this.methodName, newTargetType.Name)); + { + if (this.actionNonExistentBehaviour == ActionUnavailableBehaviour.Throw) + throw new ArgumentException(String.Format("Unable to find method {0} on {1}", this.methodName, newTargetType.Name)); + } + else + { + var methodParameters = targetMethodInfo.GetParameters(); + if (methodParameters.Length > 1 || (methodParameters.Length == 1 && !methodParameters[0].ParameterType.IsAssignableFrom(typeof(RoutedEventArgs)))) + throw new ArgumentException(String.Format("Method {0} on {1} must have zero parameters, or a single parameter accepting a RoutedEventArgs", this.methodName, newTargetType.Name)); + } } this.target = newTarget; @@ -86,7 +106,8 @@ namespace Stylet private void InvokeCommand(object sender, RoutedEventArgs e) { - if (this.target == null) + // Any throwing will have been handled above + if (this.target == null || this.targetMethodInfo == null) return; var parameters = this.targetMethodInfo.GetParameters().Length == 1 ? new object[] { e } : null; diff --git a/StyletIntegrationTests/Actions/ActionsView.xaml b/StyletIntegrationTests/Actions/ActionsView.xaml index 611bb28..e52ef56 100644 --- a/StyletIntegrationTests/Actions/ActionsView.xaml +++ b/StyletIntegrationTests/Actions/ActionsView.xaml @@ -1,7 +1,7 @@  diff --git a/StyletIntegrationTests/ShellView.xaml b/StyletIntegrationTests/ShellView.xaml index 77ba1ea..c26a78b 100644 --- a/StyletIntegrationTests/ShellView.xaml +++ b/StyletIntegrationTests/ShellView.xaml @@ -1,7 +1,7 @@  diff --git a/StyletIntegrationTests/ShowDialogAndDialogResult/DialogView.xaml b/StyletIntegrationTests/ShowDialogAndDialogResult/DialogView.xaml index d71f2c6..2e167af 100644 --- a/StyletIntegrationTests/ShowDialogAndDialogResult/DialogView.xaml +++ b/StyletIntegrationTests/ShowDialogAndDialogResult/DialogView.xaml @@ -1,7 +1,7 @@  Choose the desired DialogResult, then close the dialog. diff --git a/StyletIntegrationTests/WindowDisplayNameBound/WindowView.xaml b/StyletIntegrationTests/WindowDisplayNameBound/WindowView.xaml index a8adc7b..f0836f8 100644 --- a/StyletIntegrationTests/WindowDisplayNameBound/WindowView.xaml +++ b/StyletIntegrationTests/WindowDisplayNameBound/WindowView.xaml @@ -1,7 +1,7 @@  Press the button, and verify that the count in the window title increases. When you are done, close the window. diff --git a/StyletIntegrationTests/WindowGuardClose/WindowView.xaml b/StyletIntegrationTests/WindowGuardClose/WindowView.xaml index 3305fe5..6496f2e 100644 --- a/StyletIntegrationTests/WindowGuardClose/WindowView.xaml +++ b/StyletIntegrationTests/WindowGuardClose/WindowView.xaml @@ -1,7 +1,7 @@  Leave the checkbox unchecked, then close the window using the red X at the top. It should not close. diff --git a/StyletUnitTests/BindableCollectionTests.cs b/StyletUnitTests/BindableCollectionTests.cs index f3b0ca7..b603c91 100644 --- a/StyletUnitTests/BindableCollectionTests.cs +++ b/StyletUnitTests/BindableCollectionTests.cs @@ -66,8 +66,7 @@ namespace StyletUnitTests Assert.AreEqual(1, changedEvents.Count); var changedEvent = changedEvents[0]; - Assert.AreEqual(NotifyCollectionChangedAction.Add, changedEvent.Action); - Assert.AreEqual(elementsToAdd, changedEvent.NewItems); + Assert.AreEqual(NotifyCollectionChangedAction.Reset, changedEvent.Action); } [Test] @@ -97,8 +96,7 @@ namespace StyletUnitTests Assert.AreEqual(1, changedEvents.Count); var changedEvent = changedEvents[0]; - Assert.AreEqual(NotifyCollectionChangedAction.Add, changedEvent.Action); - Assert.AreEqual(itemsToRemove, changedEvent.NewItems); + Assert.AreEqual(NotifyCollectionChangedAction.Reset, changedEvent.Action); } [Test] diff --git a/StyletUnitTests/PropertyChangedExtensionsTests.cs b/StyletUnitTests/PropertyChangedExtensionsTests.cs index a0f629f..85413cb 100644 --- a/StyletUnitTests/PropertyChangedExtensionsTests.cs +++ b/StyletUnitTests/PropertyChangedExtensionsTests.cs @@ -38,13 +38,13 @@ namespace StyletUnitTests public string LastFoo; private WeakEventManager weakEventManager = new WeakEventManager(); - public IPropertyChangedBinding BindStrong(NotifyingClass notifying) + public IEventBinding BindStrong(NotifyingClass notifying) { // Must make sure the compiler doesn't generate an inner class for this, otherwise we're not testing the right thing return notifying.Bind(x => x.Foo, x => this.LastFoo = x); } - public IPropertyChangedBinding BindWeak(NotifyingClass notifying) + public IEventBinding BindWeak(NotifyingClass notifying) { return this.weakEventManager.BindWeak(notifying, x => x.Foo, x => this.LastFoo = x); } @@ -84,22 +84,6 @@ namespace StyletUnitTests Assert.AreEqual("bar", newVal); } - [Test] - public void StrongBindingRetainsBindingClass() - { - var binding = new BindingClass(); - - // Means of determining whether the class has been disposed - var weakBinding = new WeakReference(binding); - - var notifying = new NotifyingClass(); - binding.BindStrong(notifying); - - binding = null; - GC.Collect(); - Assert.IsTrue(weakBinding.TryGetTarget(out binding)); - } - [Test] public void StrongBindingDoesNotRetainNotifier() { @@ -180,23 +164,6 @@ namespace StyletUnitTests Assert.IsFalse(weakBinding.TryGetTarget(out binding)); } - [Test] - public void WeakBindingRetainsClassIfIPropertyChangedBindingRetained() - { - var binding = new BindingClass(); - - // Means of determining whether the class has been disposed - var weakBinding = new WeakReference(binding); - - var notifying = new NotifyingClass(); - // Retain this - var binder = binding.BindWeak(notifying); - - binding = null; - GC.Collect(); - Assert.IsTrue(weakBinding.TryGetTarget(out binding)); - } - [Test] public void WeakBindingDoesNotRetainNotifier() {