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"
Related
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;
}
}
}
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 .
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>
Well, I have a listbox and I bind it with an itemsource:
<ScrollViewer>
<ListBox Name="servicesGroupList" Height="574" Width="408" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Padding="5,0,5,0" Text="{Binding serviceName}" FontSize="20" />
<TextBlock Padding="5,0,5,0" Text="{Binding serviceDesc}" FontSize="10" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</ScrollViewer>
And I have code behind:
public ObservableCollection<ServiceInfoDTO> dynamicServicesList = new ObservableCollection<ServiceInfoDTO>();
public MainPage()
{
this.DataContext = this.dynamicServicesList;
InitializeComponent();
}
And also my class is:
public class ServiceInfoDTO
{
public string serviceName;
public string serviceDesc;
}
The class is declared in ServiceInfoDTO.cs
When I start the application, I see nothing, I guess the binding is wrong... I tested SO MUCH different methods, and I came here, please help me :)
MANY THANKS TO Alaa Masoud!
I guess, this post is a simpliest method to implement the binding and will be very helpful for newbies, I didn't find such post in Internet.
Make a get accessor for your properties..
public class ServiceInfoDTO {
public string serviceName { get; set; }
public string serviceDesc { get; set; }
}
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.