ReactiveCommand.Create throws "NotSupportedException": "Index expressions are only supported with constants." - win-universal-app

The following line throws a runtime exception:
Accept = ReactiveCommand.Create(this.WhenAnyValue(x => x.Canexecute()));
Here's the code:
public class InstructionsViewModel : ReactiveObject
{
public InstructionsViewModel()
{
Accept = ReactiveCommand.Create(this.WhenAnyValue(x => x.CanExecute));
Accept.Subscribe(x =>
{
Debug.Write("Hello World");
});
}
public ReactiveCommand<object> Accept { get; }
bool _canExecute;
public bool CanExecute { get { return _canExecute; } set { this.RaiseAndSetIfChanged(ref _canExecute, value); } }
}
Error:
Cannot convert lambda expression to type 'IObserver' because
it is not a delegate type
I've also tried the following:
public InstructionsViewModel()
{
Accept = ReactiveCommand.Create(this.WhenAnyValue(x => x.Canexecute()));
Accept.Subscribe(x =>
{
Debug.Write("Hello World");
});
}
public ReactiveCommand<object> Accept { get; }
public bool Canexecute() => true;
I receive the following error:
An exception of type 'System.NotSupportedException' occurred in
ReactiveUI.dll but was not handled in user code
Additional information: Index expressions are only supported with
constants.
Is this even supported on Windows Phone 10?

I guess that your problem is not with ReactiveCommand, but with WhenAnyValue.
WhenAnyValue accepts a property, while you feed it with a method, which causes run time exception (see the sourcecode).
Check if this works (I changed CanExecute to be a property instead of a method):
public InstructionsViewModel()
{
Accept = ReactiveCommand.Create(this.WhenAnyValue(x => x.CanExecute));
Accept.Subscribe(x =>
{
Debug.Write("Hello World");
});
}
public ReactiveCommand<object> Accept { get; }
private bool _canExecute;
public bool CanExecute { get { return _canExecute; } set { this.RaiseAndSetIfChanged(ref _canExecute, value); } }
Also, as a general advice - do not nest your calls, this makes debugging harder. You should split creating command into two lines:
var canExecute = this.WhenAnyValue(x => x.CanExecute)
Accept = ReactiveCommand.Create(canExecute);

Related

CommandParameter binding in DialogViewController

