i'm using MvvmCross on xamarin iOS. I'm using fluent for the bindings on the ViewModel and json. I wanted to try the WithFallback() function, but when the property on my ViewModel (string in this case), comes null or empty, it doesn't do anything. I tried this:
//This works
this.BindLanguage(Header1, "Title");
/* This works when vm.Message is not null or empty,
/* else print nothing, but don't call the WithFallback function
*/
set.Bind(myLbl).For(view => view.Text).To(vm => vm.Message).WithFallback("Something");
set.Apply();
And another question is how i can bind that fallback with a property of the viewmodel or json. Thanks a lot!
Fallback will only be used if the binding fails, not if the property exists and is null or whatever.
You can read more about this in the official documentation.
In your case, I would suggest you use a ValueConverter, something like this will work:
public class MyValueConverter : MvxValueConverter<string, string>
{
protected override string Convert(string value, Type targetType, object parameter, CultureInfo culture)
{
return !string.IsNullOrEmpty(value) ? value : "Something";
}
protected override string ConvertBack(string value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And then your binding:
set.Bind(myLbl).For(view => view.Text).To(vm => vm.Message).WithConversion<MyValueConverter>();
Related
First time using MvvmCross Value Convertors. I've created a value convertor to handle data manipulation between a bool? element in my view and a bool property in my view model.
public sealed class NullableBooleanValueConverter : MvxValueConverter<bool, bool?>
{
// ViewModel -> View
protected override bool? Convert(bool value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (bool?)value;
}
// View -> ViewModel
protected override bool ConvertBack(bool? value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value ?? false;
}
}
And I've bound my view element to the view model when in my view's ViewDidLoad.
var set = this.CreateBindingSet<SettingsView, SettingsViewModel>();
set.Bind(wifiOnlyElement).For(View => View.BooleanValue).To(ViewModel => ViewModel.ConnectOnWifiOnly).WithConversion("NullableBoolean").TwoWay();
set.Apply();
Note: I added the .TwoWay() binding modifier to the set.Bind, thinking the mode might have to be explicitly stated, with no change.
When the view appears the Convert method of NullableBooleanValueConverter is called.
However, when the view is closed, the corresponding ConvertBack method is not called.
Based on this question I suspect that I'm not binding to the correct property. The screen control that I'm creating the binding on is a custom UITableViewCell descended from Xamarin's Dialog/Element classes and the nullable BooleanValue on this screen control is public and that's where the true/false value is stored when the element is tapped.
BooleanValue is a public property on a Dialog/Element class called CheckboxElement that inherits from the base Element class.
NSObject
|_ Element
|_ CheckboxElement
.BooleanValue
There is an overridden property in CheckboxElement called Selected that changes the value of BooleanValue when the element is tapped.
public override void Selected(DialogViewController dvc, UITableView tableView, NSIndexPath indexPath)
{
BooleanValue = !BooleanValue;
...
}
Selected is called by the DialogViewController's RowSelected method.
public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
Container.Selected(indexPath);
}
Which calls the DialogViewController's Selected method (element is the CheckboxElement).
public virtual void Selected(NSIndexPath indexPath)
{
var section = Root.Sections[indexPath.Section];
var element = section.Elements[indexPath.Row];
ActiveElement = element;
element.Selected(this, Root.TableView, indexPath);
}
Doesn't look like there's anything there that's interfering.
Let me know if you need more info. Thanks in advance.
Mvvmcross relies on event notifications to tell it when values have changed.
When event notifications aren't available, then you can write custom bindings to help MvvmCross know when the ui has updated. For more on this, see n=28 - custom,bindings in http://mvvmcross.blogspot.com
For the specific case of Monotouch.Dialog, Mvvmcross provides its own branch which includes two-way bindings for boolean elements such as UiSwitch-based Elements. You may find it easier to use this mvvmcross branch - for more on this, look for dialog in the n+1 videos.
I'm trying to create a dynamic object that can be used as a component of a static object. Here is a contrived example of what I'm trying to accomplish.
Here is the dynamic component:
public class DynamicComponent : DynamicObject
{
public override bool TryInvokeMember(
InvokeMemberBinder binder,
object[] args,
out object result)
{
result = "hello";
return true;
}
}
And here is a class where inheriting from DynamicObject isn't an option...assume that there is some third party class that I'm forced to inherit from.
public class AStaticComponent : VendorLibraryClass, IDynamicMetaObjectProvider
{
IDynamicMetaObjectProvider component = new DynamicComponent();
public DynamicMetaObject GetMetaObject(Expression parameter)
{
var result = component.GetMetaObject(parameter);
return result;
}
}
The direct usage of DynamicComponent works:
dynamic dynamicComponent = new DynamicComponent();
Assert.AreEqual(dynamicComponent.AMethod(), "hello");
However, forwarding the GetMetaObject through AStaticComponent causes some form of an infinite loop.
dynamic dynamicComponent = new AStaticComponent();
Assert.AreEqual(dynamicComponent.AMethod(), "hello"); //causes an infinite loop
Anyone know why this occurs?
And if it's some baked in behavior of DynamicObject that I cannot change, could someone provide some help on how to create a IDynamicMetaObjectProvider from scratch to accomplish a component based dynamic object (just something to get things started)?
I think the problem is that the Expression parameter passed to GetMetaObject represents the target of the dynamic invocation (i.e. the current object). You are passing the outer object to the call on component.GetMetaObject, so the returned meta object is trying to resolve the call to AMethod on the outer object instead of itself, hence the infinite loop.
You can create your own meta object which delegates to the inner component when binding member invocations:
public class AStaticComponent : VendorLibraryClass, IDynamicMetaObjectProvider
{
IDynamicMetaObjectProvider component = new DynamicComponent();
public DynamicMetaObject GetMetaObject(Expression parameter)
{
return new DelegatingMetaObject(component, parameter, BindingRestrictions.GetTypeRestriction(parameter, this.GetType()), this);
}
private class DelegatingMetaObject : DynamicMetaObject
{
private readonly IDynamicMetaObjectProvider innerProvider;
public DelegatingMetaObject(IDynamicMetaObjectProvider innerProvider, Expression expr, BindingRestrictions restrictions)
: base(expr, restrictions)
{
this.innerProvider = innerProvider;
}
public DelegatingMetaObject(IDynamicMetaObjectProvider innerProvider, Expression expr, BindingRestrictions restrictions, object value)
: base(expr, restrictions, value)
{
this.innerProvider = innerProvider;
}
public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
{
var innerMetaObject = innerProvider.GetMetaObject(Expression.Constant(innerProvider));
return innerMetaObject.BindInvokeMember(binder, args);
}
}
}
#Lee's answer is really useful, I wouldn't have known where to get started without it. But from using it in production code, I believe it has a subtle bug.
Dynamic calls are cached at the call site, and Lee's code produces a DynamicMetaObject which effectively states that the inner handling object is a constant. If you have a place in your code where you call a dynamic method on an instance of AStaticObject, and later the same point in the code calls the same method on a different instance of AStaticObject (i.e. because the variable of type AStaticObject now has a different value) then the code will make the wrong call, always calling methods on the handling object from the first instance encountered at that place in the code during that run of the code.
This is a like-for-like replacement, the key difference being the use of Expression.Field to tell the dynamic call caching system that the handling object depends on the parent object:
public class AStaticComponent : VendorLibraryClass, IDynamicMetaObjectProvider
{
IDynamicMetaObjectProvider component = new DynamicComponent();
public DynamicMetaObject GetMetaObject(Expression parameter)
{
return new DelegatingMetaObject(parameter, this, nameof(component));
}
private class DelegatingMetaObject : DynamicMetaObject
{
private readonly DynamicMetaObject innerMetaObject;
public DelegatingMetaObject(Expression expression, object outerObject, string innerFieldName)
: base(expression, BindingRestrictions.Empty, outerObject)
{
FieldInfo innerField = outerObject.GetType().GetField(innerFieldName, BindingFlags.Instance | BindingFlags.NonPublic);
var innerObject = innerField.GetValue(outerObject);
var innerDynamicProvider = innerObject as IDynamicMetaObjectProvider;
innerMetaObject = innerDynamicProvider.GetMetaObject(Expression.Field(Expression.Convert(Expression, LimitType), innerField));
}
public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
{
return binder.FallbackInvokeMember(this, args, innerMetaObject.BindInvokeMember(binder, args));
}
}
}
I need to be able to call a method and pass in an object of an unknown type
but then have the correct overload called. I also need a default implementation that accepts
object as its parameter type. What I'm seeing is that the default overload is the only one that ever gets used.
Here's the gist of what I'm trying to do:
class Formatter
{
private object Value;
public Formatter(object val){
Value = val;
}
public override string ToString()
{
return Format(Value);
}
private string Format(object value)
{
return value.ToString();
}
private string Format(DateTime value)
{
return value.ToString("yyyyMMdd");
}
}
Ok, so far so good. Now I want to be able to do this:
public static class FancyStringBuilder()
{
public static string BuildTheString()
{
var stringFormatter = new Formatter("hello world");
var dateFormatter = new Formatter(DateTime.Now);
return String.Format("{0} {1}", stringFormatter, dateFormatter);
}
}
The result of FancyStringBuilder.BuildTheString() is "hello world 2012-12-21 00:00:00.000", when I expected "hello world 20121221"
The problem is that the overload that accepts a DateTime is not being called, instead defaulting to the overload which accepts an object. How can I call the proper method without resorting to a messy switch statement?
In Formatter.ToString(), the override Formatter.Format(object) is always called. This is because the overload resolution happens at compile-time, not run-time. At compile-time, the only thing known about Value is that it's an object.
If you really want to distinguish incoming types, you'll need to do so in Formatter's constructor. In this case, rather than hanging on to the object, you could just call ToString() immediately and only store the formatted result:
class Formatter
{
string formattedValue;
public Formatter(object value)
{
formattedValue = value.ToString();
}
public Formatter(DateTime value)
{
formattedValue = value.ToString("yyyyMMdd");
}
public string ToString()
{
return formattedValue;
}
}
Note that this does assume that your object isn't changing between the time you create the Formatter object and the time Formatter.ToString() is called, or at the very least that it's okay to take a snapshot of the string representation at the time the Formatter is created.
This also assumes that you know the incoming types at compile-time. If you want a truly run-time-only solution, you'll have to use the "is" operator or a typeof() comparison.
If your goal is just to provide custom ToString() formatting based on the incoming type, I'd probably do it using a list that maps from types to format strings:
static class Formatter
{
private static List<Tuple<Type, string>> Formats;
static Formatter()
{
Formats = new List<Tuple<Type, string>>();
// Add formats from most-specific to least-specific type.
// The format string from the first type found that matches
// the incoming object (see Format()) will be used.
AddMapping(typeof(DateTime), "yyyyMMdd");
// AddMapping(typeof(...), "...");
}
private static void AddMapping(Type type, string format)
{
Formats.Add(new Tuple<Type, string>(type, format));
}
public static string Format(object value)
{
foreach (var t in Formats)
{
// If we find a type that 'value' can be assigned to
// (either the same type, a base type, or an interface),
// consider it a match, and use the format string.
if (t.Item1.IsAssignableFrom(value.GetType()))
{
return string.Format(t.Item2, value);
}
}
// If we didn't find anything, use the default ToString()...
return value.ToString();
}
}
With that, calling code then looks like:
Console.WriteLine(
"{0} {1}",
Formatter.Format(DateTime.Now),
Formatter.Format("banana"));
I think this is because the class constructor takes an object as parameter, and then assign that object to variable Value which is also an object. There for calling Format(object) since Value is of type object
Try this
public override string ToString()
{
if(Value is DateTime)
return Format(Convert.ToDateTime(Value)); //this should call the right method
return Format(Value); //works for other non-custom-format types e.g. String
}
Recently I'm developing a tiny framework for myself,
and I met this problem:
How can I do things like follow:
void object CreateDictionary(Type dictionaryType)
{
object dict = dictionaryType.GetConstructor(new Type[] {}).Invoke(new object[] {});
// Cast dict to its real type here so that I can add key-value-pairs to it.
...
}
The dictionaryType is the type of some kind of Dictionary, and is got via reflection.
I have no idea about the full type because I don't know the generic attributes until runtime.
I've also tried changing the declaration object dict to var dict, but it does not work either.
You cannot do this. But, you know that this is some kind of Dictionary, so you can cast it to IDictionary and use methods of IDictionary.
object CreateDictionary(Type dictionaryType)
{
object dict = dictionaryType.GetConstructor(new Type[] {}).Invoke(new object[] {});
var idictionary = (IDictionary)dict;
idictionary.Add(key, value);
}
If your all this dictionaries is inherited from one class, you can cast it to this class and use methods of this class.
By the way, it is simpler to get instance of Type through:
object obj = Activator.CreateInstance(type);
OK, I managed to solve this problem at last.
Finally I noticed that what I want to do is not about casting,
but calling method.
Maybe there are better solutions than mine.
Anyway, I'd like to share my solution.
First, create an extension class for object (this is weird though):
public static class ReflectionHelper
{
public static object InvokeInstanceMethod(this object invoker, string methodName, params object[] parameters)
{
MethodInfo[] methods = invoker.GetType().GetMethods();
foreach (MethodInfo method in methods)
{
ParameterInfo[] paramInfos = method.GetParameters();
if (method.Name == methodName && paramInfos.Length == parameters.Length)
{
for (int i = 0; i < parameters.Length; i++)
{
if (!paramInfos[i].ParameterType.IsAssignableFrom(parameters[i].GetType()))
{
throw new MissingMethodException();
}
}
return method.Invoke(invoker, parameters);
}
}
throw new MissingMethodException();
}
}
This extension method allows me to call methods like this:
anyInstance.InvokeInstanceMethod("MethodName", param1, param2, ...);
Because all types, excluding Object itself, are derived from Object, this method can be cal on any instance of any type.
Then I use this method:
object dict = dictionaryType.CreateInstance(); // The method CreateInstance() is also an extension
dict.InvokeInstanceMethod("Add", key, val);
we read in msdn we "Adding new dynamic properties" by using DynamicObject Class
i write a following program
public class DemoDynamicObject : DynamicObject
{
}
class Program
{
public static void Main()
{
dynamic dd = new DemoDynamicObject();
dd.FirstName = "abc";
}
}
But when i run this program it gives runtime error :'DemoDynamicObject' does not contain a definition for 'FirstName'
if we adding dynamic property by using DynamicObject Class then why it can give this error
can anyone tell me reason and solution?
When using DynamicObject as your base class, you should provide specific overrides to TryGetMember and TrySetMember to keep track of the dynamic properties you are creating (based on the DynamicObject MSDN documentation):
class DemoDynamicObject: DynamicObject
{
Dictionary<string, object> dictionary
= new Dictionary<string, object>();
public override bool TryGetMember(
GetMemberBinder binder, out object result)
{
string name = binder.Name;
return dictionary.TryGetValue(name, out result);
}
public override bool TrySetMember(
SetMemberBinder binder, object value)
{
dictionary[binder.Name] = value;
return true;
}
}
If you just want to have a dynamic object that you can add properties to, you can simply use an ExpandoObject instance, and skip the custom class inheriting from DynamicObject.