Permalink
Browse files

Merge branch 'master' into 3.0.0

  • Loading branch information...
2 parents 55b0cf2 + 88a7c60 commit 2d8aaeb7c9dab3336a319ef1bbc4d3f2e74fbd46 @nigel-sampson nigel-sampson committed Nov 30, 2015
View
@@ -12,8 +12,4 @@ Caliburn.Micro is a small, yet powerful framework, designed for building applica
## Sponsoring
-This community project is sponsored by [Xceed](http://xceed.com/), makers of Xceed DataGrid for WPF. 50% off any Xceed product with coupon code G00B8T.
-
The Caliburn.Micro team uses ReSharper by [JetBrains](http://www.jetbrains.com/).
-
-If you like what you find here, please consider [donating](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=VZURNT9MCX3CS).
@@ -1,11 +1,13 @@
using System;
+using System.Threading.Tasks;
namespace Caliburn.Micro.WinRT.Sample.ViewModels
{
public class ActionsViewModel : ViewModelBase
{
- private string input;
- private string output;
+ string input;
+ string input2;
+ string output;
public ActionsViewModel(INavigationService navigationService)
: base(navigationService)
@@ -24,33 +26,41 @@ public string Output
}
}
- public string Input
+ public void SimpleSayHello()
{
- get
- {
- return input;
- }
- set
- {
- this.Set(ref input, value);
- }
+ Output = "Hello from Caliburn.Micro";
}
- public void SimpleSayHello()
+ public async Task AsyncSayHelloAsync()
{
- Output = "Hello from Caliburn.Micro";
+ await Task.Delay(0);
+
+ Output = "Hello from Caliburn.Micro (async)";
}
public void SayHello(string name)
{
- Output = String.Format("Hello {0} from Caliburn.Micro", Input);
+ Output = String.Format("Hello {0} from Caliburn.Micro", name);
}
public bool CanSayHello(string name)
{
return !String.IsNullOrEmpty(name);
}
+ public async Task SayHello2Async(string name)
+ {
+ await Task.Delay(0);
+
+ Output = String.Format("Hello {0} from Caliburn.Micro (async)", name);
+ }
+
+ // Notice that the guard method is sync and is missing the Async suffix.
+ public bool CanSayHello2(string name)
+ {
+ return !String.IsNullOrEmpty(name);
+ }
+
public void AppBarHello()
{
Output = "Hello from the App Bar.";
@@ -48,10 +48,15 @@
<TextBlock x:Name="Output" Style="{StaticResource ItemTextStyle}"/>
</StackPanel>
<Button x:Name="SimpleSayHello" Content="Simple Say Hello" Margin="-3,0,0,0" />
- <StackPanel Orientation="Horizontal" Margin="0,20">
+ <Button x:Name="AsyncSayHello" Content="Simple Say Hello (async)" Margin="-3,0,0,0" />
+ <StackPanel Margin="0,10" Orientation="Horizontal">
<TextBox x:Name="Input" Width="150" />
<Button Content="Say Hello with Parameter" caliburn:Message.Attach="SayHello(Input.Text)"/>
</StackPanel>
+ <StackPanel Margin="0,20" Orientation="Horizontal">
+ <TextBox x:Name="AsyncInput" Width="150" />
+ <Button Content="Say Hello with Parameter (async)" caliburn:Message.Attach="SayHello2Async(AsyncInput.Text)" />
+ </StackPanel>
<Rectangle Fill="{StaticResource MetroOrangeBrush}" Width="100" Height="100" HorizontalAlignment="Left"
caliburn:Message.Attach="[Event DoubleTapped] = [Action SimpleSayHello]"/>
</StackPanel>
@@ -439,32 +439,40 @@ void ElementUnloaded(object sender, RoutedEventArgs e)
/// </summary>
public static Action<ActionExecutionContext> PrepareContext = context => {
SetMethodBinding(context);
- if (context.Target == null || context.Method == null) {
+ if (context.Target == null || context.Method == null)
+ {
return;
}
+ var possibleGuardNames = BuildPossibleGuardNames(context).ToList();
- var guardName = "Can" + context.Method.Name;
- var targetType = context.Target.GetType();
- var guard = TryFindGuardMethod(context);
+ var guard = TryFindGuardMethod(context, possibleGuardNames);
- if (guard == null) {
+ if (guard == null)
+ {
var inpc = context.Target as INotifyPropertyChanged;
if (inpc == null)
return;
-#if WinRT
- guard = targetType.GetRuntimeMethods().SingleOrDefault(m => m.Name == "get_" + guardName);
-#else
- guard = targetType.GetMethod("get_" + guardName);
-#endif
+
+ var targetType = context.Target.GetType();
+ string matchingGuardName = null;
+ foreach (string possibleGuardName in possibleGuardNames)
+ {
+ matchingGuardName = possibleGuardName;
+ guard = GetMethodInfo(targetType, "get_" + matchingGuardName);
+ if (guard != null) break;
+ }
+
if (guard == null)
return;
PropertyChangedEventHandler handler = null;
handler = (s, e) => {
- if (string.IsNullOrEmpty(e.PropertyName) || e.PropertyName == guardName) {
+ if (string.IsNullOrEmpty(e.PropertyName) || e.PropertyName == matchingGuardName)
+ {
Caliburn.Micro.Execute.OnUIThread(() => {
var message = context.Message;
- if (message == null) {
+ if (message == null)
+ {
inpc.PropertyChanged -= handler;
return;
}
@@ -480,24 +488,27 @@ void ElementUnloaded(object sender, RoutedEventArgs e)
context.CanExecute = () => (bool)guard.Invoke(
context.Target,
- MessageBinder.DetermineParameters(context, guard.GetParameters())
- );
+ MessageBinder.DetermineParameters(context, guard.GetParameters()));
};
/// <summary>
- /// Try to find a candidate for guard function, having:
- /// - a name in the form "CanXXX"
- /// - no generic parameters
- /// - a bool return type
- /// - no parameters or a set of parameters corresponding to the action method
+ /// Try to find a candidate for guard function, having:
+ /// - a name matching any of <paramref name="possibleGuardNames"/>
+ /// - no generic parameters
+ /// - a bool return type
+ /// - no parameters or a set of parameters corresponding to the action method
/// </summary>
/// <param name="context">The execution context</param>
- /// <returns>A MethodInfo, if found; null otherwise</returns>
- static MethodInfo TryFindGuardMethod(ActionExecutionContext context) {
-#if WinRT
- var guardName = "Can" + context.Method.Name;
+ /// <param name="possibleGuardNames">Method names to look for.</param>
+ ///<returns>A MethodInfo, if found; null otherwise</returns>
+ static MethodInfo TryFindGuardMethod(ActionExecutionContext context, IEnumerable<string> possibleGuardNames) {
var targetType = context.Target.GetType();
- var guard = targetType.GetRuntimeMethods().SingleOrDefault(m => m.Name == guardName);
+ MethodInfo guard = null;
+ foreach (string possibleGuardName in possibleGuardNames)
+ {
+ guard = GetMethodInfo(targetType, possibleGuardName);
+ if (guard != null) break;
+ }
if (guard == null) return null;
if (guard.ContainsGenericParameters) return null;
@@ -510,38 +521,37 @@ void ElementUnloaded(object sender, RoutedEventArgs e)
var comparisons = guardPars.Zip(
context.Method.GetParameters(),
- (x, y) => x.ParameterType.Equals(y.ParameterType)
+ (x, y) => x.ParameterType == y.ParameterType
);
- if (comparisons.Any(x => !x)) {
+ if (comparisons.Any(x => !x))
+ {
return null;
}
return guard;
-#else
- var guardName = "Can" + context.Method.Name;
- var targetType = context.Target.GetType();
- var guard = targetType.GetMethod(guardName);
+ }
- if (guard ==null) return null;
- if (guard.ContainsGenericParameters) return null;
- if (typeof(bool) != guard.ReturnType) return null;
+ static IEnumerable<string> BuildPossibleGuardNames(ActionExecutionContext context) {
- var guardPars = guard.GetParameters();
- var actionPars = context.Method.GetParameters();
- if (guardPars.Length == 0) return guard;
- if (guardPars.Length != actionPars.Length) return null;
+ const string GuardPrefix = "Can";
- var comparisons = guardPars.Zip(
- context.Method.GetParameters(),
- (x, y) => x.ParameterType == y.ParameterType
- );
+ var methodName = context.Method.Name;
+ yield return GuardPrefix + methodName;
- if (comparisons.Any(x => !x)) {
- return null;
- }
+ const string AsyncMethodSuffix = "Async";
+ if (methodName.EndsWith(AsyncMethodSuffix, StringComparison.OrdinalIgnoreCase))
+ {
+ yield return GuardPrefix + methodName.Substring(0, methodName.Length - AsyncMethodSuffix.Length);
+ }
+ }
- return guard;
+ static MethodInfo GetMethodInfo(Type t, string methodName)
+ {
+#if WinRT
+ return t.GetRuntimeMethods().SingleOrDefault(m => m.Name == methodName);
+#else
+ return t.GetMethod(methodName);
#endif
}
}
@@ -8,8 +8,15 @@
/// Class for managing the list of rules for doing name transformation.
/// </summary>
public class NameTransformer : BindableCollection<NameTransformer.Rule> {
+
+#if NET
+ private const RegexOptions options = RegexOptions.Compiled;
+#else
+ private const RegexOptions options = RegexOptions.None;
+#endif
+
bool useEagerRuleSelection = true;
-
+
/// <summary>
/// Flag to indicate if transformations from all matched rules are returned. Otherwise, transformations from only the first matched rule are returned.
/// </summary>
@@ -62,18 +69,18 @@ public class NameTransformer : BindableCollection<NameTransformer.Rule> {
var rules = this.Reverse();
foreach(var rule in rules) {
- if(!string.IsNullOrEmpty(rule.GlobalFilterPattern) && !Regex.IsMatch(source, rule.GlobalFilterPattern)) {
+ if(!string.IsNullOrEmpty(rule.GlobalFilterPattern) && !rule.GlobalFilterPatternRegex.IsMatch(source)) {
continue;
}
- if(!Regex.IsMatch(source, rule.ReplacePattern)) {
+ if(!rule.ReplacePatternRegex.IsMatch(source)) {
continue;
}
nameList.AddRange(
rule.ReplacementValues
.Select(getReplaceString)
- .Select(repString => Regex.Replace(source, rule.ReplacePattern, repString))
+ .Select(repString => rule.ReplacePatternRegex.Replace(source, repString))
);
if (!useEagerRuleSelection) {
@@ -88,6 +95,9 @@ public class NameTransformer : BindableCollection<NameTransformer.Rule> {
/// A rule that describes a name transform.
///</summary>
public class Rule {
+ private Regex replacePatternRegex;
+ private Regex globalFilterPatternRegex;
+
/// <summary>
/// Regular expression pattern for global filtering
/// </summary>
@@ -102,6 +112,24 @@ public class Rule {
/// The list of replacement values
/// </summary>
public IEnumerable<string> ReplacementValues;
+
+ /// <summary>
+ /// Regular expression for global filtering
+ /// </summary>
+ public Regex GlobalFilterPatternRegex {
+ get {
+ return globalFilterPatternRegex ?? (globalFilterPatternRegex = new Regex(GlobalFilterPattern, options));
+ }
+ }
+
+ /// <summary>
+ /// Regular expression for replacing text
+ /// </summary>
+ public Regex ReplacePatternRegex {
+ get {
+ return replacePatternRegex ?? (replacePatternRegex = new Regex(ReplacePattern, options));
+ }
+ }
}
}
}
@@ -8,6 +8,7 @@ namespace Caliburn.Micro
using System.Linq;
using System.Collections.Generic;
using System.Reflection;
+ using System.Threading.Tasks;
#if XFORMS
using UIElement = global::Xamarin.Forms.Element;
using FrameworkElement = global::Xamarin.Forms.VisualElement;
@@ -29,6 +30,8 @@ namespace Caliburn.Micro
/// Binds a view to a view model.
/// </summary>
public static class ViewModelBinder {
+ const string AsyncSuffix = "Async";
+
static readonly ILog Log = LogManager.GetLog(typeof(ViewModelBinder));
/// <summary>
@@ -131,7 +134,12 @@ public static class ViewModelBinder {
foreach (var method in methods) {
var foundControl = unmatchedElements.FindName(method.Name);
- if (foundControl == null) {
+ if (foundControl == null && IsAsyncMethod(method)) {
+ var methodNameWithoutAsyncSuffix = method.Name.Substring(0, method.Name.Length - AsyncSuffix.Length);
+ foundControl = unmatchedElements.FindName(methodNameWithoutAsyncSuffix);
+ }
+
+ if(foundControl == null) {
Log.Info("Action Convention Not Applied: No actionable element for {0}.", method.Name);
continue;
}
@@ -174,6 +182,11 @@ public static class ViewModelBinder {
return unmatchedElements;
};
+ static bool IsAsyncMethod(MethodInfo method) {
+ return typeof(Task).IsAssignableFrom(method.ReturnType) &&
+ method.Name.EndsWith(AsyncSuffix, StringComparison.OrdinalIgnoreCase);
+ }
+
/// <summary>
/// Allows the developer to add custom handling of named elements which were not matched by any default conventions.
/// </summary>
@@ -75,6 +75,11 @@ public Windows.UI.Color BackgroundColor
private static Windows.UI.Color ToColor(string hexValue)
{
+ // if 'transparent' is entered in the app manifest, return Windows.UI.Colors.Transparent
+ // in order to prevent parsing failures
+ if (String.Equals(hexValue, "transparent", StringComparison.OrdinalIgnoreCase))
+ return Windows.UI.Colors.Transparent;
+
hexValue = hexValue.Replace("#", string.Empty);
// some loose validation (not bullet-proof)
@@ -104,4 +109,4 @@ private static Windows.UI.Color ToColor(string hexValue)
return Windows.UI.Color.FromArgb(a, r, g, b);
}
}
-}
+}
Oops, something went wrong.

0 comments on commit 2d8aaeb

Please sign in to comment.