Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

alpha 0.1

  • Loading branch information...
commit bede1ad98d2fa75f07cf3362e0f17b61aaca77f9 1 parent 697fcbb
@RobertKozak authored
Showing with 16,334 additions and 0 deletions.
  1. +51 −0 Application/DisposableObject.cs
  2. +154 −0 Application/PropertyNotifier.cs
  3. +3,109 −0 Application/TinyIoC.cs
  4. +792 −0 Application/TinyMessenger.cs
  5. +61 −0 Binding/Binding.cs
  6. +44 −0 Binding/BindingBase.cs
  7. +186 −0 Binding/BindingExpression.cs
  8. +41 −0 Binding/BindingMode.cs
  9. +215 −0 Binding/BindingOperations.cs
  10. +42 −0 Binding/IBindable.cs
  11. +45 −0 Binding/IBindingExpression.cs
  12. +41 −0 Binding/IValueConverter.cs
  13. +15 −0 Binding/PropertyPath.cs
  14. +40 −0 Binding/TypeExtensions.cs
  15. +47 −0 ChangeLog
  16. +69 −0 Collections/EnumBinder.cs
  17. +116 −0 Collections/EnumCollection.cs
  18. +100 −0 Collections/System.Collections.ObjectModel/ObservableCollection.cs
  19. +35 −0 Collections/System.Collections.Specialized/INotifyCollectionChanged.cs
  20. +37 −0 Collections/System.Collections.Specialized/NotifyCollectionChangedAction.cs
  21. +102 −0 Collections/System.Collections.Specialized/NotifyCollectionChangedEventArgs.cs
  22. +31 −0 Collections/System.Collections.Specialized/NotifyCollectionChangedEventHandler.cs
  23. +42 −0 Commands/ICommand.cs
  24. +86 −0 Commands/ReflectiveCommand.cs
  25. +74 −0 Commands/UICommand.cs
  26. +64 −0 Converters/EnumConverter.cs
  27. +91 −0 Converters/EnumItemsConverter.cs
  28. +50 −0 Converters/EnumerableConverter.cs
  29. +52 −0 Converters/MoneyConverter.cs
  30. +53 −0 Converters/PercentConverter.cs
  31. +58 −0 Converters/ViewConverter.cs
  32. +667 −0 Dialog/DialogViewController.cs
  33. +42 −0 Dialog/Elements/ActivityElement.cs
  34. +128 −0 Dialog/Elements/BaseBooleanImageElement.cs
  35. +48 −0 Dialog/Elements/BoolElement.cs
  36. +86 −0 Dialog/Elements/BooleanElement.cs
  37. +59 −0 Dialog/Elements/BooleanImageElement.cs
  38. +91 −0 Dialog/Elements/ButtonElement.cs
  39. +79 −0 Dialog/Elements/CheckboxElement.cs
  40. +60 −0 Dialog/Elements/DateElement.cs
  41. +172 −0 Dialog/Elements/DateTimeElement.cs
  42. +324 −0 Dialog/Elements/Element.cs
  43. +168 −0 Dialog/Elements/ElementBadge.cs
  44. +283 −0 Dialog/Elements/EntryElement.cs
  45. +96 −0 Dialog/Elements/FloatElement.cs
  46. +140 −0 Dialog/Elements/HtmlElement.cs
  47. +229 −0 Dialog/Elements/ImageElement.cs
  48. +81 −0 Dialog/Elements/ImageStringElement.cs
  49. +29 −0 Dialog/Elements/ListBoxElement.cs
  50. +184 −0 Dialog/Elements/LoadMoreElement.cs
  51. +160 −0 Dialog/Elements/MapViewElement.cs
  52. +61 −0 Dialog/Elements/MultilineElement.cs
  53. +111 −0 Dialog/Elements/OwnerDrawnElement.cs
  54. +113 −0 Dialog/Elements/RadioElement.cs
  55. +639 −0 Dialog/Elements/RootElement.cs
  56. +427 −0 Dialog/Elements/Section.cs
  57. +102 −0 Dialog/Elements/StringElement.cs
  58. +53 −0 Dialog/Elements/StyledMultilineElement.cs
  59. +76 −0 Dialog/Elements/StyledStringElement.cs
  60. +58 −0 Dialog/Elements/TimeElement.cs
  61. +111 −0 Dialog/Elements/UIViewElement.cs
  62. +536 −0 Dialog/Elements/ViewElement.cs
  63. +47 −0 Dialog/Group.cs
  64. +40 −0 Dialog/IElementSizing.cs
  65. +59 −0 Dialog/IRoot.cs
  66. +39 −0 Dialog/ITappable.cs
  67. +53 −0 Dialog/RadioGroup.cs
  68. +44 −0 Dialog/Reflection API/AlignmentAttribute.cs
  69. +108 −0 Dialog/Reflection API/BindAttribute.cs
  70. +635 −0 Dialog/Reflection API/BindingContext.cs
  71. +58 −0 Dialog/Reflection API/ButtonAttribute.cs
  72. +43 −0 Dialog/Reflection API/CaptionAttribute.cs
  73. +38 −0 Dialog/Reflection API/CheckboxAttribute.cs
  74. +70 −0 Dialog/Reflection API/Color.cs
  75. +38 −0 Dialog/Reflection API/DateAttribute.cs
  76. +17 −0 Dialog/Reflection API/EditButtonAttribute.cs
  77. +50 −0 Dialog/Reflection API/EntryAttribute.cs
  78. +38 −0 Dialog/Reflection API/HtmlAttribute.cs
  79. +39 −0 Dialog/Reflection API/InlineAttribute.cs
  80. +39 −0 Dialog/Reflection API/ListAttribute.cs
  81. +17 −0 Dialog/Reflection API/MapAttribute.cs
  82. +38 −0 Dialog/Reflection API/MulitlineAttribute.cs
  83. +45 −0 Dialog/Reflection API/OrderAttribute.cs
  84. +41 −0 Dialog/Reflection API/PasswordAttribute.cs
  85. +38 −0 Dialog/Reflection API/PopOnSelectionAttribute.cs
  86. +44 −0 Dialog/Reflection API/RangeAttribute.cs
  87. +47 −0 Dialog/Reflection API/RootAttribute.cs
  88. +55 −0 Dialog/Reflection API/SectionAttribute.cs
  89. +38 −0 Dialog/Reflection API/SkipAttribute.cs
  90. +38 −0 Dialog/Reflection API/TimeAttribute.cs
  91. +17 −0 Dialog/Reflection API/ToolbarButton.cs
  92. +199 −0 Dialog/RefreshTableHeaderView.cs
  93. +75 −0 Dialog/UITableViewElementCell.cs
  94. +20 −0 EnumValueConverter.cs
  95. +66 −0 Extensions/EnumExtensions.cs
  96. +138 −0 Extensions/ExpressionExtensions.cs
  97. +63 −0 Extensions/TypeExtensions.cs
  98. BIN  Images/arrow.png
  99. +11 −0 Model/IModel.cs
  100. +16 −0 Model/Model.cs
  101. BIN  MonoTouch.Dialog.pidb
  102. +219 −0 MonoTouch.MVVM.csproj
  103. BIN  MonoTouch.MVVM.pidb
  104. +50 −0 MonoTouch.MVVM.sln
  105. +62 −0 MonoTouch.MVVM.userprefs
  106. +164 −0 README.markdown
  107. +39 −0 Samples/AppDelegateIPad.cs
  108. +38 −0 Samples/AppDelegateIPhone.cs
  109. +27 −0 Samples/Converters/CurrentMovieConverter.cs
  110. +27 −0 Samples/Converters/FontConverter.cs
  111. +9 −0 Samples/Converters/MovieViewModelConverter.cs
  112. +13 −0 Samples/Info.plist
  113. +14 −0 Samples/Main.cs
  114. +37 −0 Samples/MainMenu.cs
  115. +482 −0 Samples/MainWindowIPad.xib
  116. +33 −0 Samples/MainWindowIPad.xib.designer.cs
  117. +303 −0 Samples/MainWindowIPhone.xib
  118. +47 −0 Samples/MainWindowIPhone.xib.designer.cs
  119. +30 −0 Samples/Models/MovieDataModel.cs
  120. +119 −0 Samples/Samples.csproj
  121. BIN  Samples/Samples.pidb
  122. +38 −0 Samples/ViewModels/AddressViewModel.cs
  123. +26 −0 Samples/ViewModels/InterestingViewModel.cs
  124. +92 −0 Samples/ViewModels/MovieViewModel.cs
  125. +50 −0 Samples/Views/AddressView.cs
  126. +60 −0 Samples/Views/InterestingView.cs
  127. +63 −0 Samples/Views/MovieElement.cs
  128. +77 −0 Samples/Views/MovieListView.cs
  129. +84 −0 Samples/Views/MovieView.cs
  130. +33 −0 Samples/Views/View1.cs
  131. +45 −0 View/IView.cs
  132. +152 −0 View/View.cs
  133. +40 −0 ViewModel/IViewModel.cs
  134. +44 −0 ViewModel/ViewModel.cs