Consider this simple ViewModel:
public class AboutViewModel
: MvxViewModel
{
private readonly IMvxWebBrowserTask _webBrowserTask;
public AboutViewModel(IMvxWebBrowserTask webBrowserTask) { _webBrowserTask = webBrowserTask; }
private MvxCommand<string> _showInStoreCommand;
public ICommand ShowInStoreCommand
{
get
{
_showInStoreCommand = _showInStoreCommand ?? new MvxCommand<string>(DoShowInStoreCommand);
return _showInStoreCommand;
}
}
private void DoShowInStoreCommand(string url)
{
_webBrowserTask.ShowWebPage(url);
}
public string Wp8StoreUrl
{
get { return "http://windowsphone.com/s?appId=myappid"; }
}
public string AndroidStoreUrl
{
get { return "https://play.google.com/store/apps/details?id=mypackagename"; }
}
public string TouchStoreUrl
{
get { return "http://itunes.com"; }
}
}
Which I want to bind in a MvxDialogViewController and I write something like this:
Root = new RootElement
{
new Section()
{
new StringElement("Show in App Store").Bind(bindings, e => e.SelectedCommand, vm => vm.ShowInStoreCommand, new MvxCommandParameterValueConverter(), ViewModel.TouchStoreUrl),
}
}
Why does this always give me null in the DoShowInStoreCommand's url argument? How do I use Command Parameters when binding Elements?
EDIT:
I tried the following, as what Slodge wrote in his answer does not match any of the Extension method signatures, but it is giving me errors when it tries to bind:
new StringElement("Show in App Store")
.Bind(bindings, "SelectedCommand CommandParameter(ShowInStoreCommand, TouchStoreUrl)")
error:
MvxBind: Error: 2.37 Problem parsing Lang binding MvxException: Must follow binding option CommandParameter with an '=' in SelectedCommand CommandParameter(ShowInStoreCommand, TouchStoreUrl)
at Cirrious.MvvmCross.Binding.Parse.Binding.MvxBindingParser.ParseEquals (System.String block) [0x0003c] in c:\Projects\Misc\MVVMCROSS\Cirrious\Cirrious.MvvmCross.Binding\Parse\Binding\MvxBindingParser.cs:83
at Cirrious.MvvmCross.Binding.Parse.Binding.Swiss.MvxSwissBindingParser.ParseNextBindingDescriptionOptionInto (Cirrious.MvvmCross.Binding.Parse.Binding.MvxSerializableBindingDescription description) [0x0019a] in c:\Projects\Misc\MVVMCROSS\Cirrious\Cirrious.MvvmCross.Binding\Parse\Binding\Swiss\MvxSwissBindingParser.cs:56
at Cirrious.MvvmCross.Binding.Parse.Binding.Tibet.MvxTibetBindingParser.ParseNextBindingDescriptionOptionInto (Cirriou
s.MvvmCross.Binding.Parse.Binding.MvxSerializableBindingDescription description) [0x00033] in c:\Projects\Misc\MVVMCROSS\Cirrious\Cirrious.MvvmCross.Binding\Parse\Binding\Tibet\MvxTibetBindingParser.cs:49
at Cirrious.MvvmCross.Binding.Parse.Binding.Swiss.MvxSwissBindingParser.ParseBindingDescription (ParentIsLookingForComma parentIsLookingForComma) [0x00014] in c:\Projects\Misc\MVVMCROSS\Cirrious\Cirrious.MvvmCross.Binding\Parse\Binding\Swiss\MvxSwissBindingParser.cs:176
at Cirrious.MvvmCross.Binding.Parse.Binding.Swiss.MvxSwissBindingParser.ParseBindingDescription () [0x00001] in c:\Projects\Misc\MVVMCROSS\Cirrious\Cirrious.MvvmCross.Binding\Parse\Binding\Swiss\MvxSwissBindingParser.cs:159
at Cirrious.MvvmCross.Binding.Parse.Binding.MvxBindingParser.ParseTargetPropertyNameAndDescription () [0x0000f] in c:\Projects\Misc\MVVMCROSS\Cirrious\Cirrious.MvvmCross.Binding\Parse\Binding\MvxBindingParser.cs:72
at Cirrious.MvvmCross.Binding.Parse.Binding.MvxBindingParser.TryParseBindingSpecification (System
.String text, Cirrious.MvvmCross.Binding.Parse.Binding.MvxSerializableBindingSpecification& requestedBindings) [0x0001a] in c:\Projects\Misc\MVVMCROSS\Cirrious\Cirrious.MvvmCross.Binding\Parse\Binding\MvxBindingParser.cs:51
The binding you've created in:
new StringElement("Show in App Store").Bind(
bindings,
e => e.SelectedCommand,
vm => vm.ShowInStoreCommand,
new MvxCommandParameterValueConverter(), ViewModel.TouchStoreUrl),
is a binding to the expression vm => vm.ShowInStoreCommand on the ViewModel, but it captures the current value of ViewModel.TouchStoreUrl
If you want to use MultiBinding in MvvmCross, then you can do this using the Tibet extensions (see https://github.com/MvvmCross/MvvmCross/wiki/Databinding) - but these are not easily accessible in the fluent Expression syntax - instead you have to use the string based syntax instead.
So this binding could be achieved using something like:
new StringElement("Show in App Store").Bind(
bindings,
e => e.SelectedCommand,
"CommandParameter(ShowInStoreCommand, TouchStoreUrl)")

How do you Bind an Image to the HighlightedImage-Property of an ImageView?

I'm currently trying to bind two images to an iOS ImageView via MvvmCross.
One should be displayed when the ImageView is in 'default' state, the other one when the ImageView is highlighted.
By the following code I can bind the Image for the default state. But how do I bind the one for 'highlighted' state?
public CategoryCell(IntPtr handle): base(string.Empty, handle)
{
_imageViewLoader = new MvxImageViewLoader(() => this.imageView);
this.DelayBind(() =>
{
var set = this.CreateBindingSet<CategoryCell, MaterialCategory>();
set.Bind(titleLabel).To(materialCategory => materialCategory.Label);
set.Bind(_imageViewLoader).To(materialCategory => materialCategory.ImageActiveUri);
set.Bind(this).For(cell => cell.Selected).To(materialCategory => materialCategory.IsSelected);
set.Apply();
});
}
Another approach if you do not need image loading i.e. for lots of static UX.
You can set up as follows -
_imageView = new UIImageView(UIImage.FromFile("some/image/off.png"))
{
HighlightedImage = UIImage.FromFile("some/image/on.png")
};
And bind it e.g. an "Enabled" Property -
this.DelayBind(() =>
{
var set = this.CreateBindingSet<SomeView, SomeViewModel>();
set.Bind(_imageView).For(v => v.Highlighted).To(vm => vm.Enabled);
set.Apply();
});
And don't forget to add Highlighted to your LinkerPleaseInclude.cs.
Hope this helps
I think the best solution is to introduce an extra property ImageUri. In the setter of your IsSelected you set the ImageUri dependend on the selection state.
ViewModel:
public class MaterialCategory : MvxViewModel
{
//...
public string ImageActiveUri { ... } // call UpdateImageUri() here, too
public string ImageInactiveUri { ... } // call UpdateImageUri() here, too
public string ImageUri { ... }
public bool IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
UpdateImageUri();
RaisePropertyChanged(() => IsSelected);
}
}
private void UpdateImageUri()
{
ImageUri = IsSelected ? ImageActiveUri : ImageInactiveUri;
}
}
Binding:
set.Bind(_imageViewLoader).To(materialCategory => materialCategory.ImageUri);
// instead of:
// set.Bind(_imageViewLoader).To(materialCategory => materialCategory.ImageActiveUri);

