Using a value converter in WPF, you can return something like DependecyProperty.UnsetValue or Binding.DoNothing as special values to say leave the binding alone. Is there a similar mechanism in MVVMCross?
To be more specific about what I'm trying to do, is I have a view model property that is a three-state enum that I need to bind to 3 binary controls. So I thought I could bind each of the controls to a MyEnum -> bool converter that will have a conversion parameter set to the value of the converter and in the Convert method it will return true if the MyEnum state is equal to the parameter and false otherwise. So far so good. But I want this binding to be two-way, so I need to convert back. My convert back works something like this:
protected override MyEnum ConvertBack(bool value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (parameter is MyEnum)
{
if (value)
{
return (MyEnum)parameter; // this is fine
}
else
{
return ???
}
}
return base.ConvertBack(value, targetType, parameter, culture);
}
Basically, what I want to be able to do is say, if the state of my control is true update the bound property on my view model to be the same as the parameter, if not, leave the view model property alone.
Maybe this is the problem with using the strongly typed value converters?
If anybody else encounters a similar problem, in addition to Stuart's suggestions, this is what I actually ended up doing.
In the View Model
private MyEnum _selectedValue = MyEnum.SomeValue;
public MyEnum ? SelectedValue
{
get
{
return _selectedTab;
}
set
{
if (value.HasValue)
{
_selectedTab = value.Value;
RaisePropertyChanged(() => SelectedTab);
}
}
}
Then in my converter (now a MvxValueConverter<MyEnum?, bool>):
protected override MyEnum? ConvertBack(bool value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (parameter is MyEnum) // this is just a sanity check
{
if (value)
{
return (MyEnum)parameter;
}
else
{
return null
}
}
return base.ConvertBack(value, targetType, parameter, culture);
}
It's not a general solution, but it fits my immediate need.
Update: UnsetValue is now available in mvvmcross - so this answer is no longer required.
Binding.DoNothing, DependencyProperty.UnsetValue or equivalent are currently not implemented in the MvvmCross binding system, and I don't believe there are any feature or pull requests currently open on this.
I'm afraid I don't really understand your example - the 'conversion parameter set to the value of the converter' and the if (parameter is MyEnum) snippet rather threw me - as far as I know, the parameter in windows xaml binding is a constant so I don't quite understand this example. Sorry if I've misunderstood.
Regardless, some possible approaches that might be used to assist with getting this functionality up and running in your mvx app are:
You could throw an exception from the ValueConverter:
For the Convert direction, the binding FallbackValue is used if a ValueConverter fails (throws an exception)
For the ConvertBack direction, the binding does nothing if an exception is thrown (although this would unfortunately generate some trace "error" output each time)
You could implement a custom binding for your control - something which only fires change updates when the change is one you are interested in (some custom bindings are introduced in N=28 in http://mvvmcross.wordpress.com which creates bindings like BinaryEditFooTargetBinding.cs - it would be straight-forward to customise/filter the TargetOnMyCountChanged behaviour in that class)
You could implement a custom binary control - which only fires change updates when the change is one you are interested in (for various platforms, some custom controls are introduced in N=18 to N=20 in http://mvvmcross.wordpress.com)
You could implement ViewModel binary properties which mirror your View and map to your three state enum
You could use a ValueCombiner within the Tibet binding system - it should be relatively straight-forward to work out a combiner based on top of something like MvxSingleValueCombiner.cs
Alternatively/additionally, please do also consider raising a Feature and/or Pull Request for this 'do nothing' type of functionality to be included in the TryGetValue/SetValue methods in https://github.com/slodge/MvvmCross/blob/v3/Cirrious/Cirrious.MvvmCross.Binding/Bindings/SourceSteps/MvxSourceStep.cs#L66 - but obviously the timeline on that might be longer.
Related
In my particular application, I would like the background to be Transparent, i.e., Transparent = true, when the IsSelected is false. I suspect I have to write a Converter, but perhaps there is an easier way?
set.Bind(selectedBox).For(v => v.Transparent).To(vm => vm.IsSelected).OneWay();
Looks like there is a WithConversion<...> possibility. Perhaps I need a NotValueConverter? Hard to believe this is not already implemented somewhere....
Update. I added the following to my Core
public class NotConverter : MvxValueConverter<bool, bool>
{
protected override bool Convert(bool value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return !value;
}
protected override bool ConvertBack(bool value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return !value;
}
}
but it does not seem to be picked up by the sweep and
set.Bind(selectedBox).For(v => v.Transparent).To(vm => vm.IsSelected).WithConversion("Not").OneWay();
does not make any difference.
Converters need to end with ValueConverter to be picked up automatically by MvvmCross. You can also use a generic type to use WithConversion.
set.Bind(selectedBox).For(v => v.Transparent).To(vm => vm.IsSelected).WithConversion<NotConverter>().OneWay();
I thought that "Not" was a built-in converter, and i've used it on Android, but i can't find anything on this so far.
I was curious about this, so I started looking through the MvvmCross code. On Android, you can do something like:
app:MvxBind="Enabled !SomeBoolean"
which works, but it's actually the MvxInvertedValueCombiner that's doing this, not any ValueConverter. So in fluent form this should do the trick:
set.Bind(selectedBox).For(v => v.Transparent).ByCombining(new MvxInvertedValueCombiner(), vm => vm.IsSelected)
I've actually never used it in that way, but it seems to work. Furthermore it appears that the value combiner is applied before any value converter, so let's say you were tired of that MvxInvertedVisibilityValueConverter, you could do something like this on Android:
app:MvxBind="Visibility Visibility(!HideMe)"
I'm writing my own language from ActionScript as a personal project (yeah, I guess AS3 is not the best language to build a language from, but never mind that).
NOTE: I have checked several times, and my compiler's option 'Enable Strict Mode' is set to True. I have tried setting it to False to try, but I didnt get a different result.
At any rate, I have a this:
package NodyCode.Classes
{
public class NCString
{
var value:String;
public function NCString(expression:String = "") {
value = expression;
}
public function rindex(substr:NCString, startIndex:int = 0x7fffffff):uint {
//code here
}
}
}
Since I'm writing my own language, I need to make sure functions and methods can take un unlimited number of arguments. For this reason, I'm using an anonymous function so that I can use the apply method. Like so:
//This code is in a class named ClassMethods
public static var StringMethods:Object = {
rindex: function(substr:NCString, startIndex:int = 0x7fffffff):uint {
return this.rindex(substr, startIndex);
}
}
And, somewhere else in my code, I do the call:
return ClassMethods.StringMethods["rindex"].apply(ncstr1, [ncstr2, [5]]);
I would like an error to be thrown whenever the user uses the wrong type of argument.
So, in this case, I call the rindex method on ncstr1, with arguments: substr = ncstr2 and startIndex = [5]. Notice that, according to my anonymous function's definition, startIndex is supposed to be an int, not an Array.
So, I expected an error to be thrown. Instead, though, rindex is called with startIndex = 5.
Why is [5] converted to 5, and is there any way for me to prevent that? If there isn't, I can always work around this problem, but I'd rather not if I can do otherwise.
EDIT: Finally understood that I did not mention I was using an anonymous function.
Are you compiling with strict mode set to false? (See here also.)
The strict option: "Prints undefined property and function calls; also performs compile-time type checking on assignments and options supplied to method calls".
It defaults to true, but if it got set to false somehow, compile-time checks might be disabled. I'd check your compiler settings (whether in an IDE or if you're compiling on the command-line) and make sure they're correct.
Okay, so here's what was said in the comments:
I did have my compiler on strict mode. The reason for which I was not getting an error is because I was using the apply method of an anonymous function. The type checks are loosened when using the apply method. That's why [5] was coerced to 5.
There is apparently no way to prevent this.
I started using observable ConcurrentDictionary because it's threadsafe. But now I face a problem how to bind it to the list.
local:MvxBind="ItemsSource SourceDictionary"
obviously cannot work because item consists of the KeyValuePair and not the object itself.
local:MvxBind="ItemsSource SourceDictionary.Values"
does not work. Which, I must admit, puzzles me.
I even tried a long shot and did some converter :
public class SourceDictionaryValueConverter : MvxValueConverter<KeyValuePair<string, SourceClass >, SourceClass >
{
protected override SourceClass Convert(KeyValuePair<string, SourceClass> value, Type targetType, object parameter, CultureInfo culture)
{
return value.Value;
}
}
and bind it like this
local:MvxBind="ItemsSource SourceDictionary, Converter=SourceDictionary"
but it didn't work. I suppose it asks for IList.
Is there any way to bind ListView to the Dictionary?
You should be able to bind to a Dictionary - or indeed to any IEnumerable.
If you are binding to the Dictionary itself (rather than to Values) then you will need to write your item bindings so that they know about KeyValuePair - e.g. so you might need to bind to Key.Name rather than to just Name.
However, Dictionary's themselves do not notify listeners of changes - so using a Dictionary is like using a List - not like using an ObservableCollection. If you want this type of behaviour then you will need to write or find a Dictionary class which implements INotifyCollectionChanged.
I'm designing a framework and in the process I have come across an interesting but most likely basic problem. I have a base class called CoreEngine and two other classes that extend it: CoreEngine1 and CoreEngine2. I created an interface that each of these classes would implement to increase the flexibility of my project. However, I have a problem... The definition of my methods in the interface do not match the definition in each inherited class! Each class must implement the following method:
function get avatar():AvatarBase;
The problem is that CoreEngine1 and CoreEngine2 expect a different type of avatar:
CoreEngine1
function get avatar():AvatarScaling
CoreEngine2
function get avatar():AvatarPlatform
As you can see, the return type for avatar in CoreEngine1 and CoreEngine2 do NOT match the type as specified in the interface. I was hoping that since both AvatarScaling and AvatarPlatform inherit AvatarBase that I wouldn't have a problem compiling. However, this is not the case. According to Adobe's documentation, the types MUST match the interface. I am trying to follow one of the core concepts of object oriented programming to extend the flexibility of my framework: "Program to an interface rather than an implementation". The first thing that comes to my mind is that the return type of the accessor method should be of an interface type (Maybe I just answered my own question).
I'm certain this is a common problem others have run into before. Architecturally, what do you think is the best way to solve this problem? Thanks in advance!
Regards,
Will
This is a limitation of how interfaces work and are declared.
If there's inheritance that can happen with the return types, as you've described with AvatarBase and subclasses, then I think the right approach is to make the return type the lowest common denominator and just handle the resulting object on the other end. So, if you're dealing with a CoreEngine1 object, you know you can cast the result from AvatarBase to AvatarScaling. Alternately, if you don't know the object type that you are calling get avatar() on, then you can type check the returned value. The type check would then only be needed if you're looking to call a method that exists on AvatarScaling but not on AvatarBase. I don't think returning an interface type will buy you much in this case because the only things that interface can implement would be things that all forms of Avatar share, which wouldn't be any different than methods in AvatarBase.
Like HotN and Dinko mentioned, it would be best to allow get avatar() to return AvatarBase allways and then cast the returned object as the concrete subclass.
Using Dinko's example:
public /* abstract */ class CoreEngine
{
public /* abstract */ function get avatar():AvatarBase {}
}
public function CoreEngine1 extends CoreEngine
{
override public function get avatar():AvatarBase { return new AvatarScaling(); }
}
public function CoreEngine2 extends CoreEngine
{
override public function get avatar():AvatarBase { return new AvatarPlatform(); }
}
public /* abstract */ class AvatarBase {}
public class AvatarScaling extends AvatarBase
{
public function someAvatarScalingMethod():void {}
}
public class AvatarPlatform extends AvatarBase
{
public function someAvatarPlatformMethod():void {}
}
To use a method from AvatarScaling, cast the returned object:
var c1:CoreEngine1 = new CoreEngine1();
var avatarScaling:AvatarScaling = AvatarScaling(c1.avatar());
avatarScaling.someAvatarScalingMethod();
hth
I think you answered your own question... the return type would still be AvatarBase, you need to follow the signature that you specified in the interface... but you can technically return ANY descendent of AvatarBase in that function. So doing something like
return new AvatarScaling();
in CoreEngine1 would be perfectly acceptable.
Of course in your calling function you will get back an AvatarBase instance, and you will have to know what this is in order to cast to a specific subclass.
CoreEngine1 ce1 = new CoreEngine1();
AvatarScaling avatar = ce1.avatar() as AvatarScaling;
Consider the following test:
[Test]
public void Create_ServiceWithDynamicDependency_Created()
{
// arrange
IWindsorContainer container = new WindsorContainer();
container.AddFacility<TypedFactoryFacility>();
container.Register(Component.For<IServiceFactory>().AsFactory());
container.Register(Component.For<ServiceWithDynamicDependency>().LifeStyle.Transient);
container.Register(Component.For<SomethingStatic>().LifeStyle.Transient);
var factory = container.Resolve<IServiceFactory>();
// act
ServiceWithDynamicDependency serviceWithDynamicDependency = factory.Create(null);
// assert
Assert.That(serviceWithDynamicDependency, Is.Not.Null);
}
This fails with the following exception: Could not resolve non-optional dependency for 'Testing.Windsor.Factory.ServiceWithDynamicDependency' (Testing.Windsor.Factory.ServiceWithDynamicDependency). Parameter 'somethingDynamic' type 'System.String'
If I replace the assert part with this:
ServiceWithDynamicDependency serviceWithDynamicDependency = factory.Create("foo");
The component is resolved as expected. Does anyone know of a workaround for this or do I have to hand-roll factories which accepts null arguments?
Although you didn't specify it I'm assuming your component has one constructor that looks like this:
public ServiceWithDynamicDependency(string somethingDynamic) {}
The behavior you're observing is not a bug - this is by design, and here's why.
If a value is required an actual value has to be provided, and null is not considered to be a correct value. Otherwise Windsor would just be passing null in for every constructor instead of bothering to find actual dependencies for that.
null is a special value in .NET and it means "no value". So even though you explicitly provide null as an argument Windsor will ignore it.
To solve this, instead of working around be explicit that the value is optional and provide a second constructor that does not include it.
Windsor is smart enough to pick the right constructor and if the value is not provided (or null as in your case) it will pick the no-argument constructor, and when the value is there (is not null) it will pick the one with the value.