how to specify a binding value in a collectionview? - xamarin

I created a CollectionView inside which has a custom View.
I want to specify the property of the customview dynamically so I add a Binding Value to the customView but obviously it need a Property in the Binding Data.
But I want to specify it in the cs file.
<CollectionView ItemsSource="{Binding Items}">
<CollectionView.ItemTemplate>
<DataTemplate>
<local:MyView WidthRequest="{Binding Width}"/>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
obviously I need add a Width property in the Items.But I do not want it.
Is there anyway to do it?

Different from Button and Image . ContentView will not have a default size if you don't set the child elements .
So if you want to set the size in runtime , use data binding is the best way . If you don't want to define the property in model and set it multi times , you can set the binding path of the ContentView . In this way you should make sure that each row has the same height .
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:XXX"
mc:Ignorable="d"
x:Name="contentPage" // set the name
x:Class="XXX.MainPage">
<local:MyView HeightRequest="{Binding Source={x:Reference contentPage},Path=BindingContext.Height}" />
in ViewModel
public double Height {get; private set;}

Try to use <Grid> with one column specifying it's width as Auto
instead of binding dynamic Width:
<CollectionView ItemsSource="{Binding Items}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<local:MyView Grid.Column="0"/>
<Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>

I have a not so better solution.
I created a style which specify the HeightReqeust and WidthRequest of the customView.
and I specify the value in the cs file.
till now I have not found a better solution.

Related

Xamarin Forms: Call Command of ViewModel from DataTemplate