Why use Automappers ValueResolver?

Why do this
Mapper.CreateMap<MyObject, AnotherObject>().
ForMember(x => x.DateAsString, m => m.ResolveUsing<StringToDateTimeFormatter>());
private class StringToDateTimeFormatter : ValueResolver<DateTime, string>
{
protected override string ResolveCore(DateTimesource)
{
return source.ToString("yyyy-MM-dd");
}
}
when you can do this
Mapper.CreateMap<MyObject, AnotherObject>().
ForMember(x => x.DateAsString, m => m.MapFrom(x => x.Date.ToString("yyy-MM-dd")));
???
Update
Here's an example on how to do more complex business logic
Mapper.CreateMap<MyObject, AnotherObject>().
ForMember(x => x.DateAsString, m => m.MapFrom(n => MyMethod(n.DateAsString)));
private object MyMethod(string dateTime)
{
if(!MyDomainObjectIsValid(dateTime))
{
throw new MyValidationException();
}
// do more stuff
}
I still don't see the need for a ValueResolver...
Obviously for your example it is more reasonable to use just MapFrom.
ValueResolvers are needed for more complicated cases. For example when you need to do some validation and throw exception accordingly.
EDIT
ValueResolvers provide access to the destination type and value. Here is small example.
public class FakeResolver : IValueResolver
{
public ResolutionResult Resolve(ResolutionResult source)
{
if (source.Context.DestinationType == typeof(string) && source.Context.DestinationValue == "test")
throw new Exception();
return source;
}
}

How can I get this old Ninject 2 code to work with Ninject 3 and the MVC 2 extension (NinjectControllerFactory)?

