I'm trying to use a view-to-view binding using x:Reference, but am having no luck. Specifically, I am binding the command/parameter of a TextCell's context actions menu. I wish to bind the command itself to an ICommand property of the main view model, while keeping the command parameter bound to the current item of the ListView that contains the cells. So this means using x:Reference as the Source of the command binding. Yet this doesn't seem to work.
I tried digging through the Xamarin Forms code using dotPeek to see if I could figure out what was going wrong - and I don't even see a ReferenceExtension class in XF 1.3 (Stable)?? Was this removed from the platform, or am I just looking in the wrong place?
My code looks very similar to this, however the binding's Source is totally being ignored:
<ListView ItemsSource="{Binding EventsList}" x:Name="EventsList"> <ListView.ItemTemplate> <DataTemplate> <TextCell Text="{Binding Name}"> <TextCell.ContextActions> <MenuItem Text="Delete" IsDestructive="True" Command="{Binding Path=BindingContext.DeleteEventCommand, Source={x:Reference Name=EventsList}}" CommandParameter="{Binding}"/> </TextCell.ContextActions> </TextCell> </DataTemplate> </ListView.ItemTemplate> </ListView>
The ListView's BindingContext is a viewmodel class that has an ObservableCollection of items named EventsList, as well as an ICommand named DeleteEventCommand.
Posts
I wasn't able to get x:Reference working, but I was able to create a suitably simple workaround for this using a custom markup extension.
Markup extension code:
Updated XAML:
This does essentially what x:Reference is supposed to do (I think).
Thanks for posting this. It would be interesting/helpful/useful to know if x:Reference is planned for XF. Maybe file a bug report?
Good point Michael. I've submitted it as a bug: https://bugzilla.xamarin.com/show_bug.cgi?id=27532
@TheRealJasonSmith @KeithRome
x:Reference works as expected in other scenarios, e.g. I use bindings in ListView headers like this (set on container element inside header):
BindingContext="{Binding Source={x:Reference root}, Path=BindingContext}"
where 'root' points to the ContentPage element.
It seems to me that x:Reference only breaks when used inside DataTemplate. I try to use that for cell ContextAction command binding (like Keith) and experience the same behavior. Since there was no mention whatsoever in the reference documentation that DataTemplate had been intended to have this effect on x:Reference, I think it is safe to say that there is a major bug there.
By the way, without command binding there is no way to properly implement enabled/disabled context menu actions other than hacking in the code-behind file, or setting DataTemplate's BindingContext explicitly from code-behind, both of which workarounds are rather inelegant.
You can use Xamarin.Forms.Behaviours, it supports relativecontext binding
https://www.nuget.org/packages/Xamarin.Forms.Behaviors/
<ListView Grid.Row="1" ItemsSource="{Binding Customer}" VerticalOptions="Fill" x:Name="ListviewCustomer"> <ListView.ItemTemplate> <DataTemplate> <local:CustomImageCell Text="{Binding CustomerName}" Detail="{Binding AccountNo}"> <b:Interaction.Behaviors> <b:BehaviorCollection> <b:EventToCommand CommandNameContext="{b:RelativeContext CustDetails}" EventName="Tapped" CommandName="SelectCommand" CommandParameter="{Binding .}" /> </b:BehaviorCollection> </b:Interaction.Behaviors> </local:CustomImageCell> </DataTemplate> </ListView.ItemTemplate> </ListView>
@NMackay
I couldn't find a way to attach behaviors to ContextAction's MenuItem. But this approach just works.
@KeithRome - Thanks for sharing this.
@KeithRome you're a hero. Thanks for sharing.
@KeithRome nice to see your workaround working for you. just know that it would return wrong values if you try to reference object by name in the DataTemplate itself, because you're using the wrong NameScope.
Thanks - I didn't think to try out that scenario. My use case was in a quick prototype for a client, so supporting data templates wasn't an issue. Do you have a way to solve that case easily?
That utility class to set the binding context worked great for me thanks!
@KeithRome, your approach works fine, but if I move the DataTemplate to <Application.Resources>....</Application.Resources> (in order to reuse the DataTemplate in more than one views) it does not work.
Do you know why?
This is the source code of the ListView:
And this is the source code of the app resources:
Please, can you help me?
@Francisco.7655
The problem is, that the rootProvider is not the root of the whole page, but only of the current file. If you use it in a DataTemplate, then the ElementSource won't find the element you defined the x:Name on.
@StephaneDelcroix said above, that the NameScope is wrong, but neither Keith nor I know how to do it better.
@MichaelRumpler I do
. This should be fixed in 1.4.3.
see https://bugzilla.xamarin.com/show_bug.cgi?id=30684
@StephaneDelcroix, I am working with 1.4.3 and I get this error.
Did you get it to work?
@Francisco.7655 could you please share your xaml, and the error you are seeing ?
I am using just the same ElementSource posted markup posted in this topic:
@StephaneDelcroix, I am not getting any error, but the button I had referenced now is no working.
As you can see in my xaml code, I have two commands binded using ElementSource:
When I was defining the DataTemplate in the related view, this was working fine. But it left to work once I moved the template to a ResourceDictionary (in order to share this template between two very similar screens).
@Francisco.7655:
x:Reference
is fixed in 1.4.3. I have no idea what's the status ofElementSource
Ah, ok, I wil try with x:Reference.
How should I use it? Just like below?
@StephaneDelcroix, how are you using x:Reference?
Thanks!!
Please, somebody can help me?
@Francisco.7655 : you shouldn't need to use ElementSource code above any longer. ReferenceExtension now appears in the current build of Xamarin Forms. ElementSource was just a temporary workaround written for a time when ReferenceExtension was missing.
@KeithRome, ok, so I am trying to use x:Reference but it isn't working for me.
I have the following template inside ResourceDictionary in my App.xaml (because this template will be shared in some views).
I am using x:Reference in order to do the binding for + and - buttons commands.
And in my ItemsSearchView, I am using the SelectableItemsListViewTemplate template and the name of the list is ItemsListView as I declared in the template.
However, the binding is not working, because when I tap on the buttons, nothing is happening.
Am I using correctly x:Reference?
@Francisco.7655
Does the data template work if you just put it in your page? I've seen a few issues with people trying to use shared data templates in this way.
@Franceso.7655: this is very unlikely to work. In a resource dictionary, you shouldn't x:Reference anything outside of the ResourceDictionary.
Yeah, the "ItemsListView" you are trying to reference needs to be reachable. What you really want is something that works like
{RelativeSource TemplatedParent}
from WPF/Silverlight. But that doesn't exist in Xamarin Forms markup.Ok, so....no templates in ResourceDictionary
-1 for Xamarin Forms....
Templates are fine in ResourceDictionary. What you were specifically trying to do (access an external element) from within a template in a ResourceDictionary is what doesn't work.
I am having bit of issues setting the binding programatically.
I have a page with a listview and the data template for the listview is in a different cs file. Does anyone know how to bind the button command to the page bindingcontext not the list view item bindingcontext.
I tried the below code as shown in the OPs post programatically
cameraButton.SetBinding( Button.CommandProperty, new Binding("CameraCommand", BindingMode.Default, null, null, null, new ReferenceExtension { Name = "listView"}) );
and this
cameraButton.SetBinding( Button.CommandProperty, new Binding("BindingContext.CameraCommand", BindingMode.Default, null, null, null, new ReferenceExtension { Name = "listView"}) );
I get
@LeonardoSuryana: you wouldn't instantiate a ReferenceExtension in that case. Markup Extensions are only needed to convert string input from the source XAML to .NET objects... sort of like a smart parser.
Instead of passing
new ReferenceExtension { Name = "listView"})
, just pass in a reference to thelistView
object directly.I was able to get this to work using the fix for x:Reference inside a DataTemplate supplied in Xamarin.Forms v1.4.4
For a code example, see my post here: http://forums.xamarin.com/discussion/comment/144204/#Comment_144204
hi FranciscoCG.....are u getting solution to Button inside Listview.....can u plz share the solution if u have....thanks in advance
If a view model is set to the BindingContext in XAML like this:
I found I could implement IMarkupExtension.ProviderValue(IServiceProvider) like this:
This approach returns my view model from within XAML:
This means that the value in ElementName is ignored.
Here is the command in the XAML:
...
<ListView.ItemTemplate>
<ViewCell.View>
...
...
</ViewCell.View>
</ListView.ItemTemplate>
...
The command parameter binding is on my object associated with my list, while the command is coming from my view mode class, which is found in the BindingContext of the page XAML as shown previously.
For anyone else that stumbles upon this thread I've found that you can get around the problem of x:Relative not working in a DataTemplate by using a BindingProxy, as explained by Thomas Levesque at thomaslevesque.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/. The Xamarin Forms version of the BindingProxy class is as follows:
To use it you need to give your content page a name:
Then in your resource dictionary (say) declare a proxy that binds to _this, and use StaticResource in your Command bindings to bind to the proxy:
This particular example uses a DataTemplate to add a behavior called
CommandBehavior
and binds its command handler to a property in the ContentPage's view model calledClickedCommand
, the use of StaticResource is what allows you to side-step x:Relative altogether.Fantastic, @MarkFeldman, you posted this yesterday. Today I can use it to change our bindings (getting rid of behaviour:RelativeContext) and enable XamlCompilation! Thanks!
hello @MarkFeldman and @14skywalker , why is this solution necessary when you can use 'RelativeSource' as shown by TomSonderling?
Am I right, when I assume that the data template has to reside in the content page you named '_this' and it does not work, when the data template resides in App.xaml ?
I liked the idea of the markup extension and fixed it for XamlCompilation... However, keep in mind: This will only work als long as the Xamarin Team will keep the functions and properties used alive. (Xamarin.Forms.2.3.4.247 works)
I'm try get a text and another functions that i have in bindingcontext in my view and use inside of listview. Used this method that @KeithRome show us, but not work.
I want that property MenuItem show text that exist in class inside BindingContext. Other bind works.
I'm using Xamarin Forms.
Inside Path Custom Objects i have a class exacly he show.