Set ItemsSource of UWP ComboBox to ViewModel Property - xaml

I am trying to set the ItemsSource of a UWP ComboBox to a property of the ViewModel, but I get an error:
Error: BindingExpression path error: 'componentsLookup' property not found on 'Orders.Component'
The relevant bit of XAML looks like this:
<Page.DataContext>
<local:OrderPageViewModel x:Name="OrderPageViewModel" />
</Page.DataContext>
<ListView
Name="ComponentsList"
ItemsSource="{Binding Components}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ComboBox
ItemsSource="{Binding componentsLookup,Mode=TwoWay}"
DisplayMemberPath="ComponentCode"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The relevant bit of the ViewModel looks like this:
public class OrderPageViewModel
{
public ObservableCollection<Product> Products { get; set; } = new ObservableCollection<Product>();
public List<Component> componentsLookup = new List<Component>();
Edit 1: The models look like this
public class Product
{
public string ProductCode { get; set; }
public string ProductDescription { get; set; }
public List<Component> Components { get; set; }
public override string ToString()
{
return this.ProductCode;
}
}
public class Component
{
public Guid ComponentId { get; set; }
public Product Product { get; set; }
public string ComponentCode { get; set; }
public string ComponentDescription { get; set; }
public string ComponentColor { get; set; }
public decimal ComponentHeight { get; set; }
public decimal ComponentWidth { get; set; }
public override string ToString()
{
return this.ComponentCode;
}
}
How do I set the ItemsSource to componentsLookup

Nested binding is what you actually want to do. Since the ComboBox is nested inside the ListView, ItemsSource of ComboBox need to be a sub collection of ListView. componentsLookup should be a property of class Orders.Component in your code snippet. You can use a nested source structure like follows for binding:
public class OrderPageViewModel
{
public ObservableCollection<Product> Products { get; set; } = new ObservableCollection<Product>()
{
new Product
{
productname="productname",
componentsLookup=new List<Component>
{
new Component {componentname="test1" },
new Component {componentname="test2" }
}
},
new Product
{
componentsLookup=new List<Component>
{
new Component {componentname="test1" },
new Component {componentname="test2" }
}
}
};
}
public class Component
{
public string componentname { get; set; }
}
public class Product
{
public string productname { get; set; }
public List<Component> componentsLookup { get; set; }
}
XAML Code
<Page.DataContext>
<local:OrderPageViewModel x:Name="OrderPageViewModel" />
</Page.DataContext>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" >
<ListView Name="ComponentsList" ItemsSource="{Binding Products}" >
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding productname}"></TextBlock>
<ComboBox DisplayMemberPath="ComponentCode" ItemsSource="{Binding componentsLookup, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
More details please reference the official data binding document.

Related

Binding multiple Models in View page Xamarin

I'm trying to bind ListView using ItemsSource="{Binding modelname}" it is working fine with one model(Ads), when I added two Models (Ads) and (AdsImg) it's return nothing :
public class Ads
{
public string Titel { get; set; }
public string Description { get; set; }
public List<AdsImg> AdsImg { get; set; }
}
public class AdsImg
{
public int Id { get; set; }
public string ImgPath { get; set; }
public int AdId { get; set; }
}
I combine them in ViewModels (AdsViewModel)
public class AdsViewModel
{
public AdsImg AdsImg { get; set; }
public Ads Ads { get; set; }
}
Xaml page :
<ListView x:Name="AdsListView" ItemsSource="{Binding AdsViewModel}" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Image Source="{Binding AdsImg.ImgPath}" />
<Label Text="{Binding Ads.Titel}" TextColor="Black"></Label>
<Label Text="{Binding Ads.Description}" TextColor="Black"></Label>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Xaml.cs
public AdsPage()
{
InitializeComponent();
GetAds();
}
private async void GetAds()
{
HttpClient client = new HttpClient();
var response = await client.GetStringAsync("https://example.net/api/Ad");
var Ads = JsonConvert.DeserializeObject<List<AdsViewModel>>(response);
AdsListView.ItemsSource = Ads;
}
Update I replaced this code:
AdsImg img = new AdsImg() {ImgPath = "imagePath"};
Ads ads = new Ads() { Titel = "title",Description = "des" };
with this:
HttpClient client = new HttpClient();
var response = await client.GetStringAsync("https://example.com/api/Ad");
var Ads = JsonConvert.DeserializeObject<List<AdsViewModel>>(response);
but it is not working
I would give you some suggestions:
Don't name the property's name the same as the class name, for example: public AdsImg AdsImg, it looks confusing when you use AdsImg.
If you want to bind the ItemSource in xaml, you should bind to a List<AdsViewModel> instead of a single ViewModel.
I wrote a example and hope you can get some idea from it:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
AdsViewModel vm = new AdsViewModel();
vm.getData();
BindingContext = vm;
}
}
public class AdsViewModel : INotifyPropertyChanged
{
private List<AdsViewModel> _modelDatas;
public List<AdsViewModel> modelDatas
{
get { return _modelDatas; }
set
{
_modelDatas = value;
OnPropertyChanged("modelDatas");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(propertyName));
}
public AdsImg AdsImgModel { get; set; }
public Ads AdsModel { get; set; }
public AdsViewModel()
{
}
public void getData() {
AdsImg img = new AdsImg() {ImgPath = "imagePath"};
Ads ads = new Ads() { Titel = "title",Description = "des" };
modelDatas = new List<AdsViewModel>();
modelDatas.Add( new AdsViewModel() { AdsImgModel = img, AdsModel = ads });
modelDatas.Add(new AdsViewModel() { AdsImgModel = img, AdsModel = ads });
modelDatas.Add(new AdsViewModel() { AdsImgModel = img, AdsModel = ads });
}
}
public class Ads
{
public string Titel { get; set; }
public string Description { get; set; }
public List<AdsImg> AdsImg { get; set; }
}
public class AdsImg
{
public int Id { get; set; }
public string ImgPath { get; set; }
public int AdId { get; set; }
}
And in Xaml:
<ListView x:Name="AdsListView" ItemsSource="{Binding modelDatas}" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Image Source="{Binding AdsImg.ImgPath}" />
<Label Text="{Binding AdsModel.Titel}" TextColor="Black"></Label>
<Label Text="{Binding AdsModel.Description}" TextColor="Black"></Label>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Sample project has been uploaded here
and please feel free to ask me any question!

