MvxTableViewCell BackgroudColor binding fails on iPad but work on simulator - mvvmcross

I'm not sure how to approach this problem. The binding works fine on the simulator but fails on the real device - iPad.
public RunSheetItemView (IntPtr handle) : base (handle)
{
this.DelayBind (() => {
var set = this.CreateBindingSet<RunSheetItemView, RunSheetItemViewModel>();
set.Bind(this).For(v => v.BackgroundColor).To(vm => vm.Status).WithConversion("InspectionStatusColorConverter");
set.Apply();
});
}

Found it, the linker optimisation is causing the problem Add the following to the LinkerPleaseInclude.cs file
public class LinkerIncludePlease
{
public void Include(MvxTableViewCell vc)
{
vc.BackgroundColor = UIColor.Blue;
}
}

Related

How to use DependencyResolver in Net48 selfhosted application?

I have gotten a task that contains creating a .Net 4.8 application that contains a "HttpSelfHostServer".
I'm stuck in the quest of assigning "IServiceCollection services" to config.DependencyResolver (of type System.Web.Http.Dependencies.IDependencyResolver)
I would really like not to use autofac or other frameworks, but all guids I can find are pointing toward these frameworks. Isn't Microsoft providing a way through?
I just had to solve the same issue. This is how i did it:
First I created a new facade class to map the IServiceCollection from the host builder to the interface HttpSelfHostConfiguration supports:
using System;
using System.Collections.Generic;
using System.Web.Http.Dependencies;
using Microsoft.Extensions.DependencyInjection;
namespace IntegrationReceiver.WebApi
{
public class HttpSelfHostDependencyResolver : IDependencyResolver
{
private readonly IServiceProvider sp;
private readonly IServiceScope scope;
public HttpSelfHostDependencyResolver(IServiceProvider sp)
{
this.sp = sp;
this.scope = null;
}
public HttpSelfHostDependencyResolver(IServiceScope scope)
{
this.sp = scope.ServiceProvider;
this.scope = scope;
}
public IDependencyScope BeginScope() => new HttpSelfHostDependencyResolver(sp.CreateScope());
public void Dispose() => scope?.Dispose();
public object GetService(Type serviceType) => sp.GetService(serviceType);
public IEnumerable<object> GetServices(Type serviceType) => sp.GetServices(serviceType);
}
}
This required me to get the latest NuGet package Microsoft.Extensions.DependencyInjection.Abstractions according to an answer here: How do I see all services that a .NET IServiceProvider can provide?
I then registered my HttpSelfHostServer in the service provider with this code:
services.AddSingleton(sp => new HttpSelfHostDependencyResolver(sp));
services.AddSingleton(sp =>
{
//Starting the HttpSelfHostServer with user-level permissions requires to first run a command like
// netsh http add urlacl url=http://+:8080/ user=[DOMAINNAME]\[USERNAME]
var config = new HttpSelfHostConfiguration("http://localhost:8080");
config.Routes.MapHttpRoute("API Default", "api/{controller}/{id}", new { id = RouteParameter.Optional });
config.DependencyResolver = sp.GetRequiredService<HttpSelfHostDependencyResolver>();
return new HttpSelfHostServer(config);
});
And finally, to find my ApiController, I had to register that too in the service provider. I did that simply with:
services.AddScoped<HealthCheckController>();
For brewity, I'm just including my api controller below to illustrate how it now gets its dependencies:
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
namespace IntegrationReceiver.WebApi
{
public class HealthCheckController : ApiController
{
private readonly ServiceBusRunner serviceBusRunner;
public HealthCheckController(ServiceBusRunner serviceBusRunner)
{
this.serviceBusRunner = serviceBusRunner;
}
[HttpGet]
public async Task<HttpResponseMessage> Get()
{
var response = new
{
serviceBusRunner.RunningTasks,
serviceBusRunner.MaxRunningTasks
};
return await Json(response)
.ExecuteAsync(System.Threading.CancellationToken.None);
}
}
}
This is a pretty dumb-down implementation but works for me until I can upgrade this code to net5.
I hope it helps you too!

