In my xamarin.forms app, I am using Cardview's link cubeview. The cubeview is similar to carousal with a property called auto sliding. We can set the duration and it will auto slide after that time. In this cube view I am using two different templates. One for showing image and another one for video. I can show these two different views by using data template selector. Everything works fine.
Now, since the auto slide property is hard coded, even if the video or image fully loaded(I am fetching them from URL.) the view will get slided. How can bind or reference the auto duration value to the loading property of Image and video?
For Image view I am using FFImageloading
For Video view I am using Rox.Xamarin.Video
In summarise, if the image is loaded, I want to make the auto slide value to 7 seconds and if it is video I want the autoslide duration needs to be the duration of video. How can I do that? Any help is apprecited.
My cube view
<cards:CubeView
x:Name="StatusCarousal"
IsClippedToBounds="True"
SlideShowDuration="7000" // This value needs to be bind according to template
IsVerticalSwipeEnabled="True"
ItemSwiped="StatusCarousal_ItemSwiped"
PropertyChanged="StatusCarousal_PropertyChanged"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource personDataTemplateSelector}"
HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" >
</cards:CubeView>
Data Templates
<ContentPage.Resources>
<ResourceDictionary>
<DataTemplate x:Key="Page1Template" >
<ffimageloading:CachedImage Grid.Column="0" Grid.ColumnSpan="3"
x:Name="StoryImage"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
Source="{Binding Image}"
Aspect="AspectFill"
Success="CachedImage_Success"
FileWriteFinished="CachedImage_FileWriteFinished"
DownsampleToViewSize="true"
DownloadStarted="CachedImage_DownloadStarted"
>
</ffimageloading:CachedImage>
</DataTemplate>
<DataTemplate x:Key="Page2Template">
<rox:VideoView Source="{Binding VideoURl}" AutoPlay="True" FullScreen="True"
Grid.Column="0" Grid.ColumnSpan="3"
HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
>
</rox:VideoView>
</Grid>
</DataTemplate>
<local:PersonDataTemplateSelector x:Key="personDataTemplateSelector"
Page1="{StaticResource Page1Template}"
Page2="{StaticResource Page2Template}" />
</ResourceDictionary>
</ContentPage.Resources>
Model class
public partial class InnerStoreisData : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public string UserName { get; set; }
public ImageSource profileImage { get; set; }
public string VideoURl { get; set; }
public string Image { get; set; }
public int Type { get; set; }
}
Template selector
public class PersonDataTemplateSelector : DataTemplateSelector
{
public DataTemplate Page1 { get; set; }
public DataTemplate Page2 { get; set; }
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
switch (((InnerStoreisData)item).Type)
{
case 0:
return Page1;
case 1:
return Page2;
default:
return Page1;
}
}
}
Related
I have a carouselview in my Xam.Forms project. I have also created 3 ContentViews (one for each DataTemplate). My template selector class looks like this
public class DashboardTemplateSelector : DataTemplateSelector
{
public DataTemplate QuickMessageTemplate { get; set; }
public DataTemplate DataViewTemplate { get; set; }
public DataTemplate LastUsedTemplate { get; set; }
public DashboardTemplateSelector()
{
QuickMessageTemplate = new DataTemplate(typeof(QuickMessage));
DataViewTemplate = new DataTemplate(typeof(DataView));
LastUsedTemplate = new DataTemplate(typeof(LastusedView));
}
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
var cv = (Frame)item;
DataTemplate rv = null;
switch(cv.ClassId)
{
case "data":
rv = DataViewTemplate;
break;
case "quick":
rv= QuickMessageTemplate;
break;
case "last":
rv = LastUsedTemplate;
break;
}
return rv;
}
Nothing out of the ordinary and I have the ClassId on each frame within the ContentView set to match the name in the switch.
When I build the app and run it, it looks fine but there is nothing in the CarouselView and a break point set in the OnSelectTemplate method (the first line) is never hit.
My XAML for the carouselview is this
<ContentPage.Resources>
<ResourceDictionary>
<local:DashboardTemplateSelector x:Key="templateSelector" />
</ResourceDictionary>
</ContentPage.Resources>
<CarouselView Grid.Row="2" PeekAreaInsets="12" Margin="8" ItemTemplate="{StaticResource templateSelector}" HeightRequest="200" BackgroundColor="BlueViolet" />
The view shows (can see the background colour) but nothing in the view itself.
I've only checked this on a physical android device and not on iOS, but I'm guessing the same retult. My guess is that I can't cast to a Frame for the object, but I'm not sure.
To populate data, you have to set an ItemsSource via DataBinding or Code-Behind.
Then your DataTemplateSelector will be hit with each item of the ItemsSource as object item. Please see the documentation here: https://learn.microsoft.com/fr-fr/xamarin/xamarin-forms/user-interface/carouselview/layout
<CarouselView Grid.Row="2" PeekAreaInsets="12" Margin="8" ItemsSource="{Binding ViewsViewModels} ItemTemplate="{StaticResource templateSelector}" HeightRequest="200" BackgroundColor="BlueViolet" />
The item in OnSelectTemplate is itemsource data. Change the item to container and use CarouselView instead of Frame. Do not forget to set the ClassId of your CarouselView.
The whold project for your reference.
Xaml:
<ContentPage.Resources>
<ResourceDictionary>
<local:DashboardTemplateSelector x:Key="templateSelector" />
</ResourceDictionary>
</ContentPage.Resources>
<CarouselView
Grid.Row="2"
Margin="8"
ClassId="data"
HeightRequest="200"
ItemTemplate="{StaticResource templateSelector}"
ItemsSource="{Binding infos}"
PeekAreaInsets="12" />
Code behind:
public partial class MainPage : ContentPage
{
public ObservableCollection<Info> infos { get; set; }
public MainPage()
{
InitializeComponent();
infos = new ObservableCollection<Info>()
{
new Info{ DataViewText="DataViewText1", LastusedViewText="LastusedViewText1", QuickMessageText="QuickMessageText1"},
new Info{ DataViewText="DataViewText2", LastusedViewText="LastusedViewText2", QuickMessageText="QuickMessageText2"},
new Info{ DataViewText="DataViewText3", LastusedViewText="LastusedViewText3", QuickMessageText="QuickMessageText3"},
new Info{ DataViewText="DataViewText4", LastusedViewText="LastusedViewText4", QuickMessageText="QuickMessageText4"},
};
this.BindingContext = this;
}
}
public class Info
{
public string QuickMessageText { get; set; }
public string DataViewText { get; set; }
public string LastusedViewText { get; set; }
}
QuickMessage, DataView and LastusedView is a contentview with label which binding a text.
QuickMessage:
<Label Text="{Binding QuickMessageText}" />
DataView:
<Label Text="{Binding DataViewText}" />
LastusedView:
<Label Text="{Binding LastusedViewText}" />
DashboardTemplateSelector:
public class DashboardTemplateSelector : DataTemplateSelector
{
public DataTemplate QuickMessageTemplate { get; set; }
public DataTemplate DataViewTemplate { get; set; }
public DataTemplate LastUsedTemplate { get; set; }
public DashboardTemplateSelector()
{
QuickMessageTemplate = new DataTemplate(typeof(QuickMessage));
DataViewTemplate = new DataTemplate(typeof(DataView));
LastUsedTemplate = new DataTemplate(typeof(LastusedView));
}
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
var cv = (CarouselView)container;
DataTemplate rv = null;
switch (cv.ClassId)
{
case "data":
rv = DataViewTemplate;
break;
case "quick":
rv = QuickMessageTemplate;
break;
case "last":
rv = LastUsedTemplate;
break;
}
return rv;
}
}
I have a bindable StackLayout bound to a List in the ViewModel. When a button is pressed I am adding an element to the list and then invoking PropertyChanged with the name of the list.
I don't understand why the UI does not get updated in this case. I know that I should use an ObservableCollection, and I know how to do it, but I am curious about why the UI does not change if I am invoking PropertyChanged myself.
This is the ViewModel and Model class:
public class Element
{
public string Value { get; set; }
}
public class MainViewModel : INotifyPropertyChanged
{
public List<Element> Elements { get; set; }
public ICommand AddElementCommand { get; private set; }
public event PropertyChangedEventHandler PropertyChanged;
public MainViewModel()
{
AddElementCommand = new Command(AddElement);
Elements = new List<Element>();
Elements.Add(new Element { Value = "test1" });
}
void AddElement()
{
Elements.Add(new Element { Value = "testAgain" });
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Elements"));
}
}
And this is the view:
<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"
mc:Ignorable="d"
x:Class="TestList.MainPage"
x:Name="page">
<StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="Elements"
FontSize="Large"
HorizontalOptions="FillAndExpand"
VerticalOptions="Center"/>
<Button Command="{Binding AddElementCommand}"
Text="+" FontSize="Title"
VerticalOptions="Center"/>
</StackLayout>
<StackLayout BindableLayout.ItemsSource="{Binding Elements}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Entry Text="{Binding Value, Mode=TwoWay}" HorizontalOptions="FillAndExpand"/>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</StackLayout>
</ContentPage>
If we check the source code of ObservableCollection , we will see that it had implemented INotifyCollectionChanged and INotifyPropertyChanged in default while List didn't .
public class ObservableCollection<T> : Collection<T>, INotifyCollectionChanged, INotifyPropertyChanged
public class List<T> : ICollection<T>, IEnumerable<T>, IEnumerable, IList<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection, IList
If a list will contain the same items for their whole lifetime, but the individual objects within that list will change, then it's enough for just the objects to raise change notifications (typically through INotifyPropertyChanged) and List is sufficient. But if the list will contain different objects from time to time, or if the order changes, then you should use ObservableCollection.
That is why we always suggest users to choose ObservableCollection instead of List .
I've a Picker inside a ListView with databinding:
<ListView
ItemsSource="{Binding CursorPrintParametersList}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:CursorPrintParameters">
<ViewCell>
<Grid>
<Picker
ItemsSource="{Binding LabelTypes}"
SelectedItem="{Binding SelectedLabelType}"
ItemDisplayBinding="{Binding Description}"
/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
where CursorPrintParametersList is
ObservableCollection<CursorPrintParameters> CursorPrintParametersList
and
public class CursorPrintParameters : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public List<BaseDto> LabelTypes { get; set; }
private BaseDto selectedLabelType;
public BaseDto SelectedLabelType
{
get => selectedLabelType;
set
{
if (value == null) return;
selectedLabelType = value;
RaisePropertyChanged(nameof(SelectedLabelType));
}
}
}
public class BaseDto
{
public string Code{ get; set; }
public string Description{ get; set; }
}
I'm unable to set the ItemDisplayBinding="{Binding Description}" for the Picker, getting a
Property 'Description' not found on 'CursorPrintParameters'
If i put the picker outside the ListView, everything works fine.
Any help? Thanks
i reproduce your issue in Xamarin.Forms version 3.6.0.34457
and the solution is delete below codes in DataTemplate :
x:DataType="models:CursorPrintParameters"
It is better to upgrade to the latest version if possible, as there may be some API changes
ItemDisplayBinding is the name of the property to use for the Display, not an actual binding expression
ItemDisplayBinding="Description"
Hi I have an ItemSource which binds a list of contact
<ListView x:Name="contactsListView" ItemsSource="{Binding contacts}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Image Source="{Binding Should be to my View Model instead of Contacts}"></Image>
<Label Text="{Binding FullName}"></Label>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
the Full Name binding works fine. My problem is the Image Source is not included in the Contact model so I need to retrieve that from my view Model How can I do it?
VIEW MODEL
public class ContactsViewModel : INotifyPropertyChanged
{
public ObservableCollection<Contact> contacts { get; set; }
private string _contactImage;
public string ContactImage
{
get => _contactImage; set
{
_contactImage = value;
OnPropertyChanged("ContactImage");
}
}
public ContactsViewModel(List<Contact> _contacts)
{
contacts = new ObservableCollection<Contact>(_contacts);
ContactImage = "arrow.png";
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
CODE BEHIND VIEW
public partial class ContactListPage : TabbedPage
{
public ContactsViewModel vm;
public ContactListPage (List<Contact> _contacts)
{
vm = new ContactsViewModel(_contacts);
BindingContext = vm;
InitializeComponent();
}
}
This is because the scope within a ListView item is different than a level higher. To overcome this, create a reference to a parent control. I see you already named your ListView, so we can use that.
Do it like this: <Image Source="{Binding Path=BindingContext.ContactImage, Source={x:Reference contactsListView}}"></Image>
I have an ObservableCollection which is viewed in a custom listbox. I need the listbox to update the view according to changes applied, like inserting new feeds or removing feeds from the ObservableCollection
parts of code are available below
public class lbl
{
public ObservableCollection<feed> ModifiedItems
= new ObservableCollection<feed>();
public lbl()
{
InitializeComponent();
listBox1.ItemsSource = ModifiedItems ;
}
public void update(object sender, EventArgs e)
{
var x = ModifiedItems.Last();
listBox1.Items.Add(x);
}
}
public class feed
{
public int ID { get; set; }
public int source_id { get; set; }
public string title { get; set; }
public string source_icon { get; set; }
public string url { get; set; }
public string pudate { get; set; }
}
XAML
<ListBox x:Name="listBox1" >
<ListBox.ItemTemplate >
<DataTemplate >
<StackPanel Width="400" Margin="20" >
<Button x:Name="pic" Tag="{Binding Id}">
<Button.Template>
<ControlTemplate>
<TextBlock Text="{Binding title}" TextWrapping="Wrap" FontFamily="Arial" FontSize="28" Tag="{Binding Id}"/>
</ControlTemplate>
</Button.Template>
</Button>
<TextBlock Text="{Binding pudate}" TextWrapping="Wrap" FontSize="24"/>
<Image Source="{Binding source_icon}" Width="100" Height="60"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Note: This is not part of the code.
It gives me the error when trying to add an Item "Operation not supported on read-only collection."
I tried the solution posted here Implementing CollectionChanged and still I get the same error.
Any help please, Thanks in advance
The problem is with your update method:
public void update(object sender, EventArgs e)
{
var x = ModifiedItems.Last();
listBox1.Items.Add(x);
}
The ItemsSource of your ListBox is set to the ModifiedItems which is an ObservableCollection. Therefore if you add or remove items from this collection, the ListBox UI will update automatically. For example, to add a new items to your view simply do the following:
ModifiedItems.Add(new feed());
This is the whole point of an ObservableCollection, the view can observe it!
If, rather than adding / removing items, you are updating existing items, you will need to make feed implement INotifyPropertyChanged.
Since you're setting the ItemsSource of the ListBox, you're binding the ModifiedItems collection to it.
This means you have to modify ModifiedItems, and not the ListBox to add/remove items, which then will update accordingly.
public void update(object sender, EventArgs e)
{
var x = ModifiedItems.Last();
ModifiedItems.Items.Add(x);
}
Why you want to duplicate the last item is beyond me. But that's the change you need to do.