switch UIThreadDispatcher in platform (Unity) that does not have a built-in dispatcher - unity3d

I am using mvvmcross with Unity, but it doesn't have a built-in UIThreadDispatcher, there are many people had written it owns dispatcher which doesn't have a common interface. The following is my own implementation. Instead of create different MvxXXXUIThreadDispatcher, is there any other approach ?
public abstract class MvxUnityUIThreadDispatcher
: MvxMainThreadDispatcher
{
protected MvxUnityUIThreadDispatcher()
{
}
public bool RequestMainThreadAction(Action action)
{
UIThreadDispatcher.Instance.InvokeOrEnqueueOnMainThread(() => ExceptionMaskedAction(action));
return true;
}
}

The thread dispatcher is used by Mvx to make sure that UI controls are not called on non-UI threads.
If Unity doesn't need this, then you can just call action() in your implementation.
In the https://github.com/et-nowis/mvx-unity-ngui MvvmCross implementation it looks like they used SynchronizationContext:
public abstract class MvxUnityUIThreadDispatcher
: MvxMainThreadDispatcher
{
private readonly SynchronizationContext _uiSynchronizationContext;
protected MvxUnityUIThreadDispatcher()
{
if (SynchronizationContext.Current == null)
SynchronizationContext.SetSynchronizationContext(new UnitySynchronizationContext());
_uiSynchronizationContext = SynchronizationContext.Current;
}
public bool RequestMainThreadAction(Action action)
{
if (_uiSynchronizationContext == SynchronizationContext.Current)
action();
else
_uiSynchronizationContext.Post(state => ExceptionMaskedAction(action), null);
return true;
}
}
from https://github.com/et-nowis/mvx-unity-ngui/blob/master/Assets/External/MvvmCross/Cirrious/Cirrious.MvvmCross.Unity/Views/MvxUnityUIThreadDispatcher.cs - see source file there for their license terms on this code

Related

Register delegate-func by convention