View
51 Application/DisposableObject.cs
@@ -0,0 +1,51 @@
+//
+// DisposableObject.cs:
+//
+// Author:
+// Robert Kozak (rkozak@gmail.com)
+//
+// Copyright 2011, Nowcom Corporation
+//
+// Code licensed under the MIT X11 license
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+namespace MonoTouch.MVVM
+{
+ using System;
+
+ public abstract class DisposableObject : IDisposable
+ {
+ ~DisposableObject()
+ {
+ Dispose(false);
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool calledExplicitly)
+ {
+ }
+ }
+}
View
154 Application/PropertyNotifier.cs
@@ -0,0 +1,154 @@
+//
+// PropertyNotifier.cs:
+//
+// Author:
+// Robert Kozak (rkozak@gmail.com)
+//
+// Copyright 2011, Nowcom Corporation
+//
+// Code licensed under the MIT X11 license
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+namespace MonoTouch.MVVM
+{
+ using System;
+ using System.Collections.Generic;
+ using System.ComponentModel;
+ using System.Linq.Expressions;
+ using MonoTouch.Dialog;
+
+ public class PropertyNotifier : DisposableObject, INotifyPropertyChanged
+ {
+ private IDictionary<string, object> _PropertyMap = new Dictionary<string, object>();
+
+ public event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };
+
+ protected IDictionary<string, object> PropertyMap { get { return _PropertyMap; } set { _PropertyMap = value; } }
+
+ public PropertyNotifier()
+ {
+ }
+
+ public void NotifyPropertyChanged(string propertyName)
+ {
+ PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+ public void NotifyPropertyChanged<T>(Expression<Func<T>> property)
+ {
+ NotifyPropertyChanged(property.PropertyName());
+ }
+
+ public T Get<T>(Expression<Func<T>> property)
+ {
+ return Get(property, default(T));
+ }
+
+ public T Get<T>(Expression<Func<T>> property, T defaultValue)
+ {
+ return Get(property, () => defaultValue);
+ }
+
+ public T Get<T>(Expression<Func<T>> property, Func<T> defaultValue)
+ {
+ if (property == null)
+ {
+ throw new ArgumentException("cannot be null", "property");
+ }
+
+ var name = property.PropertyName();
+ var isFunction = name.Contains(".") || string.IsNullOrEmpty(name);
+
+ // Either property is of form: ()=>Foo.Bar or property is a function: ()=>CalculateValue();
+ if (isFunction)
+ {
+ object source = this;
+ var propertyInfo = this.GetType ().GetNestedProperty (ref source, name, false);
+ if (source != this) {
+ return (T)propertyInfo.GetValue (source, new object[] { });
+ }
+ }
+
+
+ if (_PropertyMap.ContainsKey(name))
+ return (T)_PropertyMap[name];
+
+ if (defaultValue != null)
+ {
+ T result = defaultValue.Invoke();
+ if (!isFunction)
+ {
+ Set(property, result);
+ }
+
+ return result;
+ }
+
+ return default(T);
+ }
+
+ public void Set<T>(Expression<Func<T>> property, T value)
+ {
+ if (property == null)
+ {
+ throw new ArgumentException("cannot be null", "property");
+ }
+
+ var name = property.PropertyName();
+
+ T oldValue = default(T);
+ if(name.Contains("."))
+ {
+ object source = this;
+ var propertyInfo = this.GetType().GetNestedProperty(ref source, name, false);
+ if (source != this)
+ {
+ propertyInfo.SetValue(source, value, new object[] { });
+ name = propertyInfo.Name;
+ }
+ }
+ else
+ {
+ object tryValue = null;
+
+ if (_PropertyMap.TryGetValue(name, out tryValue))
+ {
+ oldValue = (T)tryValue;
+
+ if (oldValue == null && value == null)
+ return;
+
+ if (oldValue != null && oldValue.Equals(value))
+ return;
+
+ _PropertyMap[name] = value;
+ }
+ else
+ {
+ _PropertyMap.Add(name, value);
+ }
+ }
+
+ NotifyPropertyChanged(name);
+ }
+ }
+}
+
View
3,109 Application/TinyIoC.cs
3,109 additions, 0 deletions not shown
View
792 Application/TinyMessenger.cs
@@ -0,0 +1,792 @@
+//===============================================================================
+// TinyIoC - TinyMessenger
+//
+// A simple messenger/event aggregator.
+//
+// http://hg.grumpydev.com/tinyioc
+//===============================================================================
+// Copyright © Steven Robbins. All rights reserved.
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
+// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+// FITNESS FOR A PARTICULAR PURPOSE.
+//===============================================================================
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace TinyMessenger
+{
+ #region Message Types / Interfaces
+ /// <summary>
+ /// A TinyMessage to be published/delivered by TinyMessenger
+ /// </summary>
+ public interface ITinyMessage
+ {
+ /// <summary>
+ /// The sender of the message, or null if not supported by the message implementation.
+ /// </summary>
+ object Sender { get; }
+ }
+
+ /// <summary>
+ /// Base class for messages that provides weak refrence storage of the sender
+ /// </summary>
+ public abstract class TinyMessageBase : ITinyMessage
+ {
+ /// <summary>
+ /// Store a WeakReference to the sender just in case anyone is daft enough to
+ /// keep the message around and prevent the sender from being collected.
+ /// </summary>
+ private WeakReference _Sender;
+ public object Sender
+ {
+ get
+ {
+ return (_Sender == null) ? null : _Sender.Target;
+ }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the MessageBase class.
+ /// </summary>
+ /// <param name="sender">Message sender (usually "this")</param>
+ public TinyMessageBase(object sender)
+ {
+ if (sender == null)
+ throw new ArgumentNullException("sender");
+
+ _Sender = new WeakReference(sender);
+ }
+ }
+
+ /// <summary>
+ /// Generic message with user specified content
+ /// </summary>
+ /// <typeparam name="TContent">Content type to store</typeparam>
+ public class GenericTinyMessage<TContent> : TinyMessageBase
+ {
+ /// <summary>
+ /// Contents of the message
+ /// </summary>
+ public TContent Content { get; protected set; }
+
+ /// <summary>
+ /// Create a new instance of the GenericTinyMessage class.
+ /// </summary>
+ /// <param name="sender">Message sender (usually "this")</param>
+ /// <param name="content">Contents of the message</param>
+ public GenericTinyMessage(object sender, TContent content)
+ : base(sender)
+ {
+ Content = content;
+ }
+ }
+
+ /// <summary>
+ /// Basic "cancellable" generic message
+ /// </summary>
+ /// <typeparam name="TContent">Content type to store</typeparam>
+ public class CancellableGenericTinyMessage<TContent> : TinyMessageBase
+ {
+ /// <summary>
+ /// Cancel action
+ /// </summary>
+ public Action Cancel { get; protected set; }
+
+ /// <summary>
+ /// Contents of the message
+ /// </summary>
+ public TContent Content { get; protected set; }
+
+ /// <summary>
+ /// Create a new instance of the CancellableGenericTinyMessage class.
+ /// </summary>
+ /// <param name="sender">Message sender (usually "this")</param>
+ /// <param name="content">Contents of the message</param>
+ /// <param name="cancelAction">Action to call for cancellation</param>
+ public CancellableGenericTinyMessage(object sender, TContent content, Action cancelAction)
+ : base(sender)
+ {
+ if (cancelAction == null)
+ throw new ArgumentNullException("cancelAction");
+
+ Content = content;
+ Cancel = cancelAction;
+ }
+ }
+
+ /// <summary>
+ /// Represents an active subscription to a message
+ /// </summary>
+ public sealed class TinyMessageSubscriptionToken : IDisposable
+ {
+ private WeakReference _Hub;
+ private Type _MessageType;
+
+ /// <summary>
+ /// Initializes a new instance of the TinyMessageSubscriptionToken class.
+ /// </summary>
+ public TinyMessageSubscriptionToken(ITinyMessengerHub hub, Type messageType)
+ {
+ if (hub == null)
+ throw new ArgumentNullException("hub");
+
+ if (!typeof(ITinyMessage).IsAssignableFrom(messageType))
+ throw new ArgumentOutOfRangeException("messageType");
+
+ _Hub = new WeakReference(hub);
+ _MessageType = messageType;
+ }
+
+ public void Dispose()
+ {
+ if (_Hub.IsAlive)
+ {
+ var hub = _Hub.Target as ITinyMessengerHub;
+
+ if (hub != null)
+ {
+ var unsubscribeMethod = typeof(ITinyMessengerHub).GetMethod("Unsubscribe", new Type[] {typeof(TinyMessageSubscriptionToken)});
+ unsubscribeMethod = unsubscribeMethod.MakeGenericMethod(_MessageType);
+ unsubscribeMethod.Invoke(hub, new object[] {this});
+ }
+ }
+
+ GC.SuppressFinalize(this);
+ }
+ }
+
+ /// <summary>
+ /// Represents a message subscription
+ /// </summary>
+ public interface ITinyMessageSubscription
+ {
+ /// <summary>
+ /// Token returned to the subscribed to reference this subscription
+ /// </summary>
+ TinyMessageSubscriptionToken SubscriptionToken { get; }
+
+ /// <summary>
+ /// Whether delivery should be attempted.
+ /// </summary>
+ /// <param name="message">Message that may potentially be delivered.</param>
+ /// <returns>True - ok to send, False - should not attempt to send</returns>
+ bool ShouldAttemptDelivery(ITinyMessage message);
+
+ /// <summary>
+ /// Deliver the message
+ /// </summary>
+ /// <param name="message">Message to deliver</param>
+ void Deliver(ITinyMessage message);
+ }
+
+ /// <summary>
+ /// Message proxy definition.
+ ///
+ /// A message proxy can be used to intercept/alter messages and/or
+ /// marshall delivery actions onto a particular thread.
+ /// </summary>
+ public interface ITinyMessageProxy
+ {
+ void Deliver(ITinyMessage message, ITinyMessageSubscription subscription);
+ }
+
+ /// <summary>
+ /// Default "pass through" proxy.
+ ///
+ /// Does nothing other than deliver the message.
+ /// </summary>
+ public sealed class DefaultTinyMessageProxy : ITinyMessageProxy
+ {
+ private static readonly DefaultTinyMessageProxy _Instance = new DefaultTinyMessageProxy();
+
+ static DefaultTinyMessageProxy()
+ {
+ }
+
+ /// <summary>
+ /// Singleton instance of the proxy.
+ /// </summary>
+ public static DefaultTinyMessageProxy Instance
+ {
+ get
+ {
+ return _Instance;
+ }
+ }
+
+ private DefaultTinyMessageProxy()
+ {
+ }
+
+ public void Deliver(ITinyMessage message, ITinyMessageSubscription subscription)
+ {
+ subscription.Deliver(message);
+ }
+ }
+ #endregion
+
+ #region Exceptions
+ /// <summary>
+ /// Thrown when an exceptions occurs while subscribing to a message type
+ /// </summary>
+ public class TinyMessengerSubscriptionException : Exception
+ {
+ private const string ERROR_TEXT = "Unable to add subscription for {0} : {1}";
+
+ public TinyMessengerSubscriptionException(Type messageType, string reason)
+ : base(String.Format(ERROR_TEXT, messageType, reason))
+ {
+
+ }
+
+ public TinyMessengerSubscriptionException(Type messageType, string reason, Exception innerException)
+ : base(String.Format(ERROR_TEXT, messageType, reason), innerException)
+ {
+
+ }
+ }
+ #endregion
+
+ #region Hub Interface
+ /// <summary>
+ /// Messenger hub responsible for taking subscriptions/publications and delivering of messages.
+ /// </summary>
+ public interface ITinyMessengerHub
+ {
+ /// <summary>
+ /// Subscribe to a message type with the given destination and delivery action.
+ /// All references are held with WeakReferences
+ ///
+ /// All messages of this type will be delivered.
+ /// </summary>
+ /// <typeparam name="TMessage">Type of message</typeparam>
+ /// <param name="deliveryAction">Action to invoke when message is delivered</param>
+ /// <returns>TinyMessageSubscription used to unsubscribing</returns>
+ TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction) where TMessage : class, ITinyMessage;
+
+ /// <summary>
+ /// Subscribe to a message type with the given destination and delivery action.
+ /// Messages will be delivered via the specified proxy.
+ /// All references (apart from the proxy) are held with WeakReferences
+ ///
+ /// All messages of this type will be delivered.
+ /// </summary>
+ /// <typeparam name="TMessage">Type of message</typeparam>
+ /// <param name="deliveryAction">Action to invoke when message is delivered</param>
+ /// <param name="proxy">Proxy to use when delivering the messages</param>
+ /// <returns>TinyMessageSubscription used to unsubscribing</returns>
+ TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction, ITinyMessageProxy proxy) where TMessage : class, ITinyMessage;
+
+ /// <summary>
+ /// Subscribe to a message type with the given destination and delivery action.
+ ///
+ /// All messages of this type will be delivered.
+ /// </summary>
+ /// <typeparam name="TMessage">Type of message</typeparam>
+ /// <param name="deliveryAction">Action to invoke when message is delivered</param>
+ /// <param name="useStrongReferences">Use strong references to destination and deliveryAction </param>
+ /// <returns>TinyMessageSubscription used to unsubscribing</returns>
+ TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction, bool useStrongReferences) where TMessage : class, ITinyMessage;
+
+ /// <summary>
+ /// Subscribe to a message type with the given destination and delivery action.
+ /// Messages will be delivered via the specified proxy.
+ ///
+ /// All messages of this type will be delivered.
+ /// </summary>
+ /// <typeparam name="TMessage">Type of message</typeparam>
+ /// <param name="deliveryAction">Action to invoke when message is delivered</param>
+ /// <param name="useStrongReferences">Use strong references to destination and deliveryAction </param>
+ /// <param name="proxy">Proxy to use when delivering the messages</param>
+ /// <returns>TinyMessageSubscription used to unsubscribing</returns>
+ TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction, bool useStrongReferences, ITinyMessageProxy proxy) where TMessage : class, ITinyMessage;
+
+ /// <summary>
+ /// Subscribe to a message type with the given destination and delivery action with the given filter.
+ /// All references are held with WeakReferences
+ ///
+ /// Only messages that "pass" the filter will be delivered.
+ /// </summary>
+ /// <typeparam name="TMessage">Type of message</typeparam>
+ /// <param name="deliveryAction">Action to invoke when message is delivered</param>
+ /// <returns>TinyMessageSubscription used to unsubscribing</returns>
+ TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction, Func<TMessage, bool> messageFilter) where TMessage : class, ITinyMessage;
+
+ /// <summary>
+ /// Subscribe to a message type with the given destination and delivery action with the given filter.
+ /// Messages will be delivered via the specified proxy.
+ /// All references (apart from the proxy) are held with WeakReferences
+ ///
+ /// Only messages that "pass" the filter will be delivered.
+ /// </summary>
+ /// <typeparam name="TMessage">Type of message</typeparam>
+ /// <param name="deliveryAction">Action to invoke when message is delivered</param>
+ /// <param name="proxy">Proxy to use when delivering the messages</param>
+ /// <returns>TinyMessageSubscription used to unsubscribing</returns>
+ TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction, Func<TMessage, bool> messageFilter, ITinyMessageProxy proxy) where TMessage : class, ITinyMessage;
+
+ /// <summary>
+ /// Subscribe to a message type with the given destination and delivery action with the given filter.
+ /// All references are held with WeakReferences
+ ///
+ /// Only messages that "pass" the filter will be delivered.
+ /// </summary>
+ /// <typeparam name="TMessage">Type of message</typeparam>
+ /// <param name="deliveryAction">Action to invoke when message is delivered</param>
+ /// <param name="useStrongReferences">Use strong references to destination and deliveryAction </param>
+ /// <returns>TinyMessageSubscription used to unsubscribing</returns>
+ TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction, Func<TMessage, bool> messageFilter, bool useStrongReferences) where TMessage : class, ITinyMessage;
+
+ /// <summary>
+ /// Subscribe to a message type with the given destination and delivery action with the given filter.
+ /// Messages will be delivered via the specified proxy.
+ /// All references are held with WeakReferences
+ ///
+ /// Only messages that "pass" the filter will be delivered.
+ /// </summary>
+ /// <typeparam name="TMessage">Type of message</typeparam>
+ /// <param name="deliveryAction">Action to invoke when message is delivered</param>
+ /// <param name="useStrongReferences">Use strong references to destination and deliveryAction </param>
+ /// <param name="proxy">Proxy to use when delivering the messages</param>
+ /// <returns>TinyMessageSubscription used to unsubscribing</returns>
+ TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction, Func<TMessage, bool> messageFilter, bool useStrongReferences, ITinyMessageProxy proxy) where TMessage : class, ITinyMessage;
+
+ /// <summary>
+ /// Unsubscribe from a particular message type.
+ ///
+ /// Does not throw an exception if the subscription is not found.
+ /// </summary>
+ /// <typeparam name="TMessage">Type of message</typeparam>
+ /// <param name="subscriptionToken">Subscription token received from Subscribe</param>
+ void Unsubscribe<TMessage>(TinyMessageSubscriptionToken subscriptionToken) where TMessage : class, ITinyMessage;
+
+ /// <summary>
+ /// Publish a message to any subscribers
+ /// </summary>
+ /// <typeparam name="TMessage">Type of message</typeparam>
+ /// <param name="message">Message to deliver</param>
+ void Publish<TMessage>(TMessage message) where TMessage : class, ITinyMessage;
+
+ /// <summary>
+ /// Publish a message to any subscribers asynchronously
+ /// </summary>
+ /// <typeparam name="TMessage">Type of message</typeparam>
+ /// <param name="message">Message to deliver</param>
+ void PublishAsync<TMessage>(TMessage message) where TMessage : class, ITinyMessage;
+
+ /// <summary>
+ /// Publish a message to any subscribers asynchronously
+ /// </summary>
+ /// <typeparam name="TMessage">Type of message</typeparam>
+ /// <param name="message">Message to deliver</param>
+ /// <param name="callback">AsyncCallback called on completion</param>
+ void PublishAsync<TMessage>(TMessage message, AsyncCallback callback) where TMessage : class, ITinyMessage;
+ }
+ #endregion
+
+ #region Hub Implementation
+ /// <summary>
+ /// Messenger hub responsible for taking subscriptions/publications and delivering of messages.
+ /// </summary>
+ public sealed class TinyMessengerHub : ITinyMessengerHub
+ {
+ #region Private Types and Interfaces
+ private class WeakTinyMessageSubscription<TMessage> : ITinyMessageSubscription
+ where TMessage : class, ITinyMessage
+ {
+ protected TinyMessageSubscriptionToken _SubscriptionToken;
+ protected WeakReference _DeliveryAction;
+ protected WeakReference _MessageFilter;
+
+ public TinyMessageSubscriptionToken SubscriptionToken
+ {
+ get { return _SubscriptionToken; }
+ }
+
+ public bool ShouldAttemptDelivery(ITinyMessage message)
+ {
+ if (!(message is TMessage))
+ return false;
+
+ if (!_DeliveryAction.IsAlive)
+ return false;
+
+ if (!_MessageFilter.IsAlive)
+ return false;
+
+ return ((Func<TMessage, bool>)_MessageFilter.Target).Invoke(message as TMessage);
+ }
+
+ public void Deliver(ITinyMessage message)
+ {
+ if (!(message is TMessage))
+ throw new ArgumentException("Message is not the correct type");
+
+ if (!_DeliveryAction.IsAlive)
+ return;
+
+ ((Action<TMessage>)_DeliveryAction.Target).Invoke(message as TMessage);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the WeakTinyMessageSubscription class.
+ /// </summary>
+ /// <param name="destination">Destination object</param>
+ /// <param name="deliveryAction">Delivery action</param>
+ /// <param name="messageFilter">Filter function</param>
+ public WeakTinyMessageSubscription(TinyMessageSubscriptionToken subscriptionToken, Action<TMessage> deliveryAction, Func<TMessage, bool> messageFilter)
+ {
+ if (subscriptionToken == null)
+ throw new ArgumentNullException("subscriptionToken");
+
+ if (deliveryAction == null)
+ throw new ArgumentNullException("deliveryAction");
+
+ if (messageFilter == null)
+ throw new ArgumentNullException("messageFilter");
+
+ _SubscriptionToken = subscriptionToken;
+ _DeliveryAction = new WeakReference(deliveryAction);
+ _MessageFilter = new WeakReference(messageFilter);
+ }
+ }
+
+ private class StrongTinyMessageSubscription<TMessage> : ITinyMessageSubscription
+ where TMessage : class, ITinyMessage
+ {
+ protected TinyMessageSubscriptionToken _SubscriptionToken;
+ protected Action<TMessage> _DeliveryAction;
+ protected Func<TMessage, bool> _MessageFilter;
+
+ public TinyMessageSubscriptionToken SubscriptionToken
+ {
+ get { return _SubscriptionToken; }
+ }
+
+ public bool ShouldAttemptDelivery(ITinyMessage message)
+ {
+ if (!(message is TMessage))
+ return false;
+
+ return _MessageFilter.Invoke(message as TMessage);
+ }
+
+ public void Deliver(ITinyMessage message)
+ {
+ if (!(message is TMessage))
+ throw new ArgumentException("Message is not the correct type");
+
+ _DeliveryAction.Invoke(message as TMessage);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the TinyMessageSubscription class.
+ /// </summary>
+ /// <param name="destination">Destination object</param>
+ /// <param name="deliveryAction">Delivery action</param>
+ /// <param name="messageFilter">Filter function</param>
+ public StrongTinyMessageSubscription(TinyMessageSubscriptionToken subscriptionToken, Action<TMessage> deliveryAction, Func<TMessage, bool> messageFilter)
+ {
+ if (subscriptionToken == null)
+ throw new ArgumentNullException("subscriptionToken");
+
+ if (deliveryAction == null)
+ throw new ArgumentNullException("deliveryAction");
+
+ if (messageFilter == null)
+ throw new ArgumentNullException("messageFilter");
+
+ _SubscriptionToken = subscriptionToken;
+ _DeliveryAction = deliveryAction;
+ _MessageFilter = messageFilter;
+ }
+ }
+ #endregion
+
+ #region Subscription dictionary
+ private class SubscriptionItem
+ {
+ public ITinyMessageProxy Proxy { get; private set; }
+ public ITinyMessageSubscription Subscription { get; private set; }
+
+ public SubscriptionItem(ITinyMessageProxy proxy, ITinyMessageSubscription subscription)
+ {
+ Proxy = proxy;
+ Subscription = subscription;
+ }
+ }
+
+ private readonly object _SubscriptionsPadlock = new object();
+ private readonly Dictionary<Type, List<SubscriptionItem>> _Subscriptions = new Dictionary<Type, List<SubscriptionItem>>();
+ #endregion
+
+ #region Public API
+ /// <summary>
+ /// Subscribe to a message type with the given destination and delivery action.
+ /// All references are held with strong references
+ ///
+ /// All messages of this type will be delivered.
+ /// </summary>
+ /// <typeparam name="TMessage">Type of message</typeparam>
+ /// <param name="deliveryAction">Action to invoke when message is delivered</param>
+ /// <returns>TinyMessageSubscription used to unsubscribing</returns>
+ public TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction) where TMessage : class, ITinyMessage
+ {
+ return AddSubscriptionInternal<TMessage>(deliveryAction, (m) => true, true, DefaultTinyMessageProxy.Instance);
+ }
+
+ /// <summary>
+ /// Subscribe to a message type with the given destination and delivery action.
+ /// Messages will be delivered via the specified proxy.
+ /// All references (apart from the proxy) are held with strong references
+ ///
+ /// All messages of this type will be delivered.
+ /// </summary>
+ /// <typeparam name="TMessage">Type of message</typeparam>
+ /// <param name="deliveryAction">Action to invoke when message is delivered</param>
+ /// <param name="proxy">Proxy to use when delivering the messages</param>
+ /// <returns>TinyMessageSubscription used to unsubscribing</returns>
+ public TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction, ITinyMessageProxy proxy) where TMessage : class, ITinyMessage
+ {
+ return AddSubscriptionInternal<TMessage>(deliveryAction, (m) => true, true, proxy);
+ }
+
+ /// <summary>
+ /// Subscribe to a message type with the given destination and delivery action.
+ ///
+ /// All messages of this type will be delivered.
+ /// </summary>
+ /// <typeparam name="TMessage">Type of message</typeparam>
+ /// <param name="deliveryAction">Action to invoke when message is delivered</param>
+ /// <param name="useStrongReferences">Use strong references to destination and deliveryAction </param>
+ /// <returns>TinyMessageSubscription used to unsubscribing</returns>
+ public TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction, bool useStrongReferences) where TMessage : class, ITinyMessage
+ {
+ return AddSubscriptionInternal<TMessage>(deliveryAction, (m) => true, useStrongReferences, DefaultTinyMessageProxy.Instance);
+ }
+
+ /// <summary>
+ /// Subscribe to a message type with the given destination and delivery action.
+ /// Messages will be delivered via the specified proxy.
+ ///
+ /// All messages of this type will be delivered.
+ /// </summary>
+ /// <typeparam name="TMessage">Type of message</typeparam>
+ /// <param name="deliveryAction">Action to invoke when message is delivered</param>
+ /// <param name="useStrongReferences">Use strong references to destination and deliveryAction </param>
+ /// <param name="proxy">Proxy to use when delivering the messages</param>
+ /// <returns>TinyMessageSubscription used to unsubscribing</returns>
+ public TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction, bool useStrongReferences, ITinyMessageProxy proxy) where TMessage : class, ITinyMessage
+ {
+ return AddSubscriptionInternal<TMessage>(deliveryAction, (m) => true, useStrongReferences, proxy);
+ }
+
+ /// <summary>
+ /// Subscribe to a message type with the given destination and delivery action with the given filter.
+ /// All references are held with WeakReferences
+ ///
+ /// Only messages that "pass" the filter will be delivered.
+ /// </summary>
+ /// <typeparam name="TMessage">Type of message</typeparam>
+ /// <param name="deliveryAction">Action to invoke when message is delivered</param>
+ /// <returns>TinyMessageSubscription used to unsubscribing</returns>
+ public TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction, Func<TMessage, bool> messageFilter) where TMessage : class, ITinyMessage
+ {
+ return AddSubscriptionInternal<TMessage>(deliveryAction, messageFilter, true, DefaultTinyMessageProxy.Instance);
+ }
+
+ /// <summary>
+ /// Subscribe to a message type with the given destination and delivery action with the given filter.
+ /// Messages will be delivered via the specified proxy.
+ /// All references (apart from the proxy) are held with WeakReferences
+ ///
+ /// Only messages that "pass" the filter will be delivered.
+ /// </summary>
+ /// <typeparam name="TMessage">Type of message</typeparam>
+ /// <param name="deliveryAction">Action to invoke when message is delivered</param>
+ /// <param name="proxy">Proxy to use when delivering the messages</param>
+ /// <returns>TinyMessageSubscription used to unsubscribing</returns>
+ public TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction, Func<TMessage, bool> messageFilter, ITinyMessageProxy proxy) where TMessage : class, ITinyMessage
+ {
+ return AddSubscriptionInternal<TMessage>(deliveryAction, messageFilter, true, proxy);
+ }
+
+ /// <summary>
+ /// Subscribe to a message type with the given destination and delivery action with the given filter.
+ /// All references are held with WeakReferences
+ ///
+ /// Only messages that "pass" the filter will be delivered.
+ /// </summary>
+ /// <typeparam name="TMessage">Type of message</typeparam>
+ /// <param name="deliveryAction">Action to invoke when message is delivered</param>
+ /// <param name="useStrongReferences">Use strong references to destination and deliveryAction </param>
+ /// <returns>TinyMessageSubscription used to unsubscribing</returns>
+ public TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction, Func<TMessage, bool> messageFilter, bool useStrongReferences) where TMessage : class, ITinyMessage
+ {
+ return AddSubscriptionInternal<TMessage>(deliveryAction, messageFilter, useStrongReferences, DefaultTinyMessageProxy.Instance);
+ }
+
+ /// <summary>
+ /// Subscribe to a message type with the given destination and delivery action with the given filter.
+ /// Messages will be delivered via the specified proxy.
+ /// All references are held with WeakReferences
+ ///
+ /// Only messages that "pass" the filter will be delivered.
+ /// </summary>
+ /// <typeparam name="TMessage">Type of message</typeparam>
+ /// <param name="deliveryAction">Action to invoke when message is delivered</param>
+ /// <param name="useStrongReferences">Use strong references to destination and deliveryAction </param>
+ /// <param name="proxy">Proxy to use when delivering the messages</param>
+ /// <returns>TinyMessageSubscription used to unsubscribing</returns>
+ public TinyMessageSubscriptionToken Subscribe<TMessage>(Action<TMessage> deliveryAction, Func<TMessage, bool> messageFilter, bool useStrongReferences, ITinyMessageProxy proxy) where TMessage : class, ITinyMessage
+ {
+ return AddSubscriptionInternal<TMessage>(deliveryAction, messageFilter, useStrongReferences, proxy);
+ }
+
+ /// <summary>
+ /// Unsubscribe from a particular message type.
+ ///
+ /// Does not throw an exception if the subscription is not found.
+ /// </summary>
+ /// <typeparam name="TMessage">Type of message</typeparam>
+ /// <param name="subscriptionToken">Subscription token received from Subscribe</param>
+ public void Unsubscribe<TMessage>(TinyMessageSubscriptionToken subscriptionToken) where TMessage : class, ITinyMessage
+ {
+ RemoveSubscriptionInternal<TMessage>(subscriptionToken);
+ }
+
+ /// <summary>
+ /// Publish a message to any subscribers
+ /// </summary>
+ /// <typeparam name="TMessage">Type of message</typeparam>
+ /// <param name="message">Message to deliver</param>
+ public void Publish<TMessage>(TMessage message) where TMessage : class, ITinyMessage
+ {
+ PublishInternal<TMessage>(message);
+ }
+
+ /// <summary>
+ /// Publish a message to any subscribers asynchronously
+ /// </summary>
+ /// <typeparam name="TMessage">Type of message</typeparam>
+ /// <param name="message">Message to deliver</param>
+ public void PublishAsync<TMessage>(TMessage message) where TMessage : class, ITinyMessage
+ {
+ PublishAsyncInternal<TMessage>(message, null);
+ }
+
+ /// <summary>
+ /// Publish a message to any subscribers asynchronously
+ /// </summary>
+ /// <typeparam name="TMessage">Type of message</typeparam>
+ /// <param name="message">Message to deliver</param>
+ /// <param name="callback">AsyncCallback called on completion</param>
+ public void PublishAsync<TMessage>(TMessage message, AsyncCallback callback) where TMessage : class, ITinyMessage
+ {
+ PublishAsyncInternal<TMessage>(message, callback);
+ }
+ #endregion
+
+ #region Internal Methods
+ private TinyMessageSubscriptionToken AddSubscriptionInternal<TMessage>(Action<TMessage> deliveryAction, Func<TMessage, bool> messageFilter, bool strongReference, ITinyMessageProxy proxy)
+ where TMessage : class, ITinyMessage
+ {
+ if (deliveryAction == null)
+ throw new ArgumentNullException("deliveryAction");
+
+ if (messageFilter == null)
+ throw new ArgumentNullException("messageFilter");
+
+ if (proxy == null)
+ throw new ArgumentNullException("proxy");
+
+ lock (_SubscriptionsPadlock)
+ {
+ List<SubscriptionItem> currentSubscriptions;
+
+ if (!_Subscriptions.TryGetValue(typeof(TMessage), out currentSubscriptions))
+ {
+ currentSubscriptions = new List<SubscriptionItem>();
+ _Subscriptions[typeof(TMessage)] = currentSubscriptions;
+ }
+
+ var subscriptionToken = new TinyMessageSubscriptionToken(this, typeof(TMessage));
+
+ ITinyMessageSubscription subscription;
+ if (strongReference)
+ subscription = new StrongTinyMessageSubscription<TMessage>(subscriptionToken, deliveryAction, messageFilter);
+ else
+ subscription = new WeakTinyMessageSubscription<TMessage>(subscriptionToken, deliveryAction, messageFilter);
+
+ currentSubscriptions.Add(new SubscriptionItem(proxy, subscription));
+
+ return subscriptionToken;
+ }
+ }
+
+ private void RemoveSubscriptionInternal<TMessage>(TinyMessageSubscriptionToken subscriptionToken)
+ where TMessage : class, ITinyMessage
+ {
+ if (subscriptionToken == null)
+ throw new ArgumentNullException("subscriptionToken");
+
+ lock (_SubscriptionsPadlock)
+ {
+ List<SubscriptionItem> currentSubscriptions;
+ if (!_Subscriptions.TryGetValue(typeof(TMessage), out currentSubscriptions))
+ return;
+
+ var currentlySubscribed = (from sub in currentSubscriptions
+ where object.ReferenceEquals(sub.Subscription.SubscriptionToken, subscriptionToken)
+ select sub).ToList();
+
+ currentlySubscribed.ForEach(sub => currentSubscriptions.Remove(sub));
+ }
+ }
+
+ private void PublishInternal<TMessage>(TMessage message)
+ where TMessage : class, ITinyMessage
+ {
+ if (message == null)
+ throw new ArgumentNullException("message");
+
+ List<SubscriptionItem> currentlySubscribed;
+ lock (_SubscriptionsPadlock)
+ {
+ List<SubscriptionItem> currentSubscriptions;
+ if (!_Subscriptions.TryGetValue(typeof(TMessage), out currentSubscriptions))
+ return;
+
+ currentlySubscribed = (from sub in currentSubscriptions
+ where sub.Subscription.ShouldAttemptDelivery(message)
+ select sub).ToList();
+ }
+
+ currentlySubscribed.ForEach(sub =>
+ {
+ try
+ {
+ sub.Proxy.Deliver(message, sub.Subscription);
+ }
+ catch (Exception)
+ {
+ // Ignore any errors and carry on
+ // TODO - add to a list of erroring subs and remove them?
+ }
+ });
+ }
+
+ private void PublishAsyncInternal<TMessage>(TMessage message, AsyncCallback callback) where TMessage : class, ITinyMessage
+ {
+ Action publishAction = () => { PublishInternal<TMessage>(message); };
+
+ publishAction.BeginInvoke(callback, null);
+ }
+ #endregion
+ }
+ #endregion
+}
View
61 Binding/Binding.cs
@@ -0,0 +1,61 @@
+//
+// Binding.cs:
+//
+// Author:
+// Robert Kozak (rkozak@gmail.com)
+//
+// Copyright 2011, Nowcom Corporation
+//
+// Code licensed under the MIT X11 license
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+namespace MonoTouch.MVVM
+{
+ using System;
+ using System.Globalization;
+ using System.ComponentModel;
+
+ public class Binding: BindingBase
+ {
+ public Binding(): this(null, null)
+ {
+ }
+
+ public Binding(string sourcePath, string targetPath)
+ {
+ SourcePath = sourcePath;
+ TargetPath = targetPath;
+ Mode = BindingMode.TwoWay;
+ }
+
+ public string TargetPath { get; set; }
+ public object Target { get; set;}
+ public string SourcePath { get; set; }
+ public object Source { get; set; }
+
+ public IValueConverter Converter { get; set; }
+ public CultureInfo ConverterCulture { get; set; }
+ public object ConverterParameter { get; set; }
+ public BindingMode Mode { get; set; }
+
+ }
+}
+
View
44 Binding/BindingBase.cs
@@ -0,0 +1,44 @@
+//
+// BindingBase.cs:
+//
+// Author:
+// Robert Kozak (rkozak@gmail.com)
+//
+// Copyright 2011, Nowcom Corporation
+//
+// Code licensed under the MIT X11 license
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+namespace MonoTouch.MVVM
+{
+ using System;
+
+ public abstract class BindingBase
+ {
+ protected BindingBase()
+ {
+ }
+
+ public object FallbackValue { get; set; }
+ public object TargetNullValue { get; set;}
+ }
+}
+
View
186 Binding/BindingExpression.cs
@@ -0,0 +1,186 @@
+//
+// BindingExpression.cs:
+//
+// Author:
+// Robert Kozak (rkozak@gmail.com)
+//
+// Copyright 2011, Nowcom Corporation
+//
+// Code licensed under the MIT X11 license
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+namespace MonoTouch.MVVM
+{
+ using System;
+ using System.Reflection;
+ using System.Globalization;
+ using MonoTouch.UIKit;
+ using System.Collections.Generic;
+ using MonoTouch.Dialog;
+
+ public class BindingExpression : IBindingExpression
+ {
+ struct PendingUpdateKey
+ {
+ public UITableViewElementCell Cell;
+ public PropertyInfo TargetProperty;
+ public object Target;
+ }
+
+ public PropertyInfo SourceProperty;
+ public PropertyInfo TargetProperty;
+ public Element Element { get; set; }
+
+ public Binding Binding { get; private set; }
+
+ public BindingExpression(Binding binding, PropertyInfo targetProperty, object target)
+ {
+ if (binding == null)
+ throw new ArgumentNullException("binding");
+
+ if (targetProperty == null)
+ throw new ArgumentNullException("targetProperty");
+
+ if (target == null)
+ throw new ArgumentNullException("target");
+
+ Binding = binding;
+ Binding.Target = target;
+ TargetProperty = targetProperty;
+ if(string.IsNullOrEmpty(binding.TargetPath))
+ {
+ binding.TargetPath = targetProperty.Name;
+ }
+
+ object source = Binding.Source;
+ SourceProperty = source.GetType().GetNestedProperty(ref source, Binding.SourcePath, true);
+ Binding.Source = source;
+ }
+
+ public void UpdateSource()
+ {
+ UpdateSource(GetTargetValue());
+ }
+
+ public void UpdateSource(object targetValue)
+ {
+ if (SourceProperty != null && SourceProperty.CanWrite && Binding.Mode != BindingMode.OneTime)
+ {
+ try
+ {
+ var convertedTargetValue = ConvertbackValue(targetValue);
+ if (Convert.GetTypeCode(convertedTargetValue) != TypeCode.Object)
+ convertedTargetValue = Convert.ChangeType(convertedTargetValue, SourceProperty.PropertyType);
+
+ SourceProperty.SetValue(Binding.Source, convertedTargetValue, null);
+ }
+ catch (NotImplementedException)
+ {
+ }
+ }
+ }
+
+ public void UpdateTarget()
+ {
+ UpdateTarget(GetSourceValue());
+ }
+
+ public void UpdateTarget(object sourceValue)
+ {
+ if (TargetProperty != null && TargetProperty.CanWrite && Binding.Mode == BindingMode.TwoWay)
+ {
+ if (sourceValue == null)
+ sourceValue = Binding.TargetNullValue;
+ try
+ {
+ var convertedSourceValue = ConvertValue(sourceValue);
+
+ if (Convert.GetTypeCode(convertedSourceValue) != TypeCode.Object)
+ convertedSourceValue = Convert.ChangeType(convertedSourceValue, TargetProperty.PropertyType);
+
+ if (Element != null && Element.Cell != null && Element.Cell.Element == Element)
+ {
+ TargetProperty.SetValue(Binding.Target, convertedSourceValue, null);
+ }
+
+ }
+ catch (InvalidCastException ex)
+ {
+ Console.WriteLine(ex.Message + " : "+ex.StackTrace);
+ }
+ catch (NotImplementedException ex)
+ {
+ }
+ }
+ }
+
+ public object ConvertValue(object value)
+ {
+ object convertedValue = value;
+
+ if (Binding.Converter != null)
+ {
+ object parameter = Element;
+ if (Binding.ConverterParameter != null)
+ parameter = Binding.ConverterParameter;
+
+ convertedValue = Binding.Converter.Convert(value, TargetProperty.PropertyType, parameter, CultureInfo.CurrentUICulture);
+ }
+
+ return convertedValue;
+ }
+
+ public object ConvertbackValue(object value)
+ {
+ object convertedValue = value;
+
+ if (Binding.Converter != null)
+ {
+ object parameter = Element;
+ if (Binding.ConverterParameter != null)
+ parameter = Binding.ConverterParameter;
+
+ convertedValue = Binding.Converter.ConvertBack(value, SourceProperty.PropertyType, parameter, CultureInfo.CurrentUICulture);
+ }
+
+ return convertedValue;
+ }
+
+ protected virtual object GetSourceValue()
+ {
+ if (Binding.Source != null)
+ {
+ var property = Binding.Source.GetType().GetProperty(Binding.SourcePath, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
+
+ if (property != null)
+ return property.GetValue(Binding.Source, null);
+ }
+
+ return Binding.FallbackValue;
+ }
+
+ protected virtual object GetTargetValue()
+ {
+ return TargetProperty.GetValue(Binding.Target, null);
+ }
+ }
+}
+
View
41 Binding/BindingMode.cs
@@ -0,0 +1,41 @@
+//
+// BindingMode.cs:
+//
+// Author:
+// Robert Kozak (rkozak@gmail.com)
+//
+// Copyright 2011, Nowcom Corporation
+//
+// Code licensed under the MIT X11 license
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+namespace MonoTouch.MVVM
+{
+ using System;
+
+ public enum BindingMode
+ {
+ OneWay = 1,
+ OneTime = 2,
+ TwoWay = 3,
+ }
+}
+
View
215 Binding/BindingOperations.cs
@@ -0,0 +1,215 @@
+//
+// BindingOperations.cs:
+//
+// Author:
+// Robert Kozak (rkozak@gmail.com)
+//
+// Copyright 2011, Nowcom Corporation
+//
+// Code licensed under the MIT X11 license
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+namespace MonoTouch.MVVM
+{
+ using System;
+ using System.Collections.Generic;
+ using System.ComponentModel;
+ using System.Linq;
+ using System.Reflection;
+ using MonoTouch.Dialog;
+
+ public static class BindingOperations
+ {
+ private class PropertyBinder
+ {
+ public IBindable Object;
+ public string Property;
+ }
+
+ private static bool _UpdatingBindings { get; set; }
+ private static Dictionary<PropertyBinder, Binding> _Bindings = new Dictionary<PropertyBinder, Binding>();
+ private static List<IBindingExpression> _BindingExpressions;
+
+ public static void ClearAllBindings()
+ {
+ _BindingExpressions.Clear();
+ _Bindings.Clear();
+ }
+
+ public static void ClearBinding(object source, string property)
+ {
+ var bindingExpression = GetBindingExpression(source, property);
+ if (bindingExpression != null)
+ _BindingExpressions.Remove(bindingExpression);
+
+ var binding = _Bindings.SingleOrDefault((kvp)=>kvp.Key.Object == source && kvp.Key.Property == property);
+ if (binding.Key != null)
+ _Bindings.Remove(binding.Key);
+ }
+
+ public static Binding GetBinding(object target, string property)
+ {
+ var bindingExpression = GetBindingExpression(target, property);
+ if (bindingExpression != null)
+ return bindingExpression.Binding;
+
+ return null;
+ }
+
+ public static IBindingExpression GetBindingExpression(object target, string property)
+ {
+ UpdateBindings();
+ if (_BindingExpressions != null)
+ {
+ return _BindingExpressions.SingleOrDefault((b)=>b.Binding.TargetPath == property && b.Binding.Target == target);
+ }
+
+ return null;
+ }
+
+ public static IBindingExpression[] GetBindingExpressionsForElement(object element)
+ {
+ UpdateBindings();
+ if (_BindingExpressions != null)
+ {
+ return _BindingExpressions.Where((b)=>b.Element == element).ToArray();
+ }
+
+ return null;
+ }
+
+ private static IBindingExpression[] GetBindingExpressions(string property)
+ {
+ UpdateBindings();
+ if (_BindingExpressions != null)
+ {
+ return _BindingExpressions.Where((b)=>b.Binding.SourcePath == property).ToArray();
+ }
+
+ return null;
+ }
+
+ public static bool IsDataBound(IBindable target, string property) { return false; }
+
+ public static IBindingExpression SetBinding(IBindable target, string targetProperty, Binding binding)
+ {
+ if (target == null)
+ throw new ArgumentNullException("target");
+
+ if (string.IsNullOrEmpty(targetProperty))
+ throw new ArgumentNullException("targetProperty");
+
+
+ if (binding == null)
+ throw new ArgumentNullException("binding");
+
+ var binderKey =_Bindings.SingleOrDefault((kvp)=>kvp.Key.Object == target && kvp.Key.Property == targetProperty).Key;
+
+ IBindingExpression bindingExpression = null;
+
+ object nestedTarget = target;
+ var element = target as Element;
+
+ PropertyInfo propertyInfo = target.GetType().GetNestedProperty(ref nestedTarget, targetProperty, false);
+ var targetReady = propertyInfo != null && nestedTarget != null;
+
+ if (targetReady)
+ {
+ if (_BindingExpressions == null)
+ _BindingExpressions = new List<IBindingExpression>();
+
+ bindingExpression = GetBindingExpression(target, targetProperty);
+
+ if (bindingExpression == null)
+ {
+ bindingExpression = new BindingExpression(binding, propertyInfo, nestedTarget) { Element = element };
+
+ _BindingExpressions.Add(bindingExpression);
+
+ var INPC = bindingExpression.Binding.Source as INotifyPropertyChanged;
+ if (INPC != null)
+ {
+ INPC.PropertyChanged -= HandleDataContextPropertyChanged;
+ INPC.PropertyChanged += HandleDataContextPropertyChanged;
+ }
+ }
+ }
+ else
+ {
+ if (binderKey == null)
+ _Bindings.Add(new PropertyBinder() { Object = target, Property = targetProperty }, binding);
+ else
+ _Bindings[binderKey] = binding;
+ }
+
+ return bindingExpression;
+ }
+
+ private static void UpdateBindings()
+ {
+ if(!_UpdatingBindings)
+ {
+ try
+ {
+ _UpdatingBindings = true;
+
+ var bindingsToRemove = new List<PropertyBinder>();
+
+ if (_Bindings.Count > 0)
+ {
+ var keys = _Bindings.Keys.ToArray();
+ for (var i = 0; i < keys.Length; i++)
+ {
+ var binding = _Bindings[keys[i]];
+ var bindingExpression = SetBinding(keys[i].Object, keys[i].Property, binding);
+ if(bindingExpression != null)
+ bindingsToRemove.Add(keys[i]);
+ }
+
+ if (bindingsToRemove.Count > 0)
+ {
+ foreach(var bindingKey in bindingsToRemove)
+ {
+ _Bindings.Remove(bindingKey);
+ }
+ }
+
+ bindingsToRemove.Clear();
+ }
+ }
+ finally
+ {
+ _UpdatingBindings = false;
+ }
+ }
+ }
+
+ private static void HandleDataContextPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ IBindingExpression[] bindingExpressions = GetBindingExpressions(e.PropertyName);
+ foreach (var bindingExpression in bindingExpressions)
+ {
+ bindingExpression.UpdateTarget();
+ }
+ }
+ }
+}
+
View
42 Binding/IBindable.cs
@@ -0,0 +1,42 @@
+//
+// IBindable.cs:
+//
+// Author:
+// Robert Kozak (rkozak@gmail.com)
+//
+// Copyright 2011, Nowcom Corporation
+//
+// Code licensed under the MIT X11 license
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+namespace MonoTouch.MVVM
+{
+ using System;
+ using MonoTouch.Dialog;
+
+ public interface IBindable
+ {
+ IBindingExpression SetBinding(IBindable target, string targetProperty, Binding binding);
+ IBindingExpression SetBinding(string targetProperty, Binding binding);
+ IBindingExpression GetBindingExpression(string targetProperty);
+ }
+}
+
View
45 Binding/IBindingExpression.cs
@@ -0,0 +1,45 @@
+//
+// IBindingExpression.cs:
+//
+// Author:
+// Robert Kozak (rkozak@gmail.com)
+//
+// Copyright 2011, Nowcom Corporation
+//
+// Code licensed under the MIT X11 license
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+namespace MonoTouch.MVVM
+{
+ using System;
+ using MonoTouch.Dialog;
+
+ public interface IBindingExpression
+ {
+ Binding Binding { get; }
+ object ConvertValue(object value);
+ object ConvertbackValue(object value);
+ Element Element { get; }
+
+ void UpdateTarget();
+ void UpdateSource();
+ }
+}
View
41 Binding/IValueConverter.cs
@@ -0,0 +1,41 @@
+//
+// IValueConverter.cs:
+//
+// Author:
+// Robert Kozak (rkozak@gmail.com)
+//
+// Copyright 2011, Nowcom Corporation
+//
+// Code licensed under the MIT X11 license
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+namespace MonoTouch.MVVM
+{
+ using System;
+ using System.Globalization;
+
+ public interface IValueConverter
+ {
+ object Convert(object value, Type targetType, object parameter, CultureInfo culture);
+ object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture);
+ }
+}
+
View
15 Binding/PropertyPath.cs
@@ -0,0 +1,15 @@
+namespace MonoTouch.MVVM
+{
+ using System;
+
+ public sealed class PropertyPath
+ {
+ public string Path { get; internal set; }
+
+ public PropertyPath(string path)
+ {
+ Path = path;
+ }
+ }
+}
+
View
40 Binding/TypeExtensions.cs
@@ -0,0 +1,40 @@
+//
+// TypeExtensions.cs:
+//
+// Author:
+// Robert Kozak (rkozak@gmail.com)
+//
+// Copyright 2011, Nowcom Corporation
+//
+// Code licensed under the MIT X11 license
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+namespace MonoTouch.MVVM
+{
+ using System;
+ using System.Reflection;
+
+ public static class TypeExtensions
+ {
+
+ }
+}
+
View
47 ChangeLog
@@ -0,0 +1,47 @@
+2010-06-16 Eric Maupin <me@ermau.com>
+
+ * Elements.cs: Added support on FloatElement for an optional
+ caption (default is still no caption).
+
+ * Reflect.cs: Added [Range] optional captions support.
+
+2010-06-18 Eric Maupin <me@ermau.com>
+
+ * Element.cs: EntryElement: Fixed an error with FetchValue
+ if the text field hadn't yet been generated.
+
+ StringElement: SelectionStyle is only blue when there's a
+ tap handler registered.
+
+2010-06-17 Eric Maupin <me@ermau.com>
+
+ * Reflect.cs: EntryAttribute: Removed placeholder requirement
+ and added KeyboardType support.
+
+2010-05-16 Miguel de Icaza <miguel@novell.com>
+
+ * DialogViewController.cs (StartSearch, FinishSearch,
+ PerformFilter): Made public these methods. They allow consumers
+ to start, stop and trigger a filter operation programatically.
+
+ (OnSearchTextChanged): New virtual method that subclasses can hook
+ up into to check for changes in the search bar.
+
+ (SearchTextChanged): Event to listen to changes in the search.
+
+ (Selected): New virtual method that actually dispatches to the
+ Selected method in the element. This wasy subclasses can catch
+ the Selected method without having to hook up hundreds of
+ methods.
+
+ * Element.cs (Section.Insert): the overload that takes an
+ IEnumerable now returns the number of items that were inserted.
+
+ (Section.Remove): Add new overload to remove an element from the
+ section.
+
+ (RootElement): New constructor that can take a function that
+ creates the nested DialogViewController, this prevents code from
+ having to derive a new class just to push a new kind of
+ UIViewController.
+
View
69 Collections/EnumBinder.cs
@@ -0,0 +1,69 @@
+//
+// EnumBinder.cs:
+//
+// Author:
+// Robert Kozak (rkozak@gmail.com)
+//
+// Copyright 2011, Nowcom Corporation
+//
+// Code licensed under the MIT X11 license
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+namespace MonoTouch.MVVM
+{
+ public class EnumBinder : PropertyNotifier
+ {
+ public string GroupName
+ {
+ get { return Get(() => GroupName); }
+ set { Set(() => GroupName, value); }
+ }
+
+ public string FieldName
+ {
+ get { return Get(() => FieldName); }
+ set { Set(() => FieldName, value); }
+ }
+
+ public int Index
+ {
+ get { return Get(() => Index); }
+ set { Set(() => Index, value); }
+ }
+
+ public string Description
+ {
+ get { return Get(() => Description); }
+ set { Set(() => Description, value); }
+ }
+
+ public bool IsChecked
+ {
+ get { return Get(() => IsChecked); }
+ set { Set(() => IsChecked, value); }
+ }
+
+ public override string ToString()
+ {
+ return Description;
+ }
+ }
+}
View
116 Collections/EnumCollection.cs
@@ -0,0 +1,116 @@
+//
+// EnumCollection.cs: Generic Collection of Enum Values
+//
+// Author:
+// Robert Kozak (rkozak@gmail.com)
+//
+// Copyright 2011, Nowcom Corporation
+//
+// Code licensed under the MIT X11 license
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+namespace MonoTouch.MVVM
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.Linq;
+
+ public class EnumCollection<T> : EnumCollection where T : struct
+ {
+ public EnumCollection() : base(typeof(T))
+ {
+ }
+ }
+
+ public class EnumCollection : ObservableCollection<EnumBinder>, IDisposable
+ {
+ public Type EnumType { get; set; }
+
+ public ICollection<EnumBinder> TrueValues
+ {
+ get { return this.Where(binder => binder.IsChecked).ToList(); }
+ }
+
+ public ICollection<EnumBinder> FalseValues
+ {
+ get { return this.Where(binder => !binder.IsChecked).ToList(); }
+ }
+
+ public ICollection<EnumBinder> AllValues
+ {
+ get { return this.ToList(); }
+ }
+
+ private bool _IsOrdered { get; set; }
+
+ public EnumCollection(Type type) : this(type, false) { }
+
+ public EnumCollection(Type type, bool isOrdered)
+ {
+ _IsOrdered = isOrdered;
+
+ if (type == null || !type.IsEnum)
+ throw new ArgumentException("This class only supports Enum types");
+
+ EnumType = type;
+
+ var enumItems = from field in EnumType.GetFields()
+ where field.IsLiteral
+ select field.Name;
+
+ if (_IsOrdered)
+ enumItems = from item in enumItems
+ orderby item
+ select item;
+
+ foreach (var item in enumItems)
+ {
+ Add(new EnumBinder()
+ {
+ GroupName = EnumType.FullName,
+ Index = (int)Enum.Parse(EnumType, item, true),
+ FieldName = item,
+ Description = EnumExtensions.GetDescriptionValue(item, EnumType)
+ });
+ }
+ }
+
+ ~EnumCollection()
+ {
+ Dispose(false);
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool calledExplicitly)
+ {
+ foreach (var item in Items)
+ {
+ item.Dispose();
+ }
+ }
+ }
+}
View
100 Collections/System.Collections.ObjectModel/ObservableCollection.cs
@@ -0,0 +1,100 @@
+//
+// ObservableCollection.cs
+//
+// Contact:
+// Moonlight List (moonlight-list@lists.ximian.com)
+//
+// Copyright 2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+namespace System.Collections.ObjectModel
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.Specialized;
+ using System.ComponentModel;
+
+ public class ObservableCollection<T> : Collection<T>, INotifyCollectionChanged, INotifyPropertyChanged
+ {
+ public ObservableCollection()
+ {
+ }
+
+ public ObservableCollection(IEnumerable<T> collection)
+ {
+ throw new NotImplementedException();
+ }
+
+ public ObservableCollection(List<T> list)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override void ClearItems()
+ {
+ base.ClearItems();
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
+ }
+
+ protected override void InsertItem(int index, T item)
+ {
+ base.InsertItem(index, item);
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
+ }
+
+ protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
+ {
+ if (CollectionChanged != null)
+ CollectionChanged(this, e);
+ }
+
+ protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
+ {
+ if (PropertyChanged != null)
+ PropertyChanged(this, e);
+ }
+
+ protected override void RemoveItem(int index)
+ {
+ T oldItem = this[index];
+
+ base.RemoveItem(index);
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Remove, oldItem, index));
+ }
+
+ protected override void SetItem(int index, T item)
+ {
+ T oldItem = this[index];
+ base.SetItem(index, item);
+
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Replace, item, oldItem, index));
+ }
+
+ public event NotifyCollectionChangedEventHandler CollectionChanged;
+ protected event PropertyChangedEventHandler PropertyChanged;
+
+ event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
+ {
+ add { PropertyChanged += value; }
+ remove { PropertyChanged -= value; }
+ }
+ }
+}
View
35 Collections/System.Collections.Specialized/INotifyCollectionChanged.cs
@@ -0,0 +1,35 @@
+//
+// INotifyCollectionChanged.cs
+//
+// Contact:
+// Moonlight List (moonlight-list@lists.ximian.com)
+//
+// Copyright 2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+namespace System.Collections.Specialized
+{
+
+ public interface INotifyCollectionChanged
+ {
+ event NotifyCollectionChangedEventHandler CollectionChanged;
+ }
+}
View
37 Collections/System.Collections.Specialized/NotifyCollectionChangedAction.cs
@@ -0,0 +1,37 @@
+//
+// NotifyCollectionChangedAction.cs
+//
+// Contact:
+// Moonlight List (moonlight-list@lists.ximian.com)
+//
+// Copyright 2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+namespace System.Collections.Specialized
+{
+ public enum NotifyCollectionChangedAction
+ {
+ Add,
+ Remove,
+ Replace,
+ Reset = 4
+ }
+}
View
102 Collections/System.Collections.Specialized/NotifyCollectionChangedEventArgs.cs
@@ -0,0 +1,102 @@
+//
+// NotifyCollectionChangedEventArgs.cs
+//
+// Contact:
+// Moonlight List (moonlight-list@lists.ximian.com)
+//
+// Copyright 2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System.Collections.Generic;
+
+namespace System.Collections.Specialized
+{
+
+ public sealed class NotifyCollectionChangedEventArgs : EventArgs
+ {
+ private List<object> _NewItems, _OldItems;
+
+ public NotifyCollectionChangedAction Action { get; private set; }
+
+ public IList NewItems
+ {
+ get { return _NewItems; }
+ }
+
+ public IList OldItems
+ {
+ get { return _OldItems; }
+ }
+
+ public int NewStartingIndex { get; private set; }
+ public int OldStartingIndex { get; private set; }
+
+ public NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction action)
+ {
+ if (action != NotifyCollectionChangedAction.Reset)
+ throw new NotSupportedException ();
+
+ Action = action;
+ NewStartingIndex = -1;
+ OldStartingIndex = -1;
+ }
+
+ public NotifyCollectionChangedEventArgs (NotifyCol