Automapper - Map fails after profile is added

I am using mapping by convention and have issues when adding profile to the configuration. Consider configuration below. I am using AutoFac to resolve profiles in CoreMapper and the profiles are correctly injected.
Mapping configuration
public CoreMapper(IEnumerable<Profile> profiles)
{
MapperConfiguration = new MapperConfiguration(cfg =>
{
cfg.CreateMissingTypeMaps = true;
cfg.AllowNullCollections = true;
cfg.AllowNullDestinationValues = true;
cfg.ForAllMaps((mapType, mapperExpression) =>
{
mapperExpression.PreserveReferences();
mapperExpression.MaxDepth(10);
});
cfg.IgnoreUnmapped();
foreach (var profile in profiles)
{
cfg.AddProfile(profile);
}
cfg.CreateMap<string, string>().ConvertUsing(str => string.IsNullOrEmpty(str) ? null : str);
});
Mapper = MapperConfiguration.CreateMapper();
}
IgnoreUnmapped is as follows:
private static void IgnoreUnmappedProps(TypeMap map, IMappingExpression expression)
{
foreach(var propName in map.GetUnmappedPropertyNames())
{
if (map.SourceType.GetProperty(propName) != null)
expression.ForSourceMember(propName, opt => opt.Ignore());
if (map.DestinationType.GetProperty(propName) != null)
expression.ForMember(propName, opt => opt.Ignore());
}
}
public static void IgnoreUnmapped(this IProfileExpression profile)
{
profile.ForAllMaps(IgnoreUnmappedProps);
}
In my code I have a generic class implementation with the line like this:
mapper.Map<TPoco>(entity);
Note: mapper is an instance of CoreMapper from above.
where TPoco is POCO model defined like:
public class ModelPoco : IModel {
// props
}
and entity is database entity model.
The mapping works fine. The result of mapper.Map< TPoco>(entity) is correct.
I then proceed and add a profile for a specific map that is not at all related to the ModelPoco being mapped before.
The profile being added:
public class RepositoryLayerProfile : AutoMapper.Profile {
public RepositoryLayerProfile() {
CreateMap<SomeOtherEntity, ISomeOtherModelInterface>();
}
}
Version: 6.2.2
Expected behavior
Mapping of ModelEntity to ModelPoco should happen normally.
Actual behavior
The mapping breaks with the message:
Unable to cast object of type 'Proxy< IModel>' to type 'ModelPoco'. on line mapper.Map(entity);
I am not sure why does it create Proxy class for IModel interface and then tries to cast to concrete implementation?
I explicitly set that I want to map entity to a concrete implementation.
If I remove CreateMap from the profile it works fine, but the first time I define CreateMap in the said profile, it breaks.
It seems to me like it forgets about the configuration for some reason even tho it has nothing to do with it.

Init not being called when using ShowViewModel<T> in MVVMCross MvX

i'm seeing some weirdness in my Windows Phone app with MVVMCross.
I use ShowViewModel<MyViewModel>(); to load a new view on a command being executed.
I've changed that to:
ShowViewModel<MyViewModel>(new { First = "Hello", Second = "World", Answer = 42 });
But Init isn't being called in the MyViewModel, MyViewModel inherits from another class that in turn inherits from a MvxViewModel, I've even changed both view models to inherit directly from MvxViewModel.
If i used:
Mvx.Resolve<IMvxViewModelLoader>().LoadViewModel(MvxViewModelRequest<MyViewModel>.GetDefaultRequest(), null);
Init get's called, same for the InitFromBundle, I passed a bundle containing that test object but I didn't get the values passed through.
Init method just looks like this:
public void Init(string First, string Second, int Answer)
I'm totally confused, setup, app.cs all look like the navigation example, any ideas what I might have forgot?
Windows 8.1, VS 2013, Hot Tuna, Windows phone 8.
Init methods:
public void Init()
{
}
public void Init(string First, string Second, int Answer)
{
// use the values
var meh = "";
Mvx.Trace("Init called in {0}", GetType().Name);
}
protected override void InitFromBundle(IMvxBundle bundle)
{
}
You should use ShowViewModel<NameOfViewModelClass>(new { First = "Hello", Second = "World", Answer = 42 }); instead