xamarin forms Update Item in CollectionView

iam trying to create xmarin forms application witch use a collection view
that contain two item ( label = binding name & switch = binding switched )
but i have a problem with the switch its not update without Scrolling the collection
i use Model-View-ViewModel to bind my data and do operation
that`s my xaml :
<CollectionView x:Name="GroupsCV"
ItemsSource="{Binding Groups}"
SelectionMode="Multiple"
EmptyView="No Data"
SelectionChangedCommand="{Binding SelectedCommand}"
SelectionChangedCommandParameter="{Binding Source={x:Reference GroupsCV}}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="15">
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="0.1*"/>
</Grid.ColumnDefinitions>
<Switch Style="{StaticResource SwitchStyle}" IsEnabled="False" IsToggled="{Binding Switched}" Grid.Column="0"/>
<Label Text="{Binding Name}" Grid.Column="1" Style="{StaticResource LabelStyle}"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
and this my code from View Model:
public GroupsViewModel()
{
datarepo.conn.CreateTable<Group>();
_groups = datarepo.conn.Table<Group>().ToList();
//GroupsCount = Groups.Count();
}
private IEnumerable<Group> _groups;
public IEnumerable<Group> Groups
{
get => _groups;
set
{
if (_groups != value)
{
_groups = value;
OnPropertyChanged(nameof(Groups));
}
}
}
public ICommand SelectedCommand
{
get
{
return new Command<CollectionView>((s) =>
{
var x = s.SelectedItems.Cast<Group>();
foreach (var e in x)
{
foreach (var z in Groups)
{
if (z.Link == x..Link)
{
if (z.Switched == false)
{
z.Switched = true;
}
else
{
z.Switched = false;
}
break;
}
}
}
OnPropertyChanged(nameof(Groups));
});
}
}
All Thing Work fine only the ui doesn`t update !
Update With Group Code
[Table("Group")]
public class Group
{
[PrimaryKey, AutoIncrement()]
public int? Id { get; set; }
[Unique]
public string Link { get; set; }
public string Name { get; set; }
public string MemberCount { get; set; } = null;
public bool Switched { get; set; }
}
Can you change your Group class like this?
[Table("Group")]
public class Group: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
[PrimaryKey, AutoIncrement()]
public int? Id { get; set; }
[Unique]
public string Link { get; set; }
public string Name { get; set; }
public string MemberCount { get; set; } = null;
public bool Switched { get; set; }
}

Binding a view model to a ComboBox

I am making a UWP app and I have bound an ObservableCollection<EduRole> to a ComboBox. I can see the items populated in the ComboBox. But I also need to set it's SelectedItem property which I cant get to work.
XAML:
<ComboBox Name="EducationalLevelComboBoxUpdate"
ItemsSource="{x:Bind EduRoleList, Mode=OneWay}"
<!--IS THIS OKAY?-->
SelectedItem="{x:Bind Path=User.EduRole.Read, Mode=OneWay}"
PlaceholderText="Select educational role">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="viewModels:EduRoleViewModel">
<TextBlock Text="{x:Bind Read}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Source:
private ObservableCollection<EduRoleViewModel> EduRoleList { get; set; } = new ObservableCollection<EduRoleViewModel>();
ViewModel:
public class EduRoleViewModel
{
public string Key { get; set; }
public string Read { get; set; }
}
User is object of a class called UserViewModel that has EduRoleViewModel type property here's that class:
class UserViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private EduRoleViewModel _eduRole;
public EduRoleViewModel EduRole
{
get { return _eduRole; }
set
{
_eduRole = value;
this.OnPropertyChanged();
}
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
I want the Read property of the EduRole property of UserViewModel to be the SelectedItem of the ComboBox

UWP binding to a method inside a ListView

How do you call a custom method in a bind that already has a x:DataType set to a class? The error message shows: The property 'TotalMembers' was not found in type 'Family'. When adding the method from the page load to the Family class, the error remains.
// model
public class Family
{
public int ID { get; set; }
public string Surname { get; set; }
public int TotalMembers() { return "some code..." }
}
public class Person
{
public int ID { get; set; }
public int FamilyID { get; set; }
public string FirstName { get; set; }
}
// page load
public ObservableCollection<Family> ocFamily { get; set; }
ocFamily = new ObservableCollection<Family>();
// xaml
<ListView ItemsSource="{x:Bind ocFamily}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="data:Family>
<StackPanel>
<TextBlock Text="{x:Bind Surname}" />
<TextBlock Text="{x:Bind TotalMembers()}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The second textblock should show the total number of members of the family.
To use functions with {x:Bind}, your app's minimum target SDK version
must be 14393 or later. You can't use functions when your app targets
earlier versions of Windows 10.
You will need to set the Min Version to at least 14393 for x:Bind to function to work.
You can read more from here.
In your case, you should be able to create a TotalMembers property instead of a method.

Xamarin ViewCell containg another ListView?

I'm having trouble getting my model to show up when trying to databind it in XAML setting Xamarin's BindingContext...here's my simplified model code:
public class LeagueRootObject
{
public League League { get; set; }
public ObservableCollection<Match> Matches { get; set; }
public object Error { get; set; }
}
public class League
{
public int Id { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public DateTime PlayingDate { get; set; }
}
public class Match
{
public MatchDetail MatchDetail { get; set; }
public ObservableCollection<Player> Players { get; set; }
}
public class MatchDetail
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
}
public class Player
{
public PlayerDetail PlayerDetail { get; set; }
public object Rating { get; set; }
public int MatchPlayerId { get; set; }
}
public class PlayerDetail
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
and here's simplified XAML snippet:
<ListView ItemsSource="{Binding Path=Matches}" VerticalOptions="StartAndExpand">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout Orientation="Vertical">
<Label Text="{Binding Path=MatchDetail.Name}"/>
<ListView ItemsSource="{Binding Path=Players}">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Path=PlayerDetail.FirstName}"/>
<!-- another ViewCell here seems to hide data somehow -->
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
which is placed in the root StackLayout element of the Xamarin ContentPage and whose BindingContext is set to the LeagueRootObject instance loaded properly from json (which I've checked in debugger as working).
The XAML code above is the best I've been able to do since adding Label inside the StackLayout contained in the inner ListView's ViewCell just doesn't show up properly...how can you add a ViewCell (or any other type of cell for that matter) to the inner ListView and build its View so it can show all the data belonging to all of the inner collection objects (I'm currently only getting the first one to show up also which is a problem when there's more than one player)...what am I doing wrong, should a ListView not be used that way and have some other 'View` control be used for outer objects, all I want is to show all inner objects grouped inside outer ones somehow please...
Thanks
In addition to me maybe being bad at Xamarin-flavoured XAML you also maybe right about there being bugs in the way the listviews work...since I just tried the above code on Windows 10 mobile and there it would not show any data of the inner collection but running it on Android worked by showing multiple inner collection last names...and after scrolling down past that outer collection object it would crash the app.
So, this may not be the way to show such a list of inner objects (and something like a button action beside each of them) grouped by belonging to outer collection onjects, but I still can't find another one example in XAML.
Could it be that you can only do that in foreach looped code in Xamarin.Forms 2.0?
If I understood correctly your issue, I could suggest you solution that helped me to show ListView in ListView.
https://stackoverflow.com/a/35598179/5912513