I am stuck on a binding problem here.
I created a bindable layout which is inside a control template:
<ContentView x:Name="SettingsMenu" ControlTemplate="{StaticResource HeaderTemplate}" AbsoluteLayout.LayoutBounds="0.5,0.5,1,1"
AbsoluteLayout.LayoutFlags="All">
<ScrollView Orientation="Vertical" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<StackLayout x:Name="SettingsStack" BindableLayout.ItemsSource="{Binding Settings}" BindableLayout.ItemTemplateSelector="{StaticResource SettingsSelectorTemplate}" Orientation="Vertical" Spacing="0" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" />
</ScrollView>
</ContentView>
What I want to do is call a command inside the view model. The call is inside a item template selector as a resource dictionary inside App.xml
<ResourceDictionary>
<DataTemplate x:Key="PlaceholderSettingsTemplate">
### SOME STUFF
</DataTemplate>
<DataTemplate x:Key="HeaderSettingsTemplate">
### SOME STUFF
<Grid ...>
<Grid.GestureRecognizers>
<TapGestureRecognizer Tapped="ButtonClick" Command="{Binding BindingContext.SettingsTap, Source={x:Reference SettingsPage}}" CommandParameter="{Binding}" /> ########## <--------- WHAT TO USE FOR SOURCE?
</Grid.GestureRecognizers>
</Grid>
</DataTemplate>
<data:SettingsSelector x:Key="SettingsSelectorTemplate" Placeholder="{StaticResource PlaceholderSettingsTemplate}" Heading="{StaticResource HeaderSettingsTemplate}" Content="{StaticResource ContentSettingsTemplate}" />
</ResourceDictionary>
Before I moved it inside a resource dictionary in the App.xml file, I simply used the x:Name of the Parent Contentview. But: I can't reference it by name anymore because I moved it into a resource dictionary inside App.xml.
Now, the answer may be trivial but I just can't find a solution.
Any help is appreciated.
Kind regards
You can find the SettingsStack StackLayout using the data template's Grid which wraps all the content. Since the SettingsStack has the same binding context as the parent content view, you can access the binding context in App.cs like:
<DataTemplate x:Key="HeaderSettingsTemplate">
<!--### SOME STUFF-->
<Grid x:Name="ParentGrid">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Parent.BindingContext.SettingsTap, Source={x:Reference ParentGrid}}" CommandParameter="{Binding}" />
</Grid.GestureRecognizers>
</Grid>
</DataTemplate>
The ParentGrid's parent is SettingsStack on your current page.
From the item template (even if it's in a different file), you can access the view model of it's ancestor, using the following:
Command="{Binding Source={RelativeSource AncestorType={x:Type SettingsViewModel}}, Path=SettingsTap}" CommandParameter="{Binding}"
In this case I just assumed that the name of the ancestor's view model is SettingsViewModel, but you get the point.
Here is an interesting article regarding relative binding, that describes other scenarios as well: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/data-binding/relative-bindings#bind-to-an-ancestor
Cheers.
In another way we can use ContentPage as AncestorType directly. Then bind command with BindingContext.
{Binding BindingContext.SomeCommand, Source={RelativeSource AncestorType={x:Type ContentPage}}}
Complete example
<DragGestureRecognizer
DragStartingCommand="{Binding BindingContext.SomeCommand, Source={RelativeSource AncestorType={x:Type ContentPage}}}"
DragStartingCommandParameter="{Binding}"/>
Also this way works with separate resource files (eg. MySeparateResource.xml -> MyPageView.xaml).

How to bind a nested Layout with external ContentView in Xamarin.Forms?

I have a Page with a StackLayout using BindableLayout.ItemsSource, inside each item I have a ListView, for each item in this nested ListView I need to do a Binding to a property on the Page's ViewModel. I'm trying to use the Source+Path approach but the app crashes as soon as I open the page that contains this structure.
MainPage.xaml:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
x:Class="BindableLayoutReferenceBug.ListViewPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:BindableLayoutReferenceBug">
<StackLayout BindableLayout.ItemsSource="{Binding Items}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<local:MessageListViewTemplate />
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</ContentPage>
MessageListViewTemplate.xaml:
<?xml version="1.0" encoding="UTF-8" ?>
<ContentView
x:Class="BindableLayoutReferenceBug.MessageListViewTemplate"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Name="listView">
<ListView ItemsSource="{Binding Options}">
<ListView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding .}" />
<Button
BackgroundColor="Blue"
Command="{Binding Source={x:Reference Name=listView}, Path=Parent.BindingContext.ShowMessageCommand}"
CommandParameter="{Binding .}"
Text="Show Message" />
</StackLayout>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentView>
The exception shows that there is a problem finding the reference to the x:Name I used: Can not find the object referenced by listView
This only happens when I have a nested structure as I mentioned (StackLayout with BindableLayout > ListView). I'm not sure if this is not a supported scenario or if it is a bug.
I tried using different Paths for the binding, but since this is a problem parsing the XAML not finding the x:Name referenced, I don't think it even starts evaluating my Path.
Am I doing something wrong or is this a bug in Xamarin.Forms?
Xamarin.Forms version used: 3.6.0.293080
Repro sample: https://github.com/akamud/BindableLayoutReferenceBug
Maybe, x:Name wasn't recognized at runtime even though you have set it to your content view in XAML. You could raise an issue on GitHub.
Here I tried this binding using code behind and it works fine. You could use it as an alternative:
public MessageListViewTemplate()
{
InitializeComponent();
listView.ItemTemplate = new DataTemplate(() =>
{
ViewCell viewCell = new ViewCell();
Label label = new Label();
label.SetBinding(Label.TextProperty, new Binding("."));
Button button = new Button() { Text = "Show Message", BackgroundColor = Color.Blue };
button.SetBinding(Button.CommandProperty, new Binding("Parent.BindingContext.ShowMessageCommand", source: ContentView));
button.SetBinding(Button.CommandParameterProperty, new Binding("."));
viewCell.View = new StackLayout
{
Children =
{
label,
button
}
};
return viewCell;
});
}
XAML:
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="BindableLayoutReferenceBug.MessageListViewTemplate"
x:Name="ContentView">
<StackLayout>
<ListView ItemsSource="{Binding Options}" x:Name="listView" HasUnevenRows="True">
</ListView>
</StackLayout>
</ContentView>
Here, I defined the parent content view as ContentView and named ListView listView.
Please notice that the list view's data template should be a view cell. So I used a view cell to wrap the content here.
It was a bug after all, it is now fixed and the above should work: https://github.com/xamarin/Xamarin.Forms/issues/6192

Custom ViewCell Contain Button Has Command And Binding To This Command