Query String Not Getting Set - Cimbalino Windows Phone

I am using Cimbalino navigation but the query param never gets set for me.
Main View Model
private readonly INavigationService navigationService = null;
public MainViewModel(INavigationService navigationService)
{
this.navigationService = navigationService;
NavigateToPg2Cmd = new RelayCommand(() => NaviagateToPg2());
NavigateToPg2WithParmsCmd = new RelayCommand(() => NaviagateToPg2WithParms());
}
private void NaviagateToPg2WithParms()
{
navigationService.NavigateTo(new Uri("/Views/SecondPg.xaml?parameter=1&parm2=2", UriKind.Relative));
}
When I look into NavigationService the Query Param dictionary is always 0.
static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (ViewModelBase.IsInDesignModeStatic)
{
}
else
{
}
SimpleIoc.Default.Register<INavigationService, NavigationService>();
SimpleIoc.Default.Register<MainViewModel>();
SimpleIoc.Default.Register<SecondVM>();
}
Edit
Ok, I figured it out. When NavigateTo runs it still has not split the query string out yet so that's why it is zero.
I was also trying to do
private readonly INavigationService navigationService = null;
public SecondVM(INavigationService navigationService)
{
this.navigationService = navigationService;
if (IsInDesignMode)
{
Message = "Design Mode";
}
else
{
if (navigationService.QueryString.ContainsKey("paramter"))
{
Message = navigationService.QueryString["parameter"];
}
}
}
what did not work either as I guess it was too early as well. I really would like to pull it out at constructor time though, is there a way to do this?
I know it's not 100% the solution you are looking for, but you are true... You'll need to wait until the view is loaded before accessing the QueryString params in the ViewModel!
To do this, hook into the Loaded event of the view and pass it to a Command on the viewmodel!
If created a demo of this on my github to get you started: https://github.com/Depechie/NavigationParams

Bind to action method

