c# - How do you use RoutingState.NavigateCommandFor<T> properly -
i trying follow view model navigation pattern established in xamforms playground ran troubles getting work properly.
given following code, can see problem is:
// router = new routingstate(); // navigate = router.navigatecommandfor<myviewmodel>(); this.whenanyvalue(x => x.viewmodel.navigate) //.do(x => x.canexecuteobservable.subscribe()) .bindto(this, x => x.navigatebutton.command); this.whenanyvalue(x => x.viewmodel.router) .bindto(this, x => x.viewhost.router);
if bind navigate
command button's command
object, it's initial canexecute
state false
, there never adjustment initial state. if uncomment do
code above force initial state computed , button enables appropriately.
obviously, not correct me, expect initial canexecute
state true (at least after ui loaded).
what worse, appears heisenbug because if ever check canexecute
state of routingstate.navigate
or myviewmodel.navigate
commands (with canexecute(null)
), return true
, button display enabled upon continuation (note have check view model command enable button).
i want apply pattern correctly quite confused why seemingly simple pattern failing scenario. can confirm or deny correct approach?
doing more research on question able clear confusion, still not quite answer question. below discoveries:
first, proper way bind command according online documentation, is
this.bindcommand(viewmodel, x => x.navigate, x => x.navigatebutton);
the code sample posted above should this, if in proper (current) reactiveui framework:
this.bindcommand(viewmodel, x => x.navigate, x => x.navigatebutton); // note used "hack" binding router above, correct binding strategy this.onewaybind(viewmodel, x => x.router, x => x.viewhost.router);
in order (as seamlessly possible) create navigation commands routable view models, have created extensions methods (temporarily suffixed numbers) perform canexecuteobservable
subscribe before returning command. @ time i'm not sure best choice extension method be, i'm leaning towards numbers 3 , 4 produce typed generic command (which give automatic observability, compared ireactivecommand
).
// navigatecommandfor extensions provide ability inject navigate command (the default navigate, or navigateandreset), // dependency resolver (still defaults locator.current), , type resolution contract public static partial class extensionmethods { // simple extension inline subscription public static t subscribetocommand<t>(this t this) t : ireactivecommand { this.canexecuteobservable.subscribe(); return this; } // first attempt without analyzing current navigatecommandfor source public static ireactivecommand<object> navigatecommandfor1<t>(this routingstate this, ireactivecommand<object> navigationcommand = null, idependencyresolver dependencyresolver = null, string contract = null) t : iroutableviewmodel { navigationcommand = navigationcommand ?? this.navigate; var ret = reactivecommand.createasyncobservable(navigationcommand.canexecuteobservable, _ => navigationcommand.executeasync((dependencyresolver ?? locator.current).getservice<t>(contract))); return ret.subscribetocommand(); } // after looking @ navigatecommandfor source, minimalistic adaptation // except ajusting return value generic interface object type param public static ireactivecommand<object> navigatecommandfor2<t>(this routingstate this, ireactivecommand<object> navigationcommand = null, idependencyresolver dependencyresolver = null, string contract = null) t : iroutableviewmodel { navigationcommand = navigationcommand ?? this.navigate; var ret = new reactivecommand<object>(navigationcommand.canexecuteobservable, x => observable.return(x)); ret.select(_ => (dependencyresolver ?? locator.current).getservice<t>(contract)).invokecommand(navigationcommand); return ret.subscribetocommand(); } // attempt optimize return value typed // i'm not sure if has implications public static ireactivecommand<t> navigatecommandfor3<t>(this routingstate this, ireactivecommand<object> navigationcommand = null, idependencyresolver dependencyresolver = null, string contract = null) t : iroutableviewmodel { navigationcommand = navigationcommand ?? this.navigate; var ret = new reactivecommand<t>(navigationcommand.canexecuteobservable, _ => observable.return((dependencyresolver ?? locator.current).getservice<t>(contract))); ret.invokecommand(navigationcommand); return ret.subscribetocommand(); } // original source allows un-registered view models new()'d // extension provides same ability, again typed return value public static ireactivecommand<t> navigatecommandfor4<t>(this routingstate this, ireactivecommand<object> navigationcommand = null, idependencyresolver dependencyresolver = null, string contract = null) t : iroutableviewmodel, new() { navigationcommand = navigationcommand ?? this.navigate; var ret = new reactivecommand<t>(navigationcommand.canexecuteobservable, _ => observable.return((t)((iroutableviewmodel)(dependencyresolver ?? locator.current).getservice<t>(contract) ?? new t()))); ret.invokecommand(navigationcommand); return ret.subscribetocommand(); } }
i'm still not sure of these extensions appropriate, or why subscribing canexecuteobservable
necessary (i assume bug in reactivecommand
), above considered viable work-around problem posted (however not answer question still think not proper usage of navigatecommandfor
).
Comments
Post a Comment