On the ContainerBuilder i can do the following:
builder.Register<ScenariosConfig>(c =>
(ScenariosConfig)c.Resolve<ConfigFactory>()
.Create(typeof(ScenariosConfig)))
.SingleInstance();
With assembly scanning i can do the following:
builder.RegisterAssemblyTypes(assemblies)
.Where(HasSingletonAttribute)
.As(t => GetNameMatchingInterfaces(t))
.SingleInstance();
Now the question: Is there any way to achieve the following: ?
builder.RegisterAssemblyTypes(assemblies)
.Where(... some condition)
.CreateByDelegate((container, type)
=> c.Resolve<ConfigFactory>().Create(type))
.SingleInstance();
I've already found out about IRegistrationSource with which i can achieve something similar. However, I'm a bit skeptic about the performance impact of creating ton's of IRegistrationSource's for each of my conventions which require a delegate for creation...
And also there's the fact that IRegistrationSource can't be used whenever you need to resolve all instances of IFoo which should be bound by such a "convention".
In the end we did choose to use an IRegistrationSource. The only alternative i've "found" would have been to detect all types per reflection (not using autofac API...) and then generating a delegate for each and registering this with autofac. Would not really result in code that easily understandable...
So for completeness sake' here's the IRegistrationSource implementation:
public class ConfigConventionRegistrationSource : IRegistrationSource
{
public IEnumerable<IComponentRegistration> RegistrationsFor(
Service service,
Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
{
var s = service as IServiceWithType;
if (s != null
&& s.ServiceType.IsClass
&& s.ServiceType.Name.EndsWith("Config")
&& !s.ServiceType.GetInterfaces().Any())
{
yield return RegistrationBuilder
.ForDelegate((componentContext, parameters) =>
CreateConfigByFactory(componentContext, s.ServiceType))
.As(s.ServiceType)
.SingleInstance()
.CreateRegistration();
}
}
private static object CreateConfigByFactory(
IComponentContext componentContext,
Type configType)
{
IConfig configFactory = componentContext.Resolve<IConfig>();
MethodInfo method = Reflector<IConfig>
.GetMethod(x => x.Load<object>())
.GetGenericMethodDefinition()
.MakeGenericMethod(configType);
try
{
return method.Invoke(configFactory, new object[0]);
}
catch (TargetInvocationException tex)
{
ExceptionDispatchInfo
.Capture(tex.InnerException)
.Throw();
throw; // will not be reached as thrown above ;-)
}
}
public bool IsAdapterForIndividualComponents
{
get { return false; }
}
}

Is it safe to use MvxNotifyPropertyChanged as a replacement for implementing INotifyPropertyChanged?

In order to get PropertyChanged to fire in NUnit tests, I had to set ShouldAlwaysRaiseInpcOnUserInterfaceThread(false). Are there any repercussions to this when I later use the class as a ViewModel? Maybe I should be setting up a user interface thread in NUnit? Help!
public interface ISomething : INotifyPropertyChanged
{
}
public class Something : MvxNotifyPropertyChanged, ISomething
{
public Something()
{
ShouldAlwaysRaiseInpcOnUserInterfaceThread(false);
}
private int _num;
public int Num
{
get { return _num; }
set { if (_num != value) { _num = value; RaisePropertyChanged(() => Num); }
}
}
By default MvvmCross marshals calls like RaisePropertyChanged onto the UI thread for the convenience of developers.
If you want to disable this on an individual object, you can call ShouldAlwaysRaiseInpcOnUserInterfaceThread(false); for that object (this is a method call rather than a property as properties on ViewModel objects are generally reserved for INotifyPropertyChanged use)
If you want to disable this by default on all objects then you can use Mvx.Resolve<IMvxSettings>().AlwaysRaiseInpcOnUserInterfaceThread = false;
If during testing you want to provide a mock implementation for the UI thread marshalling, then see for example the N=29 video in http://mvvmcross.blogspot.co.uk/ - with some MockDispatcher code inside https://github.com/MvvmCross/NPlus1DaysOfMvvmCross/tree/master/N-29-TipCalcTest/TipCalcTest.Tests

mvvmcross custom binding to eventhandler

I am trying to implement LongClick functionality on a view and read the following which provided some info
mvvmcross touch command binding in android
Searched unsuccessfully for IMvxCommand within the code so assume this may be outdated? So I attempted a best effort but cannot get any LongClick functionality - probably due to limited knowledge of C# and eventhandlers. I implemented the following but was not sure of the MvxRelayCommand usage.
public class LongClickEventBinding: MvxBaseAndroidTargetBinding
{
private readonly View _view;
private MvxRelayCommand<JobJob> _command;
public LongClickEventBinding(View view)
{
_view = view;
_view.LongClick += ViewOnLongClick;
}
private void ViewOnLongClick(object sender, View.LongClickEventArgs eventArgs)
{
if (_command != null)
{
_command.Execute();
}
}
public override void SetValue(object value)
{
_command = (MvxRelayCommand<JobJob>)value;
}
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
_view.LongClick -= ViewOnLongClick;
}
base.Dispose(isDisposing);
}
public override Type TargetType
{
get { return typeof(MvxRelayCommand<JobJob>); }
}
public override MvxBindingMode DefaultMode
{
get { return MvxBindingMode.OneWay; }
}
}
And
protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry)
{
base.FillTargetFactories(registry);
registry.RegisterFactory(new MvxCustomBindingFactory<View>("LongClick", view => new LongClickEventBinding(view)));
}
And
public ICommand JobSelectedCommand
{
get { return new MvxRelayCommand<JobJob>(NavigateToJobTasks); }
}
public void NavigateToJobTasks(JobJob jobJob)
{
RequestNavigate<JobTaskListViewModel>(new { key = jobJob.JobID });
}
And
<Mvx.MvxBindableListView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
local:MvxBind="{'ItemsSource':{'Path':'GroupedList'},'LongClick':{'Path':'JobSelectedCommand'}}"
local:MvxItemTemplate="#layout/listitem_job_old"/>
However when I run code on the emulator and LongClick mouse button on listitem not much happens.
Does the following need to be implemented in the View
public event EventHandler<View.LongClickEventArgs> LongClick;
Any help / pointers appreciated.
For lists, vNext MvxBindableListView has supported ItemLongClick for a while anyway - see
https://github.com/slodge/MvvmCross/blob/vnext/Cirrious/Cirrious.MvvmCross.Binding.Droid/Views/MvxBindableListView.cs#L77
Note that this binding hooks into the ListView's ItemLongClick rather than into LongClick
Using this in your axml, you should be able to just do:
<Mvx.MvxBindableListView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
local:MvxBind="{'ItemsSource':{'Path':'GroupedList'},'ItemLongClick':{'Path':'JobSelectedCommand'}}"
local:MvxItemTemplate="#layout/listitem_job_old"/>
If this doesn't work then please fire a bug report on Github issues.
If you wanted to do your custom binding on a generic (non list) View, then your code would need to switch to ICommand instead of IMvxCommand, and you also couldn't really pass in the Item argument - so you'd need to just use MvxRelayCommand on the ViewModel.
I've added View-level LongClick support to the issues list - https://github.com/slodge/MvvmCross/issues/165
But for a ListView it is probably the ItemLongClick you are actually interested in