Is it possible to use a simple action method - just like with Caliburn.Micro - instead of a command with MvvmCross bindings?
Example:
public void Action()
{
Tip = 11;
}
<Button
android:text="Button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/button1"
local:MvxBind="Click Action" />
It doesn't work out of the box, I tested that.
While I found a lot of samples about adding new target bindings, I didn't find a single one about adding a new source binding.
UPDATE:
This works now out of the box with the Rio binding. To use it, add the MvvmCross MethodBinding NuGet package to the Android project.
Up until now, much of the emphasis for MvvmCross has been on allowing multi-platform target binding with the source remaining mainly 'vanilla' INotifyPropertyChanged.
There have been some deviation in terms of ViewModel structure - e.g.:
the MvxCommandCollection - http://slodge.blogspot.co.uk/2013/03/fixing-mvvm-commands-making-hot-tuna.html
some users using Fody - http://twincoders.com/blog/codigo-limpio-con-fody/
Recently, several new feature requests have also been logged in this area:
AutoCommands - I think this is what you are asking about here - https://github.com/slodge/MvvmCross/issues/301
Rio binding sources - https://github.com/slodge/MvvmCross/issues/299
Tibet binding - https://github.com/slodge/MvvmCross/issues/298
Because of these, I do expect more functionality to be exposed in this area in the future...
With that said, if you wanted to get this working today, then MvvmCross Binding is overrideable so you could fairly easily do it:
1. Implement an ICommand that invokes a MethodInfo using reflection (for completeness this should probably also use a parameter if available) - some kind of InvokeMethodCommand (code for this left to the reader!)
.
2. Implement an MyMethodSourceBinding class which wraps the InvokeMethodCommand - something like:
public class MyMethodSourceBinding : MvxSourceBinding
{
private readonly MethodInfo _methodInfo;
protected MyMethodSourceBinding(object source, MethodInfo methodInfo)
: base(source)
{
_methodInfo = _methodInfo;
}
public override void SetValue(object value)
{
// do nothing - not allowed
}
public override Type SourceType
{
get { return typeof(ICommand); }
}
public override bool TryGetValue(out object value)
{
value = new InvokeMethodCommand(source, _methodInfo);
return true;
}
}
3. Override MvvmCross's registered IMvxSourceBindingFactory with your own implementation that can detect when a method is present - sadly most of this is cut and paste coding today - it would be something like
public class MySourceBindingFactory
: IMvxSourceBindingFactory
{
private IMvxSourcePropertyPathParser _propertyPathParser;
private IMvxSourcePropertyPathParser SourcePropertyPathParser
{
get
{
if (_propertyPathParser == null)
{
_propertyPathParser = Mvx.Resolve<IMvxSourcePropertyPathParser>();
}
return _propertyPathParser;
}
}
public IMvxSourceBinding CreateBinding(object source, string combinedPropertyName)
{
var tokens = SourcePropertyPathParser.Parse(combinedPropertyName);
return CreateBinding(source, tokens);
}
public IMvxSourceBinding CreateBinding(object source, IList<MvxPropertyToken> tokens)
{
if (tokens == null || tokens.Count == 0)
{
throw new MvxException("empty token list passed to CreateBinding");
}
var currentToken = tokens[0];
if (tokens.Count == 1)
{
return CreateLeafBinding(source, currentToken);
}
else
{
var remainingTokens = tokens.Skip(1).ToList();
return CreateChainedBinding(source, currentToken, remainingTokens);
}
}
private static MvxChainedSourceBinding CreateChainedBinding(object source, MvxPropertyToken propertyToken,
List<MvxPropertyToken> remainingTokens)
{
if (propertyToken is MvxIndexerPropertyToken)
{
return new MvxIndexerChainedSourceBinding(source, (MvxIndexerPropertyToken) propertyToken,
remainingTokens);
}
else if (propertyToken is MvxPropertyNamePropertyToken)
{
return new MvxSimpleChainedSourceBinding(source, (MvxPropertyNamePropertyToken) propertyToken,
remainingTokens);
}
throw new MvxException("Unexpected property chaining - seen token type {0}",
propertyToken.GetType().FullName);
}
private static IMvxSourceBinding CreateLeafBinding(object source, MvxPropertyToken propertyToken)
{
if (propertyToken is MvxIndexerPropertyToken)
{
return new MvxIndexerLeafPropertyInfoSourceBinding(source, (MvxIndexerPropertyToken) propertyToken);
}
else if (propertyToken is MvxPropertyNamePropertyToken)
{
//**************************
// Special code is here
var propertyToken = (MvxPropertyNamePropertyToken) propertyToken;
if (source != null)
{
var method = source.GetType().GetMethod(propertyToken.PropertyName, BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance);
if (method != null)
{
return new MyMethodSourceBinding(source, method);
}
}
return new MvxSimpleLeafPropertyInfoSourceBinding(source,
(MvxPropertyNamePropertyToken) propertyToken);
// Special code ends here
//**************************
}
else if (propertyToken is MvxEmptyPropertyToken)
{
return new MvxDirectToSourceBinding(source);
}
throw new MvxException("Unexpected property source - seen token type {0}", propertyToken.GetType().FullName);
}
}
4. Supply this source binding factory in your own custom binding builder - e.g.:
public class MyAndroidBindingBuilder
: MvxAndroidBindingBuilder
{
protected override IMvxSourceBindingFactory CreateSourceBindingFactory()
{
return new MvxSourceBindingFactory();
}
}
5. Supply this binding builder during your setup
public class Setup : MvxAndroidSetup
{
// ....
protected override MvxAndroidBindingBuilder CreateBindingBuilder()
{
return new MyAndroidBindingBuilder();
}
}
Note: This approach is only for advanced users right now... As suggested in the first part of this question, I do expect the code in this area to change quite a lot so you might also encounter some issues maintaining a fork in this area. (Indeed the code in this area has already changed quite significantly on the Tibet Binding branch within the GitHub repo!)