_@StephaneDelcroix - Don't close this issue as I have no option to reopen #9954_
Can someone from Xamarin Forms team please reopen the #9954, read my issue description, review the attached screenshot and please help me with a meaningful input with a link or copy of a sample solution ? I would really appreciate this.
Please don't close issues without reading and responding with some meaningful responses.
We do invest our time in using this product and writing these issues. Please respect our time too.
Hi @andreinitescu - I am looping you in per your comments in https://github.com/xamarin/Xamarin.Forms/issues/4835 & @legistek - as the author for PR of RelativeSource Binding.
Your help will be appreciated.
Thank you.
@IoTFier I think this is indeed a duplicate of #9610. The explanation is that Behaviors don't have unique visual tree parents so you can't do a RelativeSource binding on them. Behavior instances can be shared among multiple elements so there'd be no way to know which element to use as the parent.
Maybe Self could be made to work. And this could also maybe be worked around if we evaluated the parent at XAML parsing/compilation time. Maybe worth looking into some day but certainly not something I would have the bandwidth for currently.
@legistek - I really appreciate your response. So, it looks like I am totally stuck at the moment - because as mentioned by @StephaneDelcroix - "Behaviors and Triggers inherit from BindableObject, but that's was a design mistake."
Any workaround that I can implement - where a ListView is populated at runtime to provide list of options to choose from (dynamic radiobuttons like functionality). User is allowed to select only one option via toggling the switch on selected item (among items rendered by of the DataTemplate) to enable it. It will reset switches on the other items. Thoughts? I tried using behavior but can't traverse parent hierarchy based on the sender object in the Toggled Event handler. .... would appreciate your input. Thank you.
I mean, that sounds perfectly doable without Behaviors. Have a SelectItemCommand command in the view model (ViewModel) that accepts the selected item as a command parameter. In the data template for each item, say Command="{Binding SelectItemCommand, Source={RelativeSource AncestorType={x:Type local:ViewModel}}}" CommandParameter="{Binding}"
Then in SelectItemCommand perform the logic that selects the one item and de-selects the others.
Wouldn't that work?
I mean, that sounds perfectly doable without Behaviors. Have a
SelectItemCommandcommand in the view model (ViewModel) that accepts the selected item as a command parameter. In the data template for each item, sayCommand="{Binding SelectItemCommand, Source={RelativeSource AncestorType={x:Type local:ViewModel}}}" CommandParameter="{Binding}"Then in
SelectItemCommandperform the logic that selects the one item and de-selects the others.Wouldn't that work?
Not really. Because, the SelectionMode=None in the list view. Otherwise, the UX changes completely. I don't know if RadioButton coming with XF 4.6 will support such functionality.
It wouldn't have anything to do with SelectionMode. Whatever control you're using for each item in the list, I'm assuming it has a Command property for when it's clicked that you can bind to. Like a Button would and like a RadioButton certainly will/should.
Hi @legistek, thank you but I am still not following. How will the command property - say for the outermost stacklayout in the structure outlined below, will be fired when switch is toggled ?
(trying to give pseudocode but not getting indented .. so is the space after "<" )
I am using Stacklayout that defines ItemTemplate of 2 labels and a switch and a box view
< DataTemplate>
< ViewCell>
< StackLayout>
< StackLayout>
< Label>
< Label>
< Switch> <= currently trying to use EventToCommandBehavior for this switch via Behavior
< /StackLayout>
< Box>
< /StackLayout>
< /ViewCell>
< /DataTemplate>
Now I see. Sorry I would have assumed Switch had an ICommand Command property or at least a ToggledCommand that got invoked when it was toggled. (That might be a good suggestion to make). I forget that XF doesn't always follow the WPF model.
But anyway there are still ways around this. You can implement your own ToggledCommand as an attached property for example.
This is probably a better question for StackOverflow than as an open XF issue because it sounds like you might just need some guidance on hacking XAML to make it work with MVVM. I suggest you post a question there along with your markup and if you post the link here I'll try to get over there and answer it, and maybe others will have other ideas too.
Will work on it.
But, before I got that far I would like to see just using Button and change the Text as selected and not-selected if that at least gives me a solution closer to the UX. If that were to work, how to crawl up the hierarchy to get ItemSource having all Items shown in the ListView ? Is that tricky as well ?
Hacking XAML is a good passion - but very costly while I focus on building and delivering business values. :(
If that were to work, how to crawl up the hierarchy to get ItemSource having all Items shown in the ListView ? Is that tricky as well ?
Assuming some view model has access to that list, that's exactly what the RelativeSource binding example I gave you above does for you, so you don't ever have to crawl yourself.
@StephaneDelcroix I have an idea of how to solve his problem outright and improve EventToCommandBehavior if you (or anyone) wanted to take a stab at it:
Basically EventToCommandBehavior would behave more like a Setter.
Instead of ICommand Command make it object Command. (CommandParameter is already an object). In OnAttachedTo, if Command and/or CommandParameter are of a BindingBase type then behavior would be different. Specifically it would clone the binding and bind it to a property on the target. But which property? EventToCommandBehavior could create attached BindablePropertys on the fly as needed if they don't already exist (e.g., $"{EventName}Command" and $"{EventName}CommandParameter"). Then it would apply the cloned binding(s) to the view via the appropriate attached property(s).
In OnEvent, it would resolve the actual value for Command and CommandParameter not from its own property values but from the attached property value for sender.
Then RelativeSource bindings should work.
This would basically automate the MVVM hack that has long been used for turning events into commands through attached properties. But a lot more elegant I think because it would hide the jumbled mess of attached properties that are out there.
If that were to work, how to crawl up the hierarchy to get ItemSource having all Items shown in the ListView ? Is that tricky as well ?
Assuming some view model has access to that list, that's exactly what the
RelativeSourcebinding example I gave you above does for you, so you don't ever have to crawl yourself.
@legistek - I made some progress using Label (to show font icon for selection and non-selection) and and its command and commandParameter. I get control to my viewmodel command function. But, after I update observable collection (to set and reset new selection) and after firing OnPropertyChanged on main (UI) thread, converters for each Item in the DataTemplate don't get fired again to reflect the new font icon. Am I missing anything here ? Thank you for your help.
@IoTFier glad you're making progress but you really need to take this conversation to another venue. because this is not about a bug in XF. Please close this issue and follow my suggestion and post on SO or another site and include your code and I or someone else will try to help.
@legistek - Regarding relativesource binding in the context of behavior it is still a bug (though a shortcoming by design.) agree? _I would like to see response from Xamarin forms team on your suggestion before I close this issue_. I think it is a very helpful capability that's missing right now.
Reg the last conversation - I will take it outside of this venue. What's the best email to communicate with you?
@StephaneDelcroix I have an idea of how to solve his problem outright and improve
EventToCommandBehaviorif you (or anyone) wanted to take a stab at it:Basically
EventToCommandBehaviorwould behave more like aSetter.Instead of
ICommand Commandmake itobject Command. (CommandParameteris already anobject). InOnAttachedTo, ifCommandand/orCommandParameterare of aBindingBasetype then behavior would be different. Specifically it would clone the binding and bind it to a property on the target. But which property?EventToCommandBehaviorcould create attachedBindablePropertys on the fly as needed if they don't already exist (e.g.,$"{EventName}Command"and$"{EventName}CommandParameter"). Then it would apply the cloned binding(s) to the view via the appropriate attached property(s).In
OnEvent, it would resolve the actual value forCommandandCommandParameternot from its own property values but from the attached property value forsender.Then
RelativeSourcebindings should work.This would basically automate the MVVM hack that has long been used for turning events into commands through attached properties. But a lot more elegant I think because it would hide the jumbled mess of attached properties that are out there.
Hello @davidortinau , @StephaneDelcroix , @samhouts , @pauldipietro -
Any thoughts ? This fix will also help in using Radio Buttons in similar design instead of using Switches.
@IoTFier
I'm going to close this issue, as it does not indicate a Xamarin.Forms bug.
Thanks!
@samhouts sorry it's my fault I didn't realize the original EventToCommandBehavior (which has been forked countless times) wasn't under your project domain.
But @IoTFier this only underscores the fact that you or anyone could easily make your own variant of EventToCommandBehavior that would work the way you want it to, using my outline above.
@legistek and @samhouts -
This doesn't have to do with EventToBehavior feature provided by Prism. That one is simply providing helper boiler plate to wire command pattern with XF View Element Commands.
The real issue is around Xamarin Form's behavior not working with a view element when it is not providing any Commands. @samhouts - may I please request you to revisit the suggestion provided by @legistek where the enhancement of the RelativeSource binding can provide us with the capability to handle events like Toggled of Switch.
I am more than happy to submit for a feature request if that's needed.
RelativeSource binding has been a great capability ever since it is launched when dealing with core XF Controls and 3rd party controls which may not always follow their implementation as XF team.
We will have similar need when implementing radiobutton from upcoming XF 4.6 when implemented in a DataTemplate.
Thanks
@IoTFier you're conflating different features and assuming that because in combination they don't work the way you want them to that there is a bug or some enhancement necessary to achieve what you want.
No "enhancement" to RelativeSource binding is necessary, or will solve your issue. Your issue is that you need a mechanism to attach a command to a control that doesn't accept an ICommand but only exposes events. This is one that has been faced - and dealt with - by MVVMers since the WPF days. Until now you have been using EventToBehavior, but Behaviors are, by design, not compatible with RelativeSource bindings because they are not part of the visual tree.
So you need another solution, which has been offered. If you're not able to implement it on your own, then I will suggest one last time that you seek help, on an appropriate forum (which this is not), with doing either what I suggested or soliciting suggestions from others. The bottom line is that no modifications to XF are necessary to do what you want, and there is no bug in what you've described. I'm unsubscribing from this issue now.
@legistek - Thank you. I got it as you explained it now.
Thanks.
@IoTFier How did you solve it? I have an EventToCommandBehavior in a datatemplate
@legistek, as I had bumped into the same issues as described here (see #12660), I thought I'd give your suggestion a try. Unfortunately, though, resolution of RelativeSource-bindings seems to occur somewhere as a higher level prior - i.e. prior to the value being passed on to the EventToCommandBehavior's Command-property. What I did was change the type of both my CommandProperty bindable property and Command-property to object, in hopes of indeed receiving a BindableBase that I;d then be able to resolve from within the Behavior itself. Unfortunately, however, when I ran the code, the same InvalidOperationException was thrown as before. Thinking this might be due to XAML pre-compilation, I annotated my XAML to be skipped. However, this also didn't seem to resolve the issue.
To me this makes the issue the stranger, as - apparently - an attempt is made to resolve the RelativeSource before the it's value is passed into the Behavior's bindable property (even if this attempt results in the exception being thrown). This, I believe, would invalidate the argument of resolution not being possible because a single instance of the Behavior could be bound to multiple components - right? This leaves the argument of the visual tree. But, again, if the step of resolving a RelativeSource takes place _before_ it reaches the Behavior, why wouldn't it be able to determine what control the Behavior is associated with during this resolution - especially in XAML. Code-behind might be a different matter, though... I don't know. I still find it counter-intuitive, especially as the resolution is not accessible...
As an aside, your suggestion of using an attached property won't really work either, as this would leave an event handler hanging around when the control to which the property was attached gets disposed/cleared. For now, I'll just stick to using x:Reference, though I would've loved to get rid of as many of them as possible...
I can't say why your modifications to the EventToCommandBehavior aren't working without seeing the code. But I can say, once again, that RelativeSource bindings cannot be applied to objects that don't go in the visual tree. It's simply impossible and if you're getting the exception it's because something is trying to do just that.
Remember RelativeSource works for Setters only because the setter Value takes the BindingBase directly and holds onto it until the Style is applied, at which point the BindingBase is cloned and "applied" to the visual element - but never to the Setter. So you need to do something similar.
Is EventToCommandBehavior's Command property a Bindable property? If so that could be your issue. It needs to be a POCO property for my idea to work.
As for attached properties, that technique has been around at least 10 years and works fine. It's not a problem that event handlers remain attached as long as you remove the event handler when the property is set to null and otherwise don't re-attach the handler every time the value changes.
Plus once the element is out of the visual tree it shouldn't neceive UI events anyway.
There's also no memory leak problem because the event handler you're adding will be static, its sole purpose being to retrieve the attached ICommand value and execute it. So I really don't see why the attached property route is an issue.
I think that with the EventToCommandBehavior it may indeed have been the fact I was still using a BindableProperty then... I don't think I'll invest more time in this, but thanks for pointing that out to me :)
As to using an AttachedProperty, I don't believe those get cleared - i.e., set to null - automatically. At least, this didn't show up in a test I did. As such, as with the attached property being defined in an extension class (I'm trying to make this work for a ListView), my understanding is that the event handler will remain alive, even after the control the property was attached to has been destroyed. That is, unless static event handlers act in some way differently from instance ones. Of course, I could "manually" clear the attached property in a Dispose-method, but this, I think, would cause to much clutter (my MVVM-framework implementation handles these things for Behaviors and Effects, so no clutter there). It's possible that this pattern has existed for a long time, but a lot of these low-grade memory leaks as with page disposal have gone unnoticed for a long time as well...
As to my attached property implementation, those interested can find it below:
public static class ListViewExtensions
{
public static readonly BindableProperty ItemTappedCommandProperty =
BindableProperty.CreateAttached("", typeof(ICommand), typeof(ListView), default(ICommand), propertyChanged: OnItemTappedCommandChanged);
public static ICommand GetItemTappedCommand(BindableObject bindable)
=> (ICommand)bindable.GetValue(ItemTappedCommandProperty);
public static void SetItemTappedCommand(BindableObject binable, ICommand itemTappedCommand)
=> binable.SetValue(ItemTappedCommandProperty, itemTappedCommand);
private static void OnItemTappedCommandChanged(BindableObject bindable, object oldValue, object newValue)
{
ListView control = bindable as ListView;
if (control == default(ListView))
{
return;
}
ICommand oldCommand = (ICommand)oldValue;
ICommand newCommand = (ICommand)newValue;
control.ItemTapped -= OnItemTapped;
if (newCommand != default(ICommand))
{
control.ItemTapped += OnItemTapped;
}
}
private static void OnItemTapped(object sender, ItemTappedEventArgs e)
{
ListView control = sender as ListView;
if (control == default(ListView))
{
return;
}
ICommand command = GetItemTappedCommand(control);
if (command == default(ICommand))
{
return;
}
if (command.CanExecute(e.Item))
{
command.Execute(e.Item);
}
}
}
As to using an AttachedProperty, I don't believe those get cleared - i.e., set to null - automatically.
If the binding is based on the object's BindingContext, it should get cleared to null when the object is removed from the visual tree. Even if the binding is not based on the BindingContext , it would be impossible for the control to receive ItemTapped after it's out of the visual tree, so the command would never get invoked anyway. Thus if your concern is the time between when the control is removed from the visual tree but before GC, I think this is a non-issue.
my understanding is that the event handler will remain alive, even after the control the property was attached to has been destroyed.
You have to be careful what you mean by "destroyed" but if you mean GC'd, then no, the reference to the event handler is released when the event owner object has been GCd.
but a lot of these low-grade memory leaks as with page disposal have gone unnoticed for a long time as well...
The main circumstance where event handlers lead to memory leaks is when the event handler is specific to a class instance and the event owner is static or otherwise long lived. This is because the event owner gets a hard reference to the owner of the event handler which prevents GC of the handler owner.
This does not occur when using attached properties to add event handlers to controls as long as the handler is static - which it is in your code - because the handler is not owned by any instance. You can see in your own code that no reference to bindable survives OnItemTappedCommandChanged. And merely adding a handler to control obviously does not impact whether control can be GCd.
So yes, static and instance event handlers are different in this regard.
Now, if the binding is not dependent on BindingContext, then it is true that control could hold a reference to your view model (via the ICommand), even after it's removed from the visual tree, which could delay GC of the view model. However this is not unique to using an attached property - and would be the case even with EventToCommandBehavior - and does not lead to memory leaks because when the control is GCd, that reference will be released.
You have to be careful what you mean by "destroyed" but if you mean GC'd, then no, the reference to the event handler is released when the event owner object has been GCd.
True, I probably should've been more careful with my wording here... Yet, why I used "destroyed" was because I attempted catch both the cases of regular object finalization as well as explicit disposal (using IDisposable). However, I seem to indeed have gotten confused about which object can and which can't be GC'd when event handlers are not torn down. It's not the event source that can't be GC'd, but the event target, since, as you pointed out, the event source maintains a reference to the target by way of the handler delegate.
As it seems that static objects indeed don't ever get GC'd, the extension class that defined the attached property would outlive the class to which the attached property is applied. That means that the event source can be GC'd, and there's no issue with the static event handler lingering, as this would be the case anyway (plus, it's only a single reference shared by all consumer instances).
I'm sorry it took me some time to catch up on this, but thanks for being patient with me and explaining, @legistek! Seems like the attached property is a good alternative after all. Also, the reason my test didn't show the attached property getting cleared is probably due to the binding context not being cleared either. But as this is not a problem anyway, I might switch to the attached property approach after all.. Thank you!