MvvmCross Monotouch C# - Binding Int Property - Mode: TwoWay

I am new to MvvmCross and I have a question.
I noticed that the following binding code works in one way only:
{ this, "{'CurrentIndex':{'Path':'CurrentIndex','Mode':'TwoWay'}}" }
CurrentIndex is an Int Property in the View
CurrentIndex is also an Int Property in the ViewModel
This way works!
ViewModel => View
But not this way!
View => ViewModel
I have a collection of ViewControllers and my goal was to call a DeleteCommand for the CurrentIndex in the viewModel.
However,
"Android and Touch 2 way bindings are incomplete"
Reference: MvvmCross experiences, hindsight, limitations?
My guess is the TwoWay mode only works for Controls (UILabel, UITextfield, ...) but not for Properties.
So, is there a good way to make it works in both ways? Or Are there any alternatives to my problem?
Patrick
In order for a binding to transfer any value between a View to a ViewModel, then it needs to hook into some event when the value changes.
In the ViewModel, this event is always the event in the INotifyProperty interface.
In the View/Activity, there is one single pattern employed - so each binding has to hook into a separate event. For example, the Text on EditText is hooked up using the TextChanged event (see MvxEditTextTextTargetBinding.cs) while the value in a SeekBar is hooked up using a Listener object rather than an event (see MvxSeekBarProgressTargetBinging.cs).
So if you wanted to implement this two-way binding for your activity, then you could do this by:
declaring an event - CurrentIndexChanged - in your activity (MyActivity) which is fired whenever CurrentIndex changes
declare a custom binding for your MyActivity which programmatically links CurrentIndex and CurrentIndexChanged
adding the custom binding to the binding registry during Setup
For example, your activity might include:
public event EventHandler CurrentIndexChanged;
private int _currentIndex;
public int CurrentIndex
{
get { return _currentIndex; }
set { _currentIndex = value; if (CurrentIndexChanged != null) CurrentIndexChanged(this, EventArgs.Empty); }
}
And you might then declare a binding class like:
public class MyBinding : MvxPropertyInfoTargetBinding<MyActivity>
{
public MyBinding (object target, PropertyInfo targetPropertyInfo)
: base(target, targetPropertyInfo)
{
View.CurrentIndexChanged += OnCurrentIndexChanged;
}
public override MvxBindingMode DefaultMode
{
get
{
return MvxBindingMode.TwoWay;
}
}
private void OnCurrentIndexChanged(object sender, EventArgs ignored)
{
FireValueChanged(View.CurrentIndex);
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (isDisposing)
{
View.CurrentIndexChanged -= OnCurrentIndexChanged;
}
}
}
And you'd need to tell the binding system about this binding in setup like:
registry.RegisterFactory(new MvxSimplePropertyInfoTargetBindingFactory(typeof(MyBinding), typeof(MyActivity), "CurrentIndex"));
However... at a practical level, if you are operating in C# rather than in XML, then you might be better off in this case using C# to simply update the ViewModel rather than using declarative binding in this case.
To be clear... in this case, I would most probably just write the Activity property as:
public int CurrentIndex
{
get { return _currentIndex; }
set { _currentIndex = value; ViewModel.CurrentIndex = value; }
}
Or... I'd consider not having this property in the Activity at all.
If it helps, there's some more information on custom bindings in:
MonoTouch MVVMCross binding to instance variables
In MvvmCross how do I do custom bind properties
Hope this helps! IMHO the bindings are there to help you when you're working in XML - you don't have to use them...
Stuart
UPDATE If you are going to do lots of these and follow the same name pattern - using property named X with changed EventHandler event named XChanged then something like this might work - it uses reflection to find the event automagically:
public class MyBinding<T> : MvxPropertyInfoTargetBinding<T>
where T : class
{
private readonly PropertyInfo _propertyInfo;
private readonly EventInfo _eventInfo;
public MyBinding(object target, PropertyInfo targetPropertyInfo)
: base(target, targetPropertyInfo)
{
_propertyInfo = targetPropertyInfo;
var eventName = _propertyInfo.Name + "Changed";
_eventInfo = View.GetType().GetEvent(eventName);
if (_eventInfo == null)
{
throw new MvxException("Event missing " + eventName);
}
if (_eventInfo.EventHandlerType != typeof(EventHandler))
{
throw new MvxException("Event type mismatch for " + eventName);
}
var addMethod = _eventInfo.GetAddMethod();
addMethod.Invoke(View, new object[] { new EventHandler(OnChanged) });
}
public override MvxBindingMode DefaultMode
{
get
{
return MvxBindingMode.TwoWay;
}
}
private void OnChanged(object sender, EventArgs ignored)
{
var value = _propertyInfo.GetValue(View, null);
FireValueChanged(value);
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (isDisposing)
{
var removeMethod = _eventInfo.GetRemoveMethod();
removeMethod.Invoke(View, new object[] { new EventHandler(OnChanged) });
}
}
}