I fix customize some problems in some projects
one of them that I need to use custom ViewCell in Separated class and file
like that:
<?xml version="1.0" encoding="UTF-8"?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="HBRS.Controls.ViewCellTemplates.ArticleItemViewCell">
<Image>
<Image.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding BindingContext.clickCommand, Source={x:Reference Name=mArt}}"
CommandParameter="{Binding .}" />
</Image.GestureRecognizers>
</Image>
</ViewCell>
where mArt is the view that will command make some thing with it
after that I used this view cell in on of my xamarin pages like that:
<ListView.ItemTemplate>
<DataTemplate>
<Cell:ArticleItemViewCell />
</DataTemplate>
</ListView.ItemTemplate>
when I run the application on my device, it throw an exception say that cannot find object referenced for 'mArt'
so I need some way to pass Source={x:Reference Name=mArt} up with the same result or make the interact that command will make it
From what you wrote, I assume that you have a view using your ViewCell something like
<ContentView ...
x:Name="mArt">
<ListView ...>
<ListView.ItemTemplate>
<DataTemplate>
<templates:ArticleItemViewCell ... />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentView>
And now you are trying to reference that view mArt from your ViewCell. Unfortunately that is not how things work. mArt is not something like a global variable, but a member of your view class (if you are interested in the details, have a look at the .xaml.g.cs file that is created in your object folder).
ArticleItemViewCell however is a different class from which you can't simply access fields of some other class. ArticleItemViewCell does not know anything about mArt. While it might be possible to access the parent in some way, I'd advise you no to, because you tend to forget these details and some months later you'll look at your view and wonder where the interaction with the cell is implemented, until you realize, that the cell does some fishy things. It will only cost you time. Been there, done that. Believe me.
Rather create a bindable property of type Command in your viewcell, and bind to it from your containing view
In ArticleItemViewCell.xaml.cs
public static readonly BindableProperty TappedCommandProperty = BindableProperty.Create(nameof(TappedCommand), typeof(Command), typeof(ArticleItemViewCell));
public Command TappedCommand
{
get => (Command)GetValue(TappedCommandProperty);
set => SetValue(TappedCommandProperty, value);
}
And now you can bind them from your ArticleItemViewCell
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="HBRS.Controls.ViewCellTemplates.ArticleItemViewCell"
x:Name="Cell">
<Image>
<Image.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding TappedCommand, Source={x:Reference Cell}}"
CommandParameter="{Binding .}" />
</Image.GestureRecognizers>
</Image>
</ViewCell>
And from your view you can bind the clickCommand of your VM
<ContentView ...
x:Name="mArt">
<ListView ...>
<ListView.ItemTemplate>
<DataTemplate>
<templates:ArticleItemViewCell TappedCommand="{Binding Source={x:Reference mArt}, Path=BindingContext.clickCommand}" ... />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentView>
I did not try the exact code, but basically this oughta work.
Please note: Consuming the ItemTapped event (see the docs) with a event to command behavior (see here) is more expressive and spares you the additional command.

BindableProperty for Image Source in contentview

