I've been tackling a problem regarding databinding in XAML for a little while now.
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Pedagouge.Views;assembly=Pedagouge"
x:Class="Pedagouge.Views.StudentGroupView">
<StackLayout>
<ListView x:Name="Students">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<local:PhotoView x:Name="Photo" Persona="{Binding}" />
<StackLayout Orientation="Vertical" HorizontalOptions="StartAndExpand" VerticalOptions="StartAndExpand">
<Label Text="{Binding FullName}" />
<Label Text="{Binding StudentIsAt.Group.Name}" />
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
I thought I had binding figured, but the problem I'm having clearly shows that I'm not.
Anyways, the problem is that for the custom control PhotoView it seems that the binding context used by {Binding} to the property Persona is the PhotoView's BindingContext, not the DataTemplate's context, as it is for the Labels a couple of rows down.
Had this been WPF, I would have just changed the binding to
Persona="{Binding DataContext,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ListBoxItem}}"
and that would probably have fixed it, but that won't work here (RelativeSource is apparently not defined).
How come? How can I make Persona bound to the DataTemplates context, as is the case with the Labels, instead?
Xamarin.Forms (up to 1.2.x) bindings always use the BindableObject.BindingContext as the context binding.
In the case of templates items, the item context is set to the template element, in this case the ViewCell, and recursively to the ViewCell.View, the StackLayout, your PhotoView, the inner StackLayout and the 2 Labels.
The fact that FullName and StudentIsAt.Group.Name works is a strong hint.
So, when you say
[...] for the custom control PhotoView it seems that the binding context used by
{Binding} to the property Persona is the PhotoView's BindingContext, not the
DataTemplate's context, [...]
it's both true and wrong.
The binding context used by {Binding} is the PhotoView's Bindingcontext, which is also the DataTemplate's context.
If the Binding does not work as you expect, it's probably because PhotoView somehow set the BindingContext to this, and prevent the {Binding} to work as expected, but I can't say without seeing PhotoView's code.
Related
There is nothing wrong with my code as I can see, but this error I have never seen before keeps coming up, and the Entry element (Input Field) doesn't show up in the UI when I run the app. How to get rid of this problem?
<StackLayout>
<Entry
x:Name="InputField"
Text=""
Placeholder="Enter Todo"
TextChanged="HandleTextChanged"
Completed="HandleCompleted"/>
</StackLayout>
<StackLayout x:Name="TodoList">
</StackLayout>
As Jason suggested in the comments, The Content property of the ContentPage can only accept one direct child element. To get around this, you need to use a Layout container, like a StackLayout or a Grid.
Here's the code sample below for your reference:
<ContentPage.Content>
<StackLayout>
<StackLayout>
<Entry
x:Name="InputField"
Text=""
Placeholder="Enter Todo"
TextChanged="HandleTextChanged"
Completed="HandleCompleted"/>
</StackLayout>
<StackLayout x:Name="TodoList">
</StackLayout>
</StackLayout>
</ContentPage.Content>
I want to bind to a variable from inside a CustomControl referenced in a ListView DataTemplate
This is the situation:
I have a custom control containing a ListView with many DataTemplates to display the various ViewCell (it's basically a set of dynamic fields that must be displayed according to their "type").
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:s4gvForms="clr-namespace:S4GVMobile.Forms;assembly=S4GVMobile"
xmlns:s4gvControls="clr-namespace:S4GVMobile.Controls;assembly=S4GVMobile"
x:Class="S4GVMobile.Controls.AttributesList"
x:Name="S4GVAttributesListControl"
>
<ContentView.Resources>
<ResourceDictionary>
<!-- MANY DATA TEMPLATES HERE, ONLY ONE LEFT FOR REFERENCE -->
<DataTemplate x:Key="s4gvAttributePictureTemplate">
<ViewCell x:Name="s4gvAttributePictureViewCell">
<ViewCell.View>
<StackLayout Orientation="Vertical">
<Label Text="{Binding Attribute.Key}" />
<s4gvControls:AttributePicture ParentID="{Binding Source={x:Reference s4gvAttributePictureViewCell}, Path=Parent.BindingContext.EntityID}" AttributeValue="{Binding .}" />
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
<!-- ... -->
<!-- AGAIN, LONG LIST IN THE SELECTOR -->
<s4gvForms:AttributeDataTemplateSelector x:Key="s4gvAttributeDataTemplateSelector"
AttributePictureTemplate="{StaticResource s4gvAttributePictureTemplate}"
/>
</ResourceDictionary>
</ContentView.Resources>
<StackLayout>
<ListView x:Name="s4gvAttributes" HasUnevenRows="True" ItemTemplate="{StaticResource s4gvAttributeDataTemplateSelector}" ItemsSource="{Binding AttributeValues}" />
</StackLayout>
I'm passing to this custom control both the list of items (required by the ListView) and an additional EntityID.
<s4gv:AttributesList x:Name="s4gvSerialAttributes" AttributeValues="{Binding Serial.AttributeValues}" EntityID="{Binding Serial.SerialID}" VerticalOptions="FillAndExpand"/>
The custom control has the required bindable properties (AttibuteValues and EntityID) and relies on a dedicated ViewModel
I need to retrieve the EntityID from inside the DataTemplate (and put it in the ParentID property); this value is in the custom control's ViewModel but it is "outside" the ListView's DataTemplates. I've tried using the Parent.BindingContext (it works on Command and CommandParameter) but it seems that it can only go up to the ListView's context and not higher though I need to move one further step up to the CustomControl itself.
<s4gvControls:AttributePicture ParentID="{Binding Source={x:Reference s4gvAttributePictureViewCell}, Path=Parent.BindingContext.EntityID}" AttributeValue="{Binding .}" />
Any idea? I'm happy to restructure the whole thing if required.
i keep getting this error: Items collection must be empty before using ItemsSource, when i attempt to bind data to a Panorama control. below is my xaml snippet.
<controls:Panorama x:Name="panorama">
<controls:PanoramaItem >
<StackPanel>
<TextBlock Text="{Binding Text}"/>
</StackPanel>
</controls:PanoramaItem>
</controls:Panorama>
in my code behind (xaml.cs), i do something like this:
protected override void OnNavigatedTo(NavigationEventArgs e) {
string id = NavigationContext.QueryString["id"];
ObservableCollection<MyObject> list = DataAccessService.get(id);
panorama.ItemsSource = list;
base.OnNavigatedTo(e);
}
note that MyObject has a Text property. any help is appreciated.
after modifying it per the link below, the same exception is still thrown.
<controls:Panorama x:Name="panorama">
<controls:Panorama.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
</controls:Panorama.HeaderTemplate>
<controls:PanoramaItem >
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Text}"/>
</StackPanel>
</DataTemplate>
</controls:PanoramaItem>
</controls:Panorama>
finally, after continuing further with the user below's help, this is the solution that got rid of the exception.
<controls:Panorama x:Name="panorama">
<controls:Panorama.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
</controls:Panorama.HeaderTemplate>
<controls:Panorama.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Text}"/>
</StackPanel>
</DataTemplate>
</controls:Panorama.ItemTemplate>
</controls:Panorama>
Your problem is that you're building the Panorama in XAML as though it were static rather than preparing it to be data-bound.
Take a look at this quick tutorial for a Data Binding Panorama Control:
Data Binding Panorama Control WP7 MVVM
Notice the difference in how your XAML is being constructed for the control. Instead of setting the Items collection on the Panorama control, you need to set the HeaderTemplate and ItemTemplate so that the control knows how to render things once data is bound to it.
I can use Interaction.Triggers to catch the textchanged event on a textbox like so:
<TextBox Text="{Binding Title}" Style="{StaticResource GridEditStyle}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<cmd:EventToCommand Command="{Binding TextChanged}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
However when I use it in a datatemplate for a listview celltemplate as follows:
<ListView ItemsSource="{Binding LangaugeCollection}" SelectedItem="{Binding SelectedLangauge}" BorderThickness="0" FontFamily="Calibri" FontSize="11">
<ListView.View>
<GridView>
<GridViewColumn Width="200">
<GridViewColumn.CellTemplate>
<DataTemplate >
<Grid>
<TextBlock Text="{Binding Title}" Style="{StaticResource GridBlockStyle}">
</TextBlock>
<TextBox Text="{Binding Title}" Style="{StaticResource GridEditStyle}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<cmd:EventToCommand Command="{Binding TextChanged}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
the event will not trigger.
Does anyone know why this does not trigger and how to fix it?
When you are in a DataTemplate, the DataContext might not be what you expect. Typically the DataContext in a DataTemplate is set to the item that the DataTemplate represents. If your TextChanged command is on the "main viewmodel" instead of the data item, you need to be more precise in the way that you specify the data binding, for example:
Command="{Binding Source={StaticResource Locator}, Path=Main.TextChanged}"
You can see the issue when you run the code in debug mode (F5) in Studio and observe the Output window. A Data Error will be shown if the DataContext is incorrectly set.
Cheers,
Laurent
It seems something handles the event before the TextBox. Maybe you could listen to Title property (collection) changed inside you ViewModel, because anyway you are calling TextChanged on ViewModel inside the trigger, I suppose.
Btw I think you're missing TwoWay mode in your binding expression.
Hope this helps.
First let me say I'm new to Silverlight. But I have most of the "basic" Silverlight stuff figured out. I'm using Silverlight 3 at the moment.
In a nutshell, I am not seeing my IValueConverter called inside a UserControl. But as with many things, it's not quite that simple. The UserControl is in a DataGrid cell, in a column whose DataColumnTemplate is generated at runtime by XAML.
Here's my DataTemplate for the Column:
StringBuilder CellTemp = new StringBuilder();
CellTemp.Append("<DataTemplate ");
CellTemp.Append("xmlns:aa='clr-namespace:InvTech.AA.Silverlight.UI;assembly=AASilverlight' ");
CellTemp.Append("xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' ");
CellTemp.Append("xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' ");
CellTemp.Append(">");
CellTemp.AppendFormat("<aa:ProductAssetView DataContext='{{Binding Products[{0}]}}' />", index);
CellTemp.Append("</DataTemplate>");
return CellTemp.ToString();
So the cell's contents are getting bound to my UserControl. This works; I just can't get my IValueConverter called to format the contents of the UserControl the way I want.
The operative parts of the UserControl XAML:
(declare prefix)
xmlns:aaConv="clr-namespace:InvTech.AA.Silverlight.Core;assembly=AA.Core"
(bound controls inside Grid layout)
<TextBox x:Name="txtSAA" Grid.Row="0" Grid.Column="0" Text="{Binding SAA, Converter={StaticResource PercentConverter}, Mode=TwoWay}" Width="35" FontSize="9"/>
<TextBox x:Name="txtOVR" Grid.Row="0" Grid.Column="1" Text="{Binding Overlay, Converter={StaticResource PercentConverter}, Mode=TwoWay}" Width="35" FontSize="9" />
<TextBox x:Name="txtTAA" Grid.Row="0" Grid.Column="2" Text="{Binding TAA, Converter={StaticResource PercentConverter}, Mode=TwoWay}" Width="35" FontSize="9" />
<TextBlock x:Name="tbkCurrent" Grid.Row="0" Grid.Column="3" Text="TODO" Width="35" FontSize="9" />
<Grid.Resources>
<aaConv:PercentValueConverter x:Key="PercentConverter" />
</Grid.Resources>
Is there something obviously wrong here? Is the dynamic XAML a factor? I feel like this should be trivial compared to the dynamic XAML template...
Thanks
Finally figured this out. By moving the Resource declaration to <UserControl.Resources> and putting that tag before the content, my IValueConverters were exercised.