How to bind imageSource to ImageView in MvvmCross - uiimageview

I'm having trouble to bind a source with converter to an imageview in my Touch project. I found here and here how to make it in Android
public class TypeToSourceConverter : MvxValueConverter<int, string>
{
protected override string Convert (int value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var assetName = ((AppConstants.Type)value).ToString ().ToLower ();
return "Images/" + assetName + ".png";
}
}
But I'm having trouble in how to use this converter in iOS, i just found samples with ImageURL, I'm starting in iOS now, so I have some stupid questions as how I bind the image with this converter and where should I put the image in the Touch project! Is it in Resource/Images/*.png ?
Thanks in Regards,
Gabriel
EDIT
Well, what I did, to try to solve it is:
I'm passing as parameter in Droid and in iOS, a number, so in converter I do:
protected override string Convert (int value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var assetName = ((AppConstants.Type)value).ToString ().ToLower ();
switch(int)
{
case 0://Droid
return "Images/" + assetName + ".png";
case 1://Touch
return "res:Images/" + assetName + ".png";
default:
return "";
}
And in my touch Table View Cell, i have:
var imageViewLoader = new MvxImageViewLoader (() => historicTypeImageView);
Opaque = true;
this.DelayBind(() => {
var set = this.CreateBindingSet<HistoricCell, Historic>();
set.Bind(imageViewLoader).For(i => i.DefaultImagePath).To(historic => historic.UpdateType).WithConversion("HistoricTypeToSource", 2);
set.Apply();
}
Now, the problem is: I figured out it was working only for the first cells, not all of them, like, it was replicating the first imageViews that was appearing for all cells, instead of bind each cell's image for each Type ...
I saw in the output, that i'm receiving:
2014-04-18 14:40:45.902 MyProjectTouch[23081:70b] MvxBind:Warning: 30.59 Weak Target is null in MvxWithEventPropertyInfoTargetBinding - skipping set
So probably, it is because of this weak target that it is skipping the bind of others cell... I'm looking for answers but didn't find yet. Actually found this this but not sure it works to fix my problem..
Can anyone help me, please!

The solution is exactly, what Stuart suggested here:
Try making your MvxImageViewLoader a member variable of the cell - otherwise it will go out of scope and get garbage collected
Here is also part of the MVVM Cross tutorial, where the presenter runs into the same issue while live-coding and explains it by the same reasons: https://www.youtube.com/watch?v=iFEE355587k#t=1443

Related

Castle Core Invocation create a cache key from intercepted method

I'm using a interface interceptor to cache all methods that starts with "Get" but i can't figure out how to generate a unique cache key for every unknown parameter it can be anything and using GetHashCode is not an option as i can't be 100% sure that they have overridden the GetHashCode.
some thoughts was someting in the line of How can I create a unique hashcode for a JObject?
where JSON is used for a JObject i was thinking on JSON serialize every parameter then get the hash code like explained in the link above:
var obj = JToken.Parse(jsonString);
var comparer = new JTokenEqualityComparer();
var hashCode = comparer.GetHashCode(obj);
However i think this will be a performence hit so how can this be solved ?
The code i have so far is this but it wont handle the complex type situation where .ToString won't generate the raw value type like int, string etc.
private string CreateCacheKey(IInvocation invocation)
{
string className = invocation.TargetType.FullName;
string methodName = invocation.Method.Name;
var builder = new StringBuilder(100);
builder.Append(className);
builder.Append(".");
builder.Append(methodName);
for (int i = 0; i < invocation.Arguments.Length; i++)
{
var argument = invocation.Arguments[i];
var argumentValue = invocation.GetArgumentValue(i);
builder.Append("_");
builder.Append(argument);
if (argument != argumentValue)
{
builder.Append(argumentValue);
}
}
return string.Format("{0}-{1}", this.provider.CacheKey, builder);
}
I ended up using GetHashCode as it is the only viable solution if thay dont override the method it will not cache.

How to get a standalone / unmanaged RealmObject using Realm Xamarin

Is there a way that when I read an object from Realm that it can become a standalone or unmanaged object? In EF, this is called no tracking. The usage for this would be when I want to implement more business logic on my data objects before they are updated on the persistent data storage. I may want to give the RealmObject to a ViewModel, but when the changes come back from the ViewModel, I want to compare the disconnected object to the object in the datastore to determine what was changed, so If there was a way that I could disconnect the object from Realm when I give it to the ViewModel, then I can better manage what properties have changed, using my biz logic to do what I need, then save the changes back to realm.
I understand Realm does a lot of magic and many people will not want to add a layer like this but in my app, I cant really have the UI directly updating the datastore, unless there is a event that is raised that I can subscribe too and then attach my business logic this way.
I only saw one event and it does not appear to perform this action.
Thanks for your assistance.
First, get json NUGET :
PM> Install-Package Newtonsoft.Json
And, try this "hack" :
Deserialize the modified IsManaged property does the tricks.
public d DetachObject<d>(d Model) where d : RealmObject
{
return Newtonsoft.Json.JsonConvert.DeserializeObject<d>(
Newtonsoft.Json.JsonConvert.SerializeObject(Model)
.Replace(",\"IsManaged\":true", ",\"IsManaged\":false")
);
}
.
If you facing slow-down on JsonConvert:
According to source code
, the 'IsManaged' property only has get accessor and return true when private field _realm which is available
So, we has to set the instance of field _realm to null does the tricks
public d DetachObject<d>(d Model) where d : RealmObject
{
typeof(RealmObject).GetField("_realm",
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)
.SetValue(Model, null);
return Model.IsManaged ? null : Model;
}
.
You will get empty RealmObject body after Realm are now implemented same strategy as LazyLoad
Record down live RealmObject and (deactivate) realm instance in object by Reflection. And set back recorded values to RealmObject. With handled all the ILists inside too.
public d DetachObject<d>(d Model) where d : RealmObject
{
return (d)DetachObjectInternal(Model);
}
private object DetachObjectInternal(object Model)
{
//Record down properties and fields on RealmObject
var Properties = Model.GetType().GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public)
.Where(x => x.Name != "ObjectSchema" && x.Name != "Realm" && x.Name != "IsValid" && x.Name != "IsManaged" && x.Name != "IsDefault")
.Select(x =>(x.PropertyType.Name == "IList`1")? ("-" + x.Name, x.GetValue(Model)) : (x.Name, x.GetValue(Model))).ToList();
var Fields = Model.GetType().GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public)
.Select(x => (x.Name, x.GetValue(Model))).ToList();
//Unbind realm instance from object
typeof(RealmObject).GetField("_realm", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).SetValue(Model, null);
//Set back the properties and fields into RealmObject
foreach (var field in Fields)
{
Model.GetType().GetField(field.Item1, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public).SetValue(Model, field.Item2);
}
foreach (var property in Properties.OrderByDescending(x=>x.Item1[0]).ToList())
{
if (property.Item1[0] == '-')
{
int count = (int)property.Item2.GetType().GetMethod("get_Count").Invoke(property.Item2, null);
if (count > 0)
{
if (property.Item2.GetType().GenericTypeArguments[0].BaseType.Name == "RealmObject")
{
for (int i = 0; i < count; i++)
{
var seter = property.Item2.GetType().GetMethod("set_Item");
var geter = property.Item2.GetType().GetMethod("get_Item");
property.Item2.GetType().GetField("_realm", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public).SetValue(property.Item2, null);
DetachObjectInternal(geter.Invoke(property.Item2, new object[] { i }));
}
}
}
}
else
{
Model.GetType().GetProperty(property.Item1, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public).SetValue(Model, property.Item2);
}
}
return Model;
}
.
For List of the RealmObject , Using Select():
DBs.All<MyRealmObject>().ToList().Select(t => DBs.DetachObject(t)).ToList();
.
(Java)You dont need this if youre in java:
Maybe some day, this feature will come to .NET Realm
Realm.copyFromRealm();
#xamarin #C# #Realm #RealmObject #detach #managed #IsManaged #copyFromRealm
Until its added to Realm for Xamarin, I added a property to my Model that creates a copy of the object. This seems to work for my use. The TwoWay Binding error messages are now also not an issue. For a more complicated application, I don't want to put business or data logic in the ViewModel. This allows all the Magic of xamarin forms to work and me to implement logic when its finally time to save the changes back to realm.
[Ignored]
public Contact ToStandalone()
{
return new Contact()
{
companyName = this.companyName,
dateAdded = this.dateAdded,
fullName = this.fullName,
gender = this.gender,
website = this.website
};
}
However, If there are any relationships this method does not work for the relationships. Copying the List is not really an option either as the relationship cant exist if the object is not attached to Realm, I read this some where, can't find it to ref now. So I guess we will be waiting for the additions to the framework.
Not currently in the Xamarin interface but we could add it. The Java interface already has copyFromRealm which performs a deep copy. That also has a paired merging copyToRealmOrUpdate.
See Realm github issue for further discussion.
However, as a design issue, is this really meeting your need in an optimal way?
I have used converters in WPF apps to insert logic into the binding - these are available in Xamarin Forms.
Another way in Xamarin forms is to use Behaviours, as introduced in the blog article and covered in the API.
These approaches are more about adding logic between the UI and ViewModel, which you could consider as part of the ViewModel, but before updates are propagated to bound values.
After wasting too much time in 3rd party libraries like AutoMapper, I've created my own extension function which works pretty well. This function simply uses Reflection with the recession. (Currently, Only for List type. You can extend the functionality for Dictionary and other types of the collection very easily or you can completely modify the functionality based on your own requirements.).
I didn't do too much time and complexity analysis. I've tested only for my test case which contains many nested RealmObject, built from a 3500+ line of JSON object, took only 15 milliseconds to clone the object.
Here the complete extension available via Github Gist. If you want to extend the functionality of this extension please update this Github Gist, So, other developers can take advantage of it.
Here the complete extension -
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using Realms;
namespace ProjectName.Core.Extensions
{
public static class RealmExtension
{
public static T Clone<T>(this T source) where T: new()
{
//If source is null return null
if (source == null)
return default(T);
var target = new T();
var targetType = typeof(T);
//List of skip namespaces
var skipNamespaces = new List<string>
{
typeof(Realm).Namespace
};
//Get the Namespace name of Generic Collection
var collectionNamespace = typeof(List<string>).Namespace;
//flags to get properties
var flags = BindingFlags.IgnoreCase | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance;
//Get target properties list which follows the flags
var targetProperties = targetType.GetProperties(flags);
//if traget properties is null then return default target
if (targetProperties == null)
return target;
//enumerate properties
foreach (var property in targetProperties)
{
//skip property if it's belongs to namespace available in skipNamespaces list
if (skipNamespaces.Contains(property.DeclaringType.Namespace))
continue;
//Get property information and check if we can write value in it
var propertyInfo = targetType.GetProperty(property.Name, flags);
if (propertyInfo == null || !property.CanWrite)
continue;
//Get value from the source
var sourceValue = property.GetValue(source);
//If property derived from the RealmObject then Clone that too
if (property.PropertyType.IsSubclassOf(typeof(RealmObject)) && (sourceValue is RealmObject))
{
var propertyType = property.PropertyType;
var convertedSourceValue = Convert.ChangeType(sourceValue, propertyType);
sourceValue = typeof(RealmExtension).GetMethod("Clone", BindingFlags.Static | BindingFlags.Public)
.MakeGenericMethod(propertyType).Invoke(convertedSourceValue, new[] { convertedSourceValue });
}
//Check if property belongs to the collection namespace and original value is not null
if (property.PropertyType.Namespace == collectionNamespace && sourceValue != null)
{
//get the type of the property (currently only supported List)
var listType = property.PropertyType;
//Create new instance of listType
var newList = (IList)Activator.CreateInstance(listType);
//Convert source value into the list type
var convertedSourceValue = Convert.ChangeType(sourceValue, listType) as IEnumerable;
//Enumerate source list and recursively call Clone method on each object
foreach (var item in convertedSourceValue)
{
var value = typeof(RealmExtension).GetMethod("Clone", BindingFlags.Static | BindingFlags.Public)
.MakeGenericMethod(item.GetType()).Invoke(item, new[] { item });
newList.Add(value);
}
//update source value
sourceValue = newList;
}
//set updated original value into the target
propertyInfo.SetValue(target, sourceValue);
}
return target;
}
}
}

How to use Expressions to invoke a method call with a generic list as the parameter?

We're using the very Excellent ToStringBuilder in our project as a performant, generic backing for our ToString implementations. It worked fine for debugging until I needed to generate a string representation of an object graph to check if it had changed in between loading and closing. Previously I had used a MemoryStream to write the object out to xml, but this seemed heavyweight so I decided to try out using ToStringBuilder, which is where I hit a showstopper...
Our object graph uses generic typed lists heavily, so when the lists are printed out they look like the following:
PropertyName:{System.Collections.Generic.List`1[Namespace.Path.To.MyClassDto]}
Instead of enumerating through the list and invoking ToString on each object, which is fine as that's default behaviour (btw, ToStringBuilder supports object[], but we don't want to retrofit our entire Dto layer just to fix this problem).
I tried to patch the code in question (ToStringBuilder.cs, line 177) to recognise when the type is a generic list, and then invoke string.Join(", ", list), but I couldn't get my head around how the linq reflection API handles generics.
The first thing I tried was to get a handle to the String.Join(IEnumerable<>) method like this:
var stringJoinMethod = typeof(string).GetMethod("Join", new[] { typeof(string), typeof(IEnumerable<>) });
But GetMethod returned null so that didn't work. I eventually found this StackOverflow question that showed me how to get a generic method by signature (call getmethods() instead and filter the results). That got me the correct method handle, so I tried to do something like this:
private void AppendMember(MemberInfo memberInfo)
{
AppendQuotesIfRequiredForType(memberInfo);
Type type = GetMemberType(memberInfo);
var memberAppendMethod = typeof(StringBuilder).GetMethod("Append", new[] { type });
Expression getMemberValue = Expression.MakeMemberAccess(TargetArgExpression, memberInfo);
if (type.IsValueType)
{
Type appendArgType = memberAppendMethod.GetParameters()[0].ParameterType;
if (type != appendArgType)
{
getMemberValue = Expression.TypeAs(getMemberValue, typeof(object));
}
//my code begins here.
_appendExpressions.Add(Expression.Call(SbArgExpression, memberAppendMethod, getMemberValue));
}
else if (type.IsGenericType && (type.GetGenericTypeDefinition() == typeof(List<>)))
{
// now to emit some code to do the below, you wouldn't think it'd be this hard...
// string.Join(", ", genericList);
AppendStartOfMembers();
//this returns null, because generics are not well supported by the reflection API, boo!
var stringJoinMethod = typeof(string).GetGenericMethod("Join", new[] { typeof(string), typeof(IEnumerable<>) });
var CommaSpace = Expression.Constant(", ");
// this doesn't work, throws an ArgumentException as below
getMemberValue = Expression.Call(stringJoinMethod, CommaSpace, getMemberValue);
_appendExpressions.Add(Expression.Call(SbArgExpression, memberAppendMethod, getMemberValue));
AppendEndOfMembers();
}
else
{
//primitives like strings
_appendExpressions.Add(Expression.Call(SbArgExpression, memberAppendMethod, getMemberValue));
}
//my code ends here.
AppendQuotesIfRequiredForType(memberInfo);
}
This errors with the following exception:
System.ArgumentException: "Method System.String Join[T](System.String, System.Collections.Generic.IEnumerable`1[T]) is a generic method definition"
at System.Linq.Expressions.Expression.ValidateMethodInfo(MethodInfo method)
at System.Linq.Expressions.Expression.ValidateMethodAndGetParameters(Expression instance, MethodInfo method)
at System.Linq.Expressions.Expression.Call(MethodInfo method, Expression arg0, Expression arg1)
at MyNameSpace.Common.ToStringBuilder`1.AppendMember(MemberInfo memberInfo) in C:\myproject\MyNamespace.Common\ToStringBuilder.cs:line 206
I started googling that error message and found people talking about using Expression.Lamba() to wrap calls to generic methods, at which point I realised I was way out of my depth.
So, assuming I have a List mylist, how do I generate an Expression as above that will do the equivalent of string.Join(", ", mylist); ?
thanks!
To get the "generic type" of your generic list (the type of "T"), you can do
var genericListType= type.GetGenericArguments()[0];
so in your elseif, you could do (it might be easier, I just stay as close as possible to your code)
else if (type.IsGenericType && (type.GetGenericTypeDefinition() == typeof(List<>)))
{
// now to emit some code to do the below, you wouldn't think it'd be this hard...
// string.Join(", ", genericList);
AppendStartOfMembers();
//this returns null, because generics are not well supported by the reflection API, boo!
var stringJoinMethod = typeof(string).GetGenericMethod("Join", new[] { typeof(string), typeof(IEnumerable<>) });
var CommaSpace = Expression.Constant(", ");
var genericListType= type.GetGenericArguments()[0];
var genericStringJoinMethod = stringJoinMethod.MakeGenericMethod(new[]{genericListType});
// this doesn't work, throws an ArgumentException as below
getMemberValue = Expression.Call(genericStringJoinMethod , CommaSpace, getMemberValue);
_appendExpressions.Add(Expression.Call(SbArgExpression, memberAppendMethod, getMemberValue));
AppendEndOfMembers();
}

Set value in model when dynamically creating object of model

I have a bunch of models that may or may not have a property "CommonProperty". I want to set that property when I am creating a new object of a selected model. What I have so far, which works is:
ModuleItem model = db.ModuleItems.Find(ModuleItemID);
object o = GetModuleType(model.ControllerName);
private object GetModuleType(string ModelName)
{
string projectName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
Type classtype = Type.GetType(string.Format("{0}.Models.{1}", projectName, ModelName));
PropertyInfo[] properties = classtype.GetProperties();
var classObject = classtype.GetConstructor(new Type[] { }).Invoke(null);
return classObject;
}
What I want to achieve is to set CommonProperty:
o.CommonProperty = DistinctValue;
I tried creating a class that all of my models inherit from with a virtual method and each model then has an override method. Because its not static I can't call it directly and if I create a new ModelBase then when calling the method it doesn't get overriden by the type that object "o" is. I looked at creating an interface but 1 I don't even know how these work, and 2 (probably because of 1) I am not even able to create a way of doing this without build errors.
When stepping through the code I can see all the properties of "o" using the quickwatch or intellisense or whatever it's called. Clearly it is being recognised as the correct type. I can't seem to be able to call the method though because it's not a recognised (or set) type during build only during runtime and therefore I can't build the solution.
What I would do is create a base model (eg: BaseModel) for viewmodels that have the property CommonProperty then check whether the model is of type BaseModel then set the property
if (obj is BaseModel)
{
obj.CommonProperty = DistinctValue;
}
As it turns out I was way overthinking this. Typically the answer is ridiculously simple. No base model necessary, no overrides, no virtuals, no instances, dump it in a try catch and just set the value of the context object.
object o = GetModuleType(model.ControllerName);
db.Entry(o).State = EntityState.Added;
try
{
db.Entry(o).CurrentValues["CommonProperty"] = DistinctValue;
}
catch (Exception err) { }
I did have to make sure I set the state BEFORE setting a current value otherwise it wasn't in the context (or something like that).

Reflection + Linq + DbSet

I use EF code-first 4.1. in my application. Now I want to get entities through WCF services using generic types.
I'm trying to reflect generic type and invoke the method ToList of DbSet Object.
Here is my code:
public string GetAllEntries(string objectType)
{
try
{
var mdc =
Globals.DbConnection.Create(#"some_db_connection", true);
// Getting assembly for types
var asob = Assembly.GetAssembly(typeof(CrmObject));
// getting requested object type from assembly
var genericType = asob.GetType(objectType, true, true);
if (genericType.BaseType == typeof(CrmObject))
{
// Getting Set<T> method
var method = mdc.GetType().GetMember("Set").Cast<MethodInfo>().Where(x => x.IsGenericMethodDefinition).FirstOrDefault();
// Making Set<SomeRealCrmObject>() method
var genericMethod = method.MakeGenericMethod(genericType);
// invoking Setmethod into invokeSet
var invokeSet = genericMethod.Invoke(mdc, null);
// invoking ToList method from Set<> invokeSet
var invokeToList = invokeSet.GetType().GetMember("ToList").Cast<MethodInfo>().FirstOrDefault();
//this return not referenced object as result
return invokeToList.ToString();
}
return null;
}
catch (Exception ex)
{
return ex.Message + Environment.NewLine + ex.StackTrace;
}
}
In fact then I write the code like return mdc.Set<SomeRealCrmObject>().ToList() - works fine for me! But then I use the generic types I cannot find the method ToList in object DbSet<SomeRealCrmObject>().
Eranga is correct, but this is an easier usage:
dynamic invokeSet = genericMethod.Invoke(mdc, null);
var list = Enumerable.ToList(invokeSet);
C# 4's dynamic takes care of the cumbersome generic reflection for you.
ToLIst() is not a member of DbSet/ObjectSet but is an extension method.
You can try this instead
var method = typeof(Enumerable).GetMethod("ToList");
var generic = method.MakeGenericMethod(genericType);
generic.Invoke(invokeSet, null);
I had a similar situation where I needed a way to dynamically load values for dropdown lists used for search criteria on a page allowing users to run adhoc queries against a given table. I wanted to do this dynamically so there would be no code change on the front or backend when new fields were added. So using a dynamic type and some basic reflection, it solved my problem.
What I needed to do was invoke a DbSet property on a DbContext based on the generic type for the DbSet. So for instance the type might be called County and the DbSet property is called Counties. The load method below would load objects of type T (the County) and return an array of T objects (list of County objects) by invoking the DbSet property called Counties. EntityList is just a decorator object that takes a list of lookup items and adds additional properties needed for the grid on the front-end.
public T[] Load<T>() where T : class
{
var dbProperty = typeof(Data.BmpDB).GetProperties().FirstOrDefault(
x => x.GetMethod.ReturnType.GenericTypeArguments[0].FullName == typeof(T).FullName);
if (dbProperty == null)
return null;
dynamic data = dbProperty.GetMethod.Invoke(BmpDb, null);
var list = Enumerable.ToList(data) as List<T>;
var entityList = new Data.EntityList<T>(list);
return entityList.Results;
}

Resources