I'm trying to make an expandable listview item when i press the name of an item .. the problem is that the value of IsVisiable change but that don't reflect on the page and it's stays the same (only the name of the item is shown not all the hidden details) .
First I added a IsVisiable prop to my model
public class Item
{
public int Id { get; set; }
public string UserId { get; set; }
public string Name { get; set; }
public string Category { get; set; }
public string Quality { get; set; }
public int Size { get; set; }
public decimal Price { get; set; }
public bool IsVisiable { get; set; }
}
This is the content page
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewModels="clr-namespace:XamarinApp.ViewModels;assembly=XamarinApp"
x:Class="XamarinApp.ViewModels.Views.CustomerProfilePage">
<ContentPage.BindingContext>
<viewModels:CustomerProfileViewModel/>
</ContentPage.BindingContext>
<ListView ItemsSource="{Binding Items}"
HasUnevenRows="True"
ItemTapped="ListView_OnItemTapped">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text="{Binding Name}" ></Label>
<StackLayout IsVisible="{Binding IsVisiable}">
<Label Text="{Binding Category}" ></Label>
<Label Text="{Binding Quality}" ></Label>
<Label Text="{Binding Size}" ></Label>
<Label Text="{Binding Price} "></Label>
</StackLayout>
<!-- <Label Text="المسافة بينك وبين العميل"></Label> -->
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I create a onItemTapped method
public partial class CustomerProfilePage : ContentPage
{
public CustomerProfilePage (string userId)
{
InitializeComponent ();
this.BindingContext = new CustomerProfileViewModel(userId);
}
private void ListView_OnItemTapped(object sender, ItemTappedEventArgs e)
{
var vm = BindingContext as CustomerProfileViewModel;
var Item = e.Item as Item;
vm?.HideOrShowItem(Item);
}
}
Then i added HideOrShow item method for control in my vm
public class CustomerProfileViewModel:INotifyPropertyChanged
{
public CustomerProfileViewModel()
{
}
public CustomerProfileViewModel(string cutomerId)
{
CustomerId = cutomerId;
if (GetItems.CanExecute(null))
GetItems.Execute(null);
}
public List<Item> Items
{
get => _items;
set
{
if (Equals(value, _items)) return;
_items = value;
OnPropertyChanged();
}
}
public string CustomerId { get;}
private List<Item> _items;
private Item _oldItem;
ApiServices _apiServices = new ApiServices();
public void HideOrShowItem(Item item)
{
if (_oldItem == item)
{
item.IsVisiable = !item.IsVisiable;
UpdateItems(item);
}
else
{
if (_oldItem != null)
{
_oldItem.IsVisiable = false;
UpdateItems(_oldItem);
}
item.IsVisiable = true;
UpdateItems(item);
}
_oldItem = item;
}
private void UpdateItems(Item item)
{
var index = Items.IndexOf(item);
Items.Remove(item);
Items.Insert(index, item);
}
public ICommand GetItems
{
get
{
return new Command(async () =>
{
var accesstoken = Settings.AccessToken;
Items = await _apiServices.GetItemsForSpecificUser(accesstoken, CustomerId);
});
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
The problems I can see:
Your class Item must implement INotifyPropertyChanged interface
I also see that you can update your list Items. So to reflect the list changes in your UI, Items must be an ObservableCollection<Item>
Updated Item class (should look something like that):
public class Item : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _id;
private bool _isVisible;
// . . .
public int Id
{
get => _id;
set
{
if (_id != value)
{
_id = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.Id)));
}
}
}
public bool IsVisible
{
get => _isVisible;
set
{
if (_isVisible != value)
{
_isVisible = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.IsVisible)));
}
}
}
// . . . Other properties
And in your ViewModel, declare the list of items:
private ObservableCollection<Item> _items;
public ObservableCollection<Item> Items{
get => _items;
private set{
_items = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.Items)));
}
}
Try with this and tell us if it's ok...
Related
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!
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; }
}
In my XamarinForms project I am trying to bind a label text to a property of a class. when I pass in the object to my view the label is not being populated. can someone see what I am doing wrong?
In my view model I have
class ManagerLevelPageViewModel : INotifyPropertyChanged
{
private UserSelections _MyUserSelections;
public UserSelections MyUserSelections
{
get { return _MyUserSelections; }
set {
_MyUserSelections = value;
NotifyPropertyChanged();
}
}
public ManagerLevelPageViewModel(UserSelections _temp)
{
MyUserSelections = _temp;
MyUserSelections.selectedClientName = _temp.selectedClientName;
//myUserSelections = _myUserSelections;
//SetValues();
}
here is the class
public class UserSelections
{
public int selectedClientId { get; set; }
public string selectedClientName { get; set; }
public string selectedClientShortCode { get; set; }
public decimal selectedClientPL { get; set; }
public string TopdayIdentifier { get; set; }
}
here is the view.cs
ManagerLevelPageViewModel vm;
public ManagerLevelPage (UserSelections _myUserSelections)
{
vm = new ManagerLevelPageViewModel(_myUserSelections);
InitializeComponent ();
BindingContext = vm;
DownloadData();
}
lastly here is the xaml
<Label Text="{Binding MyUserSelections.ClientName}"/>
notify property changed
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
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
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.