Using IoC container as a service locator for HttpHandler

This question relates to my other post.
Ok so after a bit more messing around I decided to do it this way. Which seems to work fine when I run it, although I'm getting the following error in NUnit: Could not load file or assembly 'Castle.Core, Version=1.0.3.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040) So not sure what is happening there???
Just wanted to know what others thought about the design and if there are any obvious 'no no's' or improvements. I.e. Is the constructor of the base handler a good place to instantiate the windsor component or is there a better place to do this? As I said in the original post the idea behind doing things this way was to keep the components nicely decoupled and to make unit testing easy. I should also add I'm new to unit testing, mocking. Thanks!
public abstract class BaseHttpHandler : IHttpHandler
{
private HttpContext _httpContext;
private ILogger _logger;
private IDataRepository _dataRepository;
protected HttpRequest Request { get { return _httpContext.Request; } }
protected HttpResponse Response { get { return _httpContext.Response; } }
protected bool IsRequestFromUAD { get { return Request.UserAgent == null ? false : Request.UserAgent.Equals("UAD"); } }
protected ILogger Logger { get { return _logger; } }
protected IDataRepository DataRepository { get { return _dataRepository; } }
public virtual bool IsReusable { get { return false; } }
public BaseHttpHandler()
{
var container = new WindsorContainer(new XmlInterpreter(new ConfigResource("castle")));
_logger = container.Resolve<ILogger>();
_dataRepository = container.Resolve<IDataRepository>();
}
public void ProcessRequest(HttpContext context)
{
_httpContext = context;
ProcessRequest(new HttpContextWrapper(context));
}
public abstract void ProcessRequest(HttpContextBase context);
}
public class UADRecordHttpHandler : BaseHttpHandler
{
public override void ProcessRequest(HttpContextBase context)
{
if (IsRequestFromUAD)
{
using (var reader = new StreamReader(context.Request.InputStream))
{
string data = reader.ReadToEnd();
if (Logger != null)
Logger.Log(data);
if(DataRepository != null)
DataRepository.Write(data);
context.Response.Write(data);
}
}
else
ReturnResponse(HttpStatusCode.BadRequest);
}
}
That's a very bad thing to do, what you're doing here. You should have one instance of the container per application, while with this code you will have one per each request.
About the error in NUnit: make sure you don't have other versions of Castle assemblies in the GAC. If so, uninstall them.
About your BaseHttpHandler: the problem with this implementation is that you're creating a new container. Instead, use a single container per application, like Krzysztof said. Use a static service locator, e.g. CommonServiceLocator. (I never recommend this but it's one of the few places where it does make sense).