Im trying to bind DogImage source which in my Contentview. Im trying to build reusable view objects like frame buttons. I just need to give them Image and Text from outside of contentview. Im using Images from resources folder.
this is my contentView
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="PawsApp.Views.AboutMyDogViews.DogsBreedPicker"
BindingContext="{Binding .}">
<ContentView.Content>
<StackLayout x:Name="breedStack" Margin="30,2,30,2">
<Frame HeightRequest="64" CornerRadius="8" BorderColor="White" HasShadow="False" BackgroundColor="White"
VerticalOptions="FillAndExpand" Padding="18,0,18,0">
<Frame.Content>
<StackLayout Orientation="Vertical" VerticalOptions="CenterAndExpand">
<Grid ColumnSpacing="18">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"></ColumnDefinition>
<ColumnDefinition Width="9*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Image x:Name="DogImage" Source="{Binding ImageSourceOf}" Grid.Row="0" Grid.Column="0" ></Image>
<Label x:Name="breedSelector" Text="Breed" FontSize="Medium" TextColor="#5f5d70" Grid.Row="0" Grid.Column="1">
</Label>
</Grid>
</StackLayout>
</Frame.Content>
</Frame>
</StackLayout>
</ContentView.Content>
</ContentView>
And this is CS file
public partial class DogsBreedPicker : ContentView
{
public static readonly BindableProperty ImageSourceProperty =
BindableProperty.Create("ImageSourceOf", typeof(ImageSource), typeof(DogsBreedPicker));
public ImageSource ImageSourceOf
{
get { return GetValue(ImageSourceProperty) as ImageSource; }
set { SetValue(ImageSourceProperty, value);}
}
public DogsBreedPicker()
{
InitializeComponent ();
BindingContext = ImageSourceOf;
}
}
And this is how i want to use it.
<views:DogsBreedPicker ImageSourceOf="dog" TextOf="Breed Selector" x:Name="DogBreed" ></views:DogsBreedPicker>
You are setting your BindingContext to ImageSourceOf in your constructor. Anyway, the properties are not set at this moment, hence ImageSourceOf is still null. However, you do not need to use BindingContext in this case anyway, since you can directly bind to the ImageSourceOf property:
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="PawsApp.Views.AboutMyDogViews.DogsBreedPicker"
x:Name="View">
<!-- Elided all the other stuff -->
<Image x:Name="DogImage" Source="{Binding ImageSourceOf, Source={x:Reference View}}" Grid.Row="0" Grid.Column="0" />
</ContentView>
remove the assignment of BindingContext from your constructor.
Why does this work?
The default source of all bindings is the BindingContext (of the view, which is propagated to all child views). Anyway, you are by no means restricted to the BindingContext as the source of a binding, but can set the Source of the binding to another object. In this case we are referencing the view (which we have given the name View with x:Name="View") by its name and use it as the source of the binding of the Source of the Image. Since the Path of the binding is ImageSourceOf, the Image.Source will be bound to View.ImageSourceOf.
Content.BindingContext = this;
Solved my issue. But is there any way to make it better? Add comment please.

Custom Toggleswitch with binding to object

I am trying to create a toggleswitch which will have multiple rows in the datatemplate bound to different properties of a single object. These toggleswitches will be inside a listbox.
My xaml code below shows the current toggleswitch template. With the code below, only the header is binding properly. I need the other two rows (in the ContentTemplate) and the toggleswitch itself to bind to a boolean property of the object.
<DataTemplate>
<toolkit:ToggleSwitch Header="{Binding Property1}" Width="450">
<toolkit:ToggleSwitch.HeaderTemplate>
<DataTemplate>
<ContentControl FontWeight="Black" FontSize="40" Foreground="{StaticResource PhoneForegroundBrush}" Content="{Binding}" VerticalAlignment="Top" />
</DataTemplate>
</toolkit:ToggleSwitch.HeaderTemplate>
<toolkit:ToggleSwitch.ContentTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Property2}" FontSize="32" FontWeight="Light" Foreground="{StaticResource PhoneAccentBrush}" />
<TextBlock Text="{Binding Property3}" FontSize="{StaticResource PhoneFontSizeSmall}" Foreground="{StaticResource PhoneSubtleBrush}" />
</StackPanel>
</DataTemplate>
</toolkit:ToggleSwitch.ContentTemplate>
</toolkit:ToggleSwitch>
</DataTemplate>
Any advice here on how to achieve the results I need?
Thanks in advance!
Here is what I am trying to achieve
Property1
Property2:On/Off [===] (this is the toggle switch)
Property3
Properties 1,2, and 3 all will have custom formatting as well. Please keep in mind these will be in a listbox, so they will be binding to a collection.
You'll need to modify the source of the ToggleSwitch in the converter.
Add extra text/string properties to have something to bind Property2 and Property3 to. (These will also need to be separate items to be templated differently- like in the alarms app.)
Then look at changing the binding for the ContentProperty or extend the OffOnConverter to include the additional new proprties.
Probably, what you need is binding to an element, check this post:
Silverlight 3 Element binding in a datatemplate
Give the element with data template x:Name property and then use element binding from within data template.
Hope this helps,
Robert

Resources