In my MVC 2 project, I originally used Ninject 2 and wrote this version of the NinjectControllerFactory:
public class NinjectControllerFactory : DefaultControllerFactory
{
private IKernel kernel = new StandardKernel(new HandiGamerServices());
protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
{
try
{
if (controllerType == null)
{
return base.GetControllerInstance(requestContext, controllerType);
// return null;
}
}
catch (HttpException ex)
{
if (ex.GetHttpCode() == 404)
{
IController errorController = kernel.Get<ErrorController>();
((ErrorController)errorController).InvokeHttp404(requestContext.HttpContext);
return errorController;
}
else
{
throw ex;
}
}
return (IController)kernel.Get(controllerType);
}
Of most importance is the retrieval of my ErrorController, which allows me to gracefully handle a multitude of HTTP errors.
The problem is that I upgraded to the MVC 2 extension via Nuget, so a NinjectControllerFactory is already provided. Would it be possible to use my own override of GetControllerInstance? If so, how?
I do exactly this, and for precisely the same reason. In Global.asax.cs, I add this to my OnApplicationStarted override (declared virtual in NinjectHttpApplication):
ControllerBuilder.Current.SetControllerFactory(
new MyControllerFactory(ControllerBuilder.Current.GetControllerFactory()));
This means you're creating your own controller factory, but providing it with the default implementation to do the heavy lifting.
Then define your controller factory like so:
public class MyControllerFactory : IControllerFactory
{
private IControllerFactory defaultFactory;
public MyControllerFactory(IControllerFactory defaultFactory)
{
this.defaultFactory = defaultFactory;
}
public IController CreateController(RequestContext requestContext, string controllerName)
{
try
{
var controller = defaultFactory.CreateController(requestContext, controllerName);
return controller;
}
catch (HttpException e)
{
// Pasted in your exception handling code here:
if (ex.GetHttpCode() == 404)
{
IController errorController = kernel.Get<ErrorController>();
((ErrorController)errorController).InvokeHttp404(requestContext.HttpContext);
return errorController;
}
else
{
throw ex;
}
}
}
public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
{
return defaultFactory.GetControllerSessionBehavior(requestContext, controllerName);
}
public void ReleaseController(IController controller)
{
defaultFactory.ReleaseController(controller);
}
}
As you can see, we're just using the default (Ninject) controller factory for most purposes unless it can't find the page. For obtaining the error controller, you can either pass in the kernel as you were already doing, or just call defaultFactory.CreateController using the error controller name.

How to pass parameters to a CodeActivity in a NativeActivity code sequence

I'm trying to get windows workflows working, and I've become a little stumped.
I've gotten a single workflow working, but now I am trying to do something a little more complex: start a workflow, where each activity itself contains a workflow. (Picture something like the main program starts the activities "Input, logic, and output", and then each of those have additional activities like "prompt user, get input, etc.")
I've had it working fine, with the example from here (http://msdn.microsoft.com/en-us/magazine/gg535667.aspx), when I am not passing any parameters from the main program to the activites. My question is, how exactly does the 'Variables' and 'metadata.SetVariablesCollection' work in the NativeActivity, and how to I get the parameters to the low level activities?
This is what I am currently trying:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Activities;
using System.Collections.ObjectModel;
using System.Activities.Statements;
namespace Project1
{
internal class MainProgram
{
internal static void Main(string[] args)
{
try
{
var act = new SimpleSequence();
act.Activities.Add((Activity)(new WriteSomeText()));
act.Activities.Add((Activity)(new WriteSomeText()));
act.Activities.Add((Activity)(new WriteSomeText()));
act.Variables.Add(new Variable<string> ("stringArg", "TEXT"));
WorkflowInvoker.Invoke(act);
}
catch (Exception ex)
{
System.Console.WriteLine("EXCEPTION: {0}", ex);
}
}
public class WriteSomeText : CodeActivity
{
[RequiredArgument]
public InArgument<string> stringArg { get; set; }
protected override void Execute(CodeActivityContext context)
{
string output = context.GetValue(stringArg);
System.Console.WriteLine(output);
}
}
public class SimpleSequence : NativeActivity
{
Collection<Activity> activities;
Collection<Variable> variables;
Variable<int> current = new Variable<int> { Default = 0 };
public Collection<Activity> Activities
{
get
{
if (this.activities == null)
this.activities = new Collection<Activity>();
return this.activities;
}
set
{
this.activities = value;
}
}
public Collection<Variable> Variables
{
get
{
if (this.variables == null)
this.variables = new Collection<Variable>();
return this.variables;
}
set
{
this.variables = value;
}
}
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
metadata.SetChildrenCollection(this.activities);
metadata.SetVariablesCollection(this.variables);
metadata.AddImplementationVariable(this.current);
}
protected override void Execute(NativeActivityContext context)
{
if (this.Activities.Count > 0)
context.ScheduleActivity(this.Activities[0], onChildComplete);
}
void onChildComplete(NativeActivityContext context, ActivityInstance completed)
{
int currentExecutingActivity = this.current.Get(context);
int next = currentExecutingActivity + 1;
if (next < this.Activities.Count)
{
context.ScheduleActivity(this.Activities[next], this.onChildComplete);
this.current.Set(context, next);
}
}
}
}
}
This ends up throwing the following exception:
EXCEPTION: System.Activities.InvalidWorkflowException: The following errors were encountered while processing the workflow tree:
'WriteSomeText': Value for a required activity argument 'stringArg' was not supplied.
'WriteSomeText': Value for a required activity argument 'stringArg' was not supplied.
'WriteSomeText': Value for a required activity argument 'stringArg' was not supplied.
at System.Activities.Validation.ActivityValidationServices.ThrowIfViolationsExist(IList`1 validationErrors)
at System.Activities.Hosting.WorkflowInstance.ValidateWorkflow(WorkflowInstanceExtensionManager extensionManager)
at System.Activities.Hosting.WorkflowInstance.RegisterExtensionManager(WorkflowInstanceExtensionManager extensionManager)
at System.Activities.WorkflowApplication.EnsureInitialized()
at System.Activities.WorkflowApplication.RunInstance(WorkflowApplication instance)
at System.Activities.WorkflowApplication.Invoke(Activity activity, IDictionary`2 inputs, WorkflowInstanceExtensionManager extensions, TimeSpan timeout)
at System.Activities.WorkflowInvoker.Invoke(Activity workflow, TimeSpan timeout, WorkflowInstanceExtensionManager extensions)
at System.Activities.WorkflowInvoker.Invoke(Activity workflow)
at Project1.MainProgram.Main(String[] args) in c:\users\user\documents\visual studio 2010\Projects\ModelingProject1\Project1\MainProgram.cs:line 25
I know, I only pass 1 parameter, but the exception still says that I am missing 3 parameters. I am missing something as to how to do this properly.
You're correctly declaring stringArg as an InArgument but you're not passing any value to it when calling it inside SimpleSequence.
You can pass something using the constructor, while constructing the all activity itself, like this:
public class WriteSomeText : CodeActivity
{
[RequiredArgument]
public InArgument<string> stringArg { get; set; }
public WriteSomeText(string stringArg)
{
this.stringArg = stringArg;
}
protected override void Execute(CodeActivityContext context
{
string output = context.GetValue(stringArg);
System.Console.WriteLine(output);
}
}
// Calling the activity like this:
internal static void Main(string[] args)
{
var act = new SimpleSequence()
{
Activities =
{
new WriteSomeText("hello"),
new WriteSomeText("world"),
new WriteSomeText("!")
}
};
WorkflowInvoker.Invoke(act);
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
Also notice that is a best practice to use the constructor to initialize collections:
public SimpleSequence()
{
activities = new Collection<Activity>();
variables = new Collection<Variable>();
}
This way is even more intuitive to initialize the activity:
var act = new SimpleSequence()
{
Activities =
{
new WriteSomeText("hello"),
new WriteSomeText("world"),
new WriteSomeText("!")
},
Variables =
{
new Variable<int>("myNewIntVar", 10),
// ....
}
};
EDIT:
There are a couple of other ways to approach the problem. This is your best friend while starting in the WF4 world.
Check WF\Basic\CustomActivities\Code-Bodied for a little push with this particular case.

Resources