Is there any chance to get access from code behind to picker inside my listview/listview.itemtemplate/datatemplate/viewcell ?
First I want to get rid of x:Array String, then I want to get data from database and bind to my picker in OnApearing() method, after I select picker data it will update my database
<Picker Title="Select text"
x:Name="Picker"
HorizontalOptions="StartAndExpand"
VerticalOptions="Center"
FontSize="20"
SelectedIndexChanged="Picker_SelectedIndexChanged">
<Picker.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>Text1</x:String>
<x:String>Text2</x:String>
<x:String>Text3</x:String>
<x:String>Text4</x:String>
</x:Array>
</Picker.ItemsSource>
</Picker>
Thanks for help :)
You could use list in list when the picker is in the ViewCell
<ListView ItemsSource="{Binding MyItems}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid BackgroundColor="#eee" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Grid.Row="0" Text="{Binding Content}" TextColor="Red" />
<Picker Grid.Row="1" ItemsSource="{Binding PickerSource}" SelectedItem="{Binding SelectedValue}" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
in code behind
in ViewModel
public class MyViewModel: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public ObservableCollection<MyModel> MyItems { get; set; }
public MyViewModel()
{
// I used local data just for demo, you could get it from DB
MyItems = new ObservableCollection<MyModel>()
{
new MyModel(){Content="items1",PickerSource = new ObservableCollection<string>(){"option1","option2","option3" } },
new MyModel(){Content="items2",PickerSource = new ObservableCollection<string>(){"option1","option2","option3" } },
new MyModel(){Content="items3",PickerSource = new ObservableCollection<string>(){"option1","option2","option3" } },
new MyModel(){Content="items4",PickerSource = new ObservableCollection<string>(){"option1","option2","option3" } },
new MyModel(){Content="items5",PickerSource = new ObservableCollection<string>(){"option1","option2","option3" } },
new MyModel(){Content="items6",PickerSource = new ObservableCollection<string>(){"option1","option2","option3" } },
new MyModel(){Content="items7",PickerSource = new ObservableCollection<string>(){"option1","option2","option3" } },
};
foreach(var model in MyItems)
{
model.PropertyChanged += Model_PropertyChanged;
}
}
private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(e.PropertyName== "SelectedValue")
{
var SelectedModel = sender as MyModel;
var SelectedValue = SelectedModel.SelectedValue;
//... do something you want
}
}
}
in Model
public class MyModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public ObservableCollection<string> PickerSource { get; set; }
public string Content { get; set; }
string selectedValue;
public string SelectedValue
{
get { return selectedValue; }
set
{
selectedValue = value;
NotifyPropertyChanged("SelectedValue");
}
}
//other properties
}
Related
I have a collection view with a checkbox. I want to count the amount of checkboxes which have been selected and show that value in a label (string Sel). I think I have mostly done it however the label doesn't update. I think this is due to not calling OnProperty changed in the correct place which would update the label. I'm still rapping my head round MVVM. Thanks
ModelView:
public class MeetAWalkerViewModel : INotifyPropertyChanged
{
public ObservableCollection<PetProfile> source;
public ObservableCollection<PetProfile> PetInfo { get; private set; }
public ObservableCollection<PetProfile> EmptyPetInfo
{
get => source;
private set
{
if (value != source)
{
source = value;
OnPropertyChanged(nameof(EmptyPetInfo));
}
}
}
public string Sel { get; private set; }
public MeetAWalkerViewModel()
{
var count = EmptyPetInfo.Count(t => t.Selected);
Sel = "Amount of selected pets" + Convert.ToString(count);
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
Xaml:
<CollectionView x:Name="petCollectionView" ItemsSource="{Binding EmptyPetInfo}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="10" RowDefinitions="80" ColumnDefinitions="120,60,60">
<Image Grid.Column="0"
Grid.Row="0"
x:Name="PetImage"
Source="{Binding imageUrl}"/>
<Label Grid.Column="1"
Grid.Row="0"
Text="{Binding PetName}"
FontAttributes="Bold"
x:Name="labelpetname" VerticalTextAlignment="Center" HorizontalTextAlignment="Center"/>
<CheckBox Grid.Row="0" Grid.Column="2" HorizontalOptions="End" IsChecked="{Binding Selected, Mode=TwoWay}" CheckedChanged="CheckBox_CheckedChanged" BindingContext="{Binding .}"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
I made a sample based on your code, it works properly.
You can refer to the following code:
MeetAWalkerViewModel.cs
public class MeetAWalkerViewModel: INotifyPropertyChanged
{
public ObservableCollection<PetProfile> source;
//public ObservableCollection<PetProfile> PetInfo { get; private set; }
public ObservableCollection<PetProfile> EmptyPetInfo
{
get => source;
private set
{
if (value != source)
{
source = value;
OnPropertyChanged(nameof(EmptyPetInfo));
}
}
}
int _count;
public int Count
{
set
{
if (_count != value)
{
_count = value;
OnPropertyChanged(nameof(Count));
Sel = "Amount of selected pets is : " + Convert.ToString(_count);
}
}
get
{
return _count;
}
}
public void updateCount(int count) {
}
String sel;
public String Sel
{
set
{
if (sel != value)
{
sel = value;
OnPropertyChanged(nameof(Sel));
}
}
get
{
return sel;
}
}
public MeetAWalkerViewModel()
{
EmptyPetInfo = new ObservableCollection<PetProfile>();
EmptyPetInfo.Add(new PetProfile { PetName = "Pet1", IsSelected= false,ImageUrl= "cherry.png" });
EmptyPetInfo.Add(new PetProfile { PetName = "Pet2", IsSelected = false, ImageUrl = "watermelon.png" });
EmptyPetInfo.Add(new PetProfile { PetName = "Pet3", IsSelected = false, ImageUrl = "cherry.png" });
EmptyPetInfo.Add(new PetProfile { PetName = "Pet4", IsSelected = false, ImageUrl = "watermelon.png" });
EmptyPetInfo.Add(new PetProfile { PetName = "Pet5", IsSelected = false, ImageUrl = "cherry.png" });
EmptyPetInfo.Add(new PetProfile { PetName = "Pet6", IsSelected = false, ImageUrl = "watermelon.png" });
foreach (PetProfile petProfile in EmptyPetInfo) {
if (petProfile.IsSelected)
{
Count++;
}
}
Sel = "Amount of selected pets is : " + Convert.ToString(Count);
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
MainPage.xaml
<StackLayout HorizontalOptions="Center" Padding="10" >
<Label x:Name="countSelectedItemsLabel" Text="{Binding Sel}" FontSize="20" />
<CollectionView x:Name="petCollectionView" ItemsSource="{Binding EmptyPetInfo}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="10" RowDefinitions="80" ColumnDefinitions="120,60,60">
<Image Grid.Column="0"
Grid.Row="0"
x:Name="PetImage"
Source="{Binding ImageUrl}"/>
<Label Grid.Column="1"
Grid.Row="0"
Text="{Binding PetName}"
FontAttributes="Bold"
x:Name="labelpetname" VerticalTextAlignment="Center" HorizontalTextAlignment="Center"/>
<CheckBox Grid.Row="0" Grid.Column="2" HorizontalOptions="End" IsChecked="{Binding IsSelected, Mode=TwoWay}" CheckedChanged="CheckBox_CheckedChanged" BindingContext="{Binding .}"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
MainPage.xaml.cs
public partial class MainPage : ContentPage
{
MeetAWalkerViewModel viewModel;
int selectedCount = 0;
public MainPage()
{
InitializeComponent();
viewModel = new MeetAWalkerViewModel();
BindingContext = viewModel;
}
private void CheckBox_CheckedChanged(object sender, CheckedChangedEventArgs e)
{
PetProfile model = (PetProfile)((CheckBox)sender).BindingContext;
if (model.IsSelected)
{
selectedCount++;
}
else
{
selectedCount--;
}
viewModel.Count = selectedCount;
}
}
PetProfile.cs
public class PetProfile
{
public string PetName { get; set; }
public string ImageUrl { get; set; }
public bool IsSelected { get; set; }
}
The result is:
You need to change the String like this
String sel ;
public String Sel
{
set
{
if (sel != value)
{
sel = value;
OnPropertyChanged(nameof(Sel ));
}
}
get
{
return sel ;
}
}
I try to use EventAggregator between 2 viewmodel. However I don't know how to create singleton class to connect between 2 ViewModel
I refer this articles to write a example for me
https://www.codeproject.com/Articles/812461/Event-Aggregator-Pattern
https://blog.magnusmontin.net/2014/02/28/using-the-event-aggregator-pattern-to-communicate-between-view-models/
My screenario is when I click on each item on listview, detailListView show detail item
Here is my code
Item.cs
public class Item
{
public Item() {}
public int Id { get; set; }
public string Name { get; set; }
public double Price { get; set; }
public int Quantity { get; set; }
}
ListViewModel.cs
internal class ListViewModel
{
protected readonly IEventAggregator _eventAggregator;
public ListViewModel(IEventAggregator eventAggregator)
{
this._eventAggregator = eventAggregator;
this.Items = new List<Item>();
this.Items.Add(new Item { Id = 1, Name = "Item A", Price = 100.00, Quantity = 250 });
this.Items.Add(new Item { Id = 2, Name = "Item B", Price = 150.00, Quantity = 150 });
this.Items.Add(new Item { Id = 2, Name = "Item C", Price = 300.00, Quantity = 100 });
}
public List<Item> Items { get; private set; }
private Item _selectedItem;
public Item SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
//Publish event:
this._eventAggregator.PublishEvent(_selectedItem);
}
}
}
DetailsViewModel.cs
public class DetailsViewModel : INotifyPropertyChanged
{
protected readonly IEventAggregator _eventAggregator;
public DetailsViewModel(IEventAggregator eventAggregator)
{
this._eventAggregator = eventAggregator;
_eventAggregator.SubsribeEvent(this);
}
private Item _item;
public Item Item
{
get { return _item; }
set { _item = value; NotifyPropertyChanged("Item"); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
ListView.xaml
<Grid>
<ListBox ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedItem}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
ListView.xaml.cs
public partial class ListView : UserControl
{
public ListView()
{
InitializeComponent();
DataContext = new ListViewModel(ApplicationService.Instance.EventAggregator);
}
}
DetailsView.xaml
<Grid>
<ContentControl Content="{Binding Item}">
<ContentControl.ContentTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Left" VerticalAlignment="Top">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="Id: " FontWeight="Bold" Grid.Row="0"/>
<TextBlock Text="Name: " FontWeight="Bold" Grid.Row="1"/>
<TextBlock Text="Price: " FontWeight="Bold" Grid.Row="2"/>
<TextBlock Text="Quantity: " FontWeight="Bold" Grid.Row="3"/>
<TextBlock Text="{Binding Id}" Grid.Row="0" Grid.Column="1"/>
<TextBlock Text="{Binding Name}" Grid.Row="1" Grid.Column="1"/>
<TextBlock Text="{Binding Price, StringFormat=c2}" Grid.Row="2" Grid.Column="1"/>
<TextBlock Text="{Binding Quantity}" Grid.Row="3" Grid.Column="1"/>
</Grid>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</Grid>
DetailsView.xaml.cs
public partial class DetailsView : UserControl
{
public DetailsView()
{
InitializeComponent();
DataContext = new DetailsViewModel(ApplicationService.Instance.EventAggregator);
}
}
And finnaly singleton class to connect 2 ViewModel
ApplicationService.cs
internal sealed class ApplicationService
{
private ApplicationService() { }
private static readonly ApplicationService _instance = new ApplicationService();
internal static ApplicationService Instance { get { return _instance; } }
private IEventAggregator _eventAggregator;
internal IEventAggregator EventAggregator
{
get
{
if (_eventAggregator == null)
_eventAggregator = new EventAggregator();
return _eventAggregator;
}
}
}
I don't know how to create _eventAggregator = new EventAggregator(); There is no EventAggregator() in Gautham Prabhu K's article.
I'd like to implement a simple picker XAML binded to 3 labels, where when I choose a value from the picker the labels will be populated automatically (the data comes from SQLite).
Here is what I have:
<Picker x:Name="ListJobs" Title="Select Job" ItemsSource="{Binding options}" ItemDisplayBinding="{Binding JobNo}" SelectedItem="{Binding SelectedJobs}"/>
<Label Text="{Binding JobsId}" IsVisible="True" x:Name="TxtId"/>
<Label Text="{Binding name}" IsVisible="True" x:Name="TxtName"/>
<Label Text="{Binding location}" IsVisible="True" x:Name="TxtLoc"/>
Model
public class Jobs
{
public string JobsId {get;set;}
public string name {get;set;}
public string location {get;set;}
public Jobs(){}
}
Code Behind:
protected override OnAppearing()
{
jobsInfo = (List<Jobs>) GetJob();
foreach (var item in jobsInfo)
{
Jobs options = new Jobs
{
JobsId = item.JobsId,
name = item.name,
location = item.location
};
BindingContext = options;
}
}
private IEnumerable<Jobs> GetJobsInfo()
{
var db = _connection.Table<Jobs>();
return db.ToList();
}
I would to select from picker (like dropdown) and populate the labels.
First, there are some mistakes in your code.
1.When you go through the loop (the data you gained from db), options is always updated with new data(so it generates using last object), and you set it to BindingContext. You should set a modelView here rather a model.
2.The itemSource of Picker must be a list, however you set a model here.
3.The viewmodel must implement INotifyPropertyChanged to notify the changes.
I think what you need understanding most is not this Picker , it is How to work with binding.
Bindable Properties
Data Binding Basics
From Data Bindings to MVVM
Okay, let us come back to this case. What you need is here
I simplified the demo and you can refer to it.
XAML
<Picker x:Name="picker"
Title="Select Job"
ItemsSource="{Binding JobList}"
ItemDisplayBinding="{Binding Name}"
SelectedItem="{Binding SelectedJob}"/>
<Label Text="{Binding SelectedJob.JobsId}" IsVisible="True" x:Name="TxtId" Margin="0,100,0,0"/>
<Label Text="{Binding SelectedJob.Name}" IsVisible="True" x:Name="TxtName"/>
<Label Text="{Binding SelectedJob.Location}" IsVisible="True" x:Name="TxtLoc"/>
Model and ViewModel:
public class Jobs
{
public string JobsId { get; set; }
public string Name { get; set; }
public string Location { get; set; }
}
public class RootModel : INotifyPropertyChanged
{
List<Jobs> jobList;
public List<Jobs> JobList
{
get { return jobList; }
set
{
if (jobList != value)
{
jobList = value;
OnPropertyChanged();
}
}
}
Jobs selectedJob;
public Jobs SelectedJob
{
get { return selectedJob; }
set
{
if (selectedJob != value)
{
selectedJob = value;
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Code behind:
public MainPage()
{
InitializeComponent();
this.BindingContext = new RootModel
{
JobList = GetJobsInfo()
};
}
private List<Jobs> GetJobsInfo()
{
var db = _connection.Table<Jobs>();
return db.ToList();
}
My Test:
XAML:
<Picker x:Name="ListJobs" Title="Select Job" ItemsSource="{Binding AllJobs}"
ItemDisplayBinding="{Binding Name}" SelectedItem="{Binding SelectedJob}"/>
<Label Text="{Binding SelectedJob.JobId}" IsVisible="True" x:Name="TxtId"/>
<Label Text="{Binding SelectedJob.Name}" IsVisible="True" x:Name="TxtName"/>
<Label Text="{Binding SelectedJob.Location}" IsVisible="True" x:Name="TxtLoc"/>
Model:
public class Job
{
public string JobId { get; set; }
public string Name { get; set; }
public string Location {get; set; }
}
public class JobModel
{
public List<Job> AllJobs { get; set; }
public Job SelectedJob { get; set; }
}
Code behind:
protected override OnAppearing()
{
BindingContext = new JobsModel {
AllJobs = GetJobs()
};
}
private List<Jobs> GetJobs()
{
var db = _connection.Table<Jobs>();
return db.ToList();
}
//How to add picker or dropdownlist in Xamarin forms and bind text ?
//we will be using Picker Properties -> pickername.ItemsSource and pickername.SelectedItem
//In above line pickername is the x:Name given to picker
//page.xaml
<Frame CornerRadius="5" HeightRequest="50" Padding="0">
<StackLayout Orientation="Horizontal">
<controls:BorderlessPicker
x:Name="Pickdoctype"
ItemDisplayBinding="{Binding text}"
SelectedIndexChanged="Pickdoctype_SelectedIndexChanged"
HorizontalOptions="FillAndExpand"
Title="Enter Document Type"
FontSize="20"
TextColor="Gray">
</controls:BorderlessPicker>
<ImageButton BackgroundColor="Transparent" Padding="0">
<ImageButton.Source>
<FontImageSource Glyph="" Size="25"
FontFamily="{StaticResource FontAwesomeSolid}"
Color="Gray"></FontImageSource>
</ImageButton.Source>
</ImageButton>
</StackLayout>
</Frame>
//page.xaml.cs
//Picker Get API
var responseUserData = await httpService.Get("manageDocument/documentType/documentType/" + UserID);
if (responseUserData.IsSuccessStatusCode)
{
var responseUserDataString = await responseUserData.Content.ReadAsStringAsync();
var myDeserializedClass = JsonConvert.DeserializeObject<List<DocumentTypeModel>>(responseUserDataString);
Pickdoctype.ItemsSource = myDeserializedClass;
}
//Get Picker value functionality
private void Pickdoctype_SelectedIndexChanged(object sender, EventArgs e)
{
try
{
DocumentTypeModel selectedItem = (DocumentTypeModel)Pickdoctype.SelectedItem;
string PickerValue = selectedItem.value;
string PickerText = selectedItem.text;
}
catch (Exception ex)
{
}
}
//Create a model class
public class DocumentTypeModel
{
public string masterName { get; set; }
public string text { get; set; }
public string value { get; set; }
}
Good morning to all, I've looked in several places I researched and did not found a solution that worked for my problems.
I'm new to C #, XAML and Xamarin, I am doing an application that creates lists with products on a screen, the screen with products is been populated via Json WebApi. Until there everthing looks fine, but since i tried to add 'ADD' and 'Share' functionalities it is loading the main page and crashes on navigating to listsPage.
I need to get a product from the product Page and add it to another view where my lists are. I created a ContextActions with 'Share' and 'AddToList' but i don't know how to get that product and 'Add' it to my lists. Same problem with 'Share' when i get the MenuItem and try to pass it to a Task in my ProductViewModel i get a NullReferenceException, but the object is not null.
I appreciate if someone could help me with this issues.
I know the post has got quite long but i wanted to give every possible info.
Here is my Lists Page:
<ListView x:Name="listaView" ItemSelected="listSelected" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout Padding="20,0,20,0"
Orientation="Horizontal"
HorizontalOptions="FillAndExpand">
<Label Text="{Binding Name}"
VerticalTextAlignment="Center"
HorizontalOptions="StartAndExpand" />
<Image Source="check.png"
HorizontalOptions="Start"
IsVisible="{Binding Done}" />
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
and my listDetail:
<Label Text="Name" />
<Entry x:Name="nameEntry" Text="{Binding Name}" />
<Label Text="Description" />
<Entry x:Name="descriptionEntry" Text="{Binding Description}" />
<Label Text="Typ" />
<controls:BindablePicker x:TypeArguments="enums:Typ" SelectedItem="{Binding Typ}" />
<Label Text="Done" />
<Switch x:Name="doneEntry" IsToggled="{Binding Done}" />
<Label Text="Products:" />
<ListView ItemsSource="{Binding Products}" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout Orientation="Horizontal">
<Image Aspect="AspectFit" HeightRequest="20" WidthRequest="20" Source="{Binding Image}" />
<Label Text="{Binding Name}" />
<Label Text="{Binding Price, StringFormat='R${0:C2}'}" />
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Text="Save" Clicked="salveClicked" />
<Button Text="Delete" Clicked="deleteClicked" />
<Button Text="Cancel" Clicked="cancelClicked" />
<Button Text="Speak" Clicked="speakClicked" />
</StackLayout>
</ContentPage>
ListDetails CODE BEHIND:
public ProductListDetailPage()
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, true);
}
void saveClicked(object sender, EventArgs e)
{
var lista = (Lists)BindingContext;
App.Database.SaveList(lista);
this.Navigation.PopAsync();
}
void deleteClicked(object sender, EventArgs e)
{
var lista = (Lists)BindingContext;
App.Database.DeleteList(lista.ListaID);
this.Navigation.PopAsync();
}
void cancelClicked(object sender, EventArgs e)
{
var lista = (Lists)BindingContext;
this.Navigation.PopAsync();
}
void speakClicked(object sender, EventArgs e)
{
var lists = (Lists)BindingContext;
DependencyService.Get<ITextToSpeech>().Speak(lists.Name+ " " + lists.Descrip);
}
}
}
I believe the problem is in my model but have no idea what it is
Product Model:
public class Product : INotifyPropertyChanged
{
private int id;
public int ProductID
{
get { return id; }
set
{
id = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(id)));
}
}
private string name;
public string Name
{
get { return name; }
set
{
name= value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(name)));
}
}
private double price;
public double Price{
get { return price; }
set
{
price= value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(price)));
}
}
private string dtFab;
public string DtFab
{
get { return dtFab; }
set
{
dtFab= value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(dtFab)));
}
}
private string dtValid;
public string DtValid {
get { return dtValid; }
set
{
dtValid= value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(dtValid)));
}
}
private string amount;
public string Amount{
get { return quantidade; }
set
{
amount= value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(amount)));
}
}
private string descrip;
public string Descrip{
get { return descrip; }
set
{
descrip= value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(descrip)));
}
}
private string image;
public string Image
{
get { return image; }
set
{
image= value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(image)));
}
}
private ICollection<ListProduct> listProduct;
public ICollection<ListProduct> ListProduct{
get { return listProduct; }
set
{
listProduct= value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(listProduct)));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
ListsModel:
public class Lists
{
public Lista()
{
}
[PrimaryKey, AutoIncrement]
public int ListID get; set; }
public string Name { get; set; }
public string Descrip { get; set; }
public bool Done { get; set; }
public Typ Typ { get; set; }
public virtual ICollection<ListsProduct> ListsProducts{ get; set; }
}
public class ListsProduct
{
public int ListsProductID{ get; set; }
public int ListID { get; set; }
public int ProductID { get; set; }
public virtual Lists Lists { get; set; }
public virtual Product Product { get; set; }
}
}
ListProductModel:
SearchProductPage:
<StackLayout Orientation="Vertical">
<SearchBar Text="{Binding SearchBarText}" />
<Button x:Name="btnPesquisar" Text="Search" Command="{Binding SearchCommand}" />
<ListView ItemsSource="{Binding Products}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.ContextActions>
<MenuItem Text="Share" Clicked="ShareProduct" />
<MenuItem Text="Add To" Clicked="AddProduct" />
</ViewCell.ContextActions>
<ViewCell.View>
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand" >
<Image Aspect="AspectFit" HeightRequest="20" WidthRequest="20" Source="{Binding Image}" />
<Label Text="{Binding Name}" />
<Label Text="{Binding Price, StringFormat='R${0:C2}'}" HorizontalOptions="End" />
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
SearchPage CODE BEHIND:
public partial class SearchPage: ContentPage
{
ProductsViewModel viewModel;
public TelaPesquisaView()
{
InitializeComponent();
this.BindingContext = new ViewModels.ProductsViewModel();
}
public async void AddProduct(object sender, EventArgs e)
{
var al = ((MenuItem)sender);
await viewModel.AddToList(al.BindingContext as Product);
var produtoLista = new ListsPage();
await Navigation.PushAsync(produtoLista);
}
public async void ShareProduct(object sender, EventArgs e)
{
var al = ((MenuItem)sender);
if (al != null) {
await viewModel.Share(al.BindingContext as Produto);
}
}
}
}
And the ProductViewModel
public class ProductViewModel : INotifyPropertyChanged
{
private string searchBarText = string.Empty;
public string SearchBarText {
get { return searchBarText ; }
set
{
if (searchBarText != value)
{
searchBarText = value ?? string.Empty;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(searchBarText )));
if (SearchCommand.CanExecute(null))
{
SearchCommand.Execute(null);
}
}
}
}
// filtrar somente os 5 primeiros
#region Command SearchCommand
private Xamarin.Forms.Command searchCommand;
public ICommand SearchCommand{
get
{
searchCommand= searchCommand?? new Xamarin.Forms.Command(DoSearchCommand, ExecuteCommand);
return searchCommand;
}
set
{
searchCommand= (Xamarin.Forms.Command)value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(searchCommand)));
}
}
private void DoSearchCommand()
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Products)));
}
private bool ExecuteCommand()
{
return true;
}
#endregion
private ObservableCollection<Models.Produto> products;
public ObservableCollection<Models.Produto> Products {
get
{
ObservableCollection<Models.Product> searchProducts = new ObservableCollection<Models.Product>();
if (products != null)
{
List<Models.Product> prod = (from p in products
where p.Name.ToLower().Contains(searchBarText.ToLower())select p).Take(3).ToList<Models.Product>();
if (prod != null && prod.Any())
{
searchedProducts = new ObservableCollection<Models.Product>(prod);
}
}
return searchedProducts ;
}
set
{
products = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Products)));
}
}
public ProductsViewModel()
{
SearchCommand = new Xamarin.Forms.Command(async () =>
{
var products = await ApiProducts.Api.GetAsync();
Products = new ObservableCollection<Models.Product>(products );
});
}
public async Task AddToList(ListsProduct prod)
{
Lists list = new Lists();
list.ListsProduct.Add(prod);
App.Database.SaveList(list);
}
public async Task Share(Models.Product prod)
{
var title = prod.NomeProduto;
var message = prod.ToString();
// Share message and an optional title.
await CrossShare.Current.Share(message, title );
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
Here is the starting point for you. For the rest you can use the same pattern passing your new product in constructors or just implement setters in your view models.
Define binding to get a new product
<MenuItem Text="Adicionar à" Clicked="AdicionaProduto" CommandParameter="{Binding .}" />
Then pass it to your ListasView constructor as parameter
public async void AdicionaProduto(object sender, EventArgs e)
{
var al = ((MenuItem)sender);
var produtoLista = new ListasView(al.CommandParameter as Produto);
await Navigation.PushAsync(produtoLista);
}
To be able to do that you need to change a constructor
public ListasView(Produto newProduto = null)
{
InitializeComponent();
//this.BindingContext = new ViewModels.ListasViewModel();
if (newProduto != null)
{
//do something
int x = 0;
}
You can take that newProduto and store it in your DB or pass further to other models or views via constructor or some setters.
I have a ListBox which is bound to a List of DataModel.
DataModel.cs
public class DataModel
{
public string Name { get; set; }
public string Desc { get; set; }
public string Code { get; set; }
}
The ListBox should display two properties, so I have defined the ItemTemplate as below
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"></TextBlock>
<TextBlock Text=" - "></TextBlock>
<TextBlock Text="{Binding Code}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
My requirement is that the user can select which two properties to display in the ListBox at run time. I am not sure how to achieve this. I have created a sample solution to explain my problem.
MainWindow.xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Margin="5">
<Label Content="Property One"></Label>
<ComboBox Name="ComboBox1" Margin="3" Width="150" ItemsSource="{Binding DataModelProperties}"></ComboBox>
<Label Content="Property Two"></Label>
<ComboBox Name="ComboBox2" Margin="3" Width="150" ItemsSource="{Binding DataModelProperties}"></ComboBox>
<Button Content="Go" Click="ButtonBase_OnClick" Margin="3"></Button>
</StackPanel>
<ListBox Grid.Row="1" ItemsSource="{Binding DataModelList}" Margin="5">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"></TextBlock>
<TextBlock Text=" - "></TextBlock>
<TextBlock Text="{Binding Code}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
}
}
public class ViewModel
{
public List<String> DataModelProperties { get; set; }
public List<DataModel> DataModelList { get; set; }
public ViewModel()
{
DataModelList = new List<DataModel>();
DataModelList.Add(new DataModel() { Name = "Name1", Code = "Code1", Desc = "Desc1" });
DataModelList.Add(new DataModel() { Name = "Name2", Code = "Code2", Desc = "Desc2" });
DataModelList.Add(new DataModel() { Name = "Name3", Code = "Code3", Desc = "Desc3" });
DataModelProperties = typeof(DataModel).GetProperties().Select(s => s.Name).ToList();
}
}
public class DataModel
{
public string Name { get; set; }
public string Desc { get; set; }
public string Code { get; set; }
}
}
Things I have tried
<TextBlock Text="{Binding ElementName=ComboBox1, Path=SelectedItem}"></TextBlock>
This just displays the selected property name.
I suggest you to manage all the stuff regarding the "dynamic description" showed in the ListBox in your ViewModel.
First of all, your view model and your model should implement INotifyPropertyChanged. In my sample I created a simple base class which implements it. My base class is called NotifyPropertyChangedImpl.
Moreover I added two properties to the view model: the comboboxes for selecting the properties are binded to those two properties.
public class ViewModel : NotifyPropertyChangedImpl
{
private string property1;
private string property2;
public List<String> DataModelProperties { get; set; }
public List<DataModel> DataModelList { get; set; }
public string Property1
{
get
{
return property1;
}
set
{
if (property1 != value)
{
property1 = value;
SetDynamicDescriptions();
}
}
}
public string Property2
{
get
{
return property2;
}
set
{
if (property2 != value)
{
property2 = value;
SetDynamicDescriptions();
}
}
}
public ViewModel()
{
DataModelList = new List<DataModel>();
DataModelList.Add(new DataModel() { Name = "Name1", Code = "Code1", Desc = "Desc1" });
DataModelList.Add(new DataModel() { Name = "Name2", Code = "Code2", Desc = "Desc2" });
DataModelList.Add(new DataModel() { Name = "Name3", Code = "Code3", Desc = "Desc3" });
DataModelProperties = typeof(DataModel).GetProperties().Select(s => s.Name).ToList();
}
private void SetDynamicDescriptions()
{
PropertyInfo propertyInfo1;
PropertyInfo propertyInfo2;
Type type = typeof(DataModel);
if (!String.IsNullOrEmpty(property1) && !String.IsNullOrEmpty(property2))
{
propertyInfo1 = type.GetProperty(property1);
propertyInfo2 = type.GetProperty(property2);
foreach (DataModel dataModel in DataModelList)
{
dataModel.DynamicDescription = String.Format("{0} - {1}",
propertyInfo1.GetValue(dataModel, null), propertyInfo2.GetValue(dataModel, null));
}
}
}
}
As you can see the method SetDynamicDescriptions rebuilds the DynamicsDescription each time Property1 or Property2 changes.
I also added a property to the model class:
public class DataModel : NotifyPropertyChangedImpl
{
private string dynamicDescription;
public string Name { get; set; }
public string Desc { get; set; }
public string Code { get; set; }
public string DynamicDescription
{
get
{
return dynamicDescription;
}
set
{
if (dynamicDescription != value)
{
dynamicDescription = value;
OnPropertyChanged("DynamicDescription");
}
}
}
}
So at the end your XAML will be:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Margin="5">
<Label Content="Property One"></Label>
<ComboBox Name="ComboBox1" Margin="3" Width="150" ItemsSource="{Binding DataModelProperties}"
SelectedItem="{Binding Property1}"></ComboBox>
<Label Content="Property Two"></Label>
<ComboBox Name="ComboBox2" Margin="3" Width="150" ItemsSource="{Binding DataModelProperties}"
SelectedItem="{Binding Property2}"></ComboBox>
</StackPanel>
<ListBox Grid.Row="1" ItemsSource="{Binding DataModelList}" Margin="5">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DynamicDescription}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
I hope this can help you.
In short, what I've done is I made 2 public properties to bind to and changed your properties to fields. Also I've made custom attribute, to control what fields user is able to select. And created a DisplayOptions class to store selection and spread it across DataModel instances.
The solution is a bit messy, considering it's a PoC, but I believe this can do:
XAML:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Margin="5">
<Label Content="Property One"></Label>
<ComboBox Name="ComboBox1" Margin="3" Width="150" ItemsSource="{Binding DataModelProperties}"></ComboBox>
<Label Content="Property Two"></Label>
<ComboBox Name="ComboBox2" Margin="3" Width="150" ItemsSource="{Binding DataModelProperties}"></ComboBox>
<Button Content="Go" Click="ButtonBase_OnClick" Margin="3"></Button>
</StackPanel>
<ListBox Grid.Row="1" ItemsSource="{Binding DataModelList}" Margin="5">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding DisplayProperty1}"></TextBlock>
<TextBlock Text=" - "></TextBlock>
<TextBlock Text="{Binding DisplayProperty2}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
CS:
public partial class MainWindow : Window
{
public ViewModel model = new ViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = model;
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
//This part has to be on the View side, but this is PoC
model.Opts.Prop1 = typeof(DataModel).GetFields()
.Where(a => a.Name == ComboBox1.SelectedItem.ToString()).FirstOrDefault();
model.Opts.Prop2 = typeof(DataModel).GetFields()
.Where(a => a.Name == ComboBox2.SelectedItem.ToString()).FirstOrDefault();
DataContext = null;
DataContext = model;
}
}
public class ViewModel
{
public DisplayOptions Opts = new DisplayOptions();
public List<String> DataModelProperties { get; set; }
public List<DataModel> DataModelList { get; set; }
public ViewModel()
{
var properties = typeof(DataModel).GetFields()
.Where(a => a.CustomAttributes.Any(b => b.AttributeType == typeof(SwitchableAttr)));
//Initialising options before creating DataModel instances
DataModelProperties = properties.Select(s => s.Name).ToList();
Opts.Prop1 = properties.ElementAt(0);
Opts.Prop2 = properties.ElementAt(1);
DataModelList = new List<DataModel>();
DataModelList.Add(new DataModel() { Name = "Name1", Code = "Code1", Desc = "Desc1", options = Opts });
DataModelList.Add(new DataModel() { Name = "Name2", Code = "Code2", Desc = "Desc2", options = Opts });
DataModelList.Add(new DataModel() { Name = "Name3", Code = "Code3", Desc = "Desc3", options = Opts });
}
}
public class DisplayOptions
{
public FieldInfo Prop1;
public FieldInfo Prop2;
}
public class DataModel
{
public DisplayOptions options;
[SwitchableAttr]
public string Name;
[SwitchableAttr]
public string Desc;
[SwitchableAttr]
public string Code;
public string DisplayProperty1 { get { return (string)options.Prop1.GetValue(this); } set { } }
public string DisplayProperty2 { get { return (string)options.Prop2.GetValue(this); } set { } }
}
public class SwitchableAttr : Attribute { }
This is not Complete MVVM
so here is the code
public partial class MainWindow : Window
{
private ViewModel vm;
public MainWindow()
{
InitializeComponent();
vm = new ViewModel();
this.DataContext = vm;
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
if (!string.IsNullOrEmpty(ComboBox1.Text) && !string.IsNullOrEmpty(ComboBox2.Text))
{
vm.AddDataToModel(ComboBox1.Text, ComboBox2.Text);
}
}
}
public class ViewModel
{
public List<String> DataModelProperties { get; set; }
private ObservableCollection<DataModel> _DataModelList;
public ViewModel()
{
_DataModelList = new ObservableCollection<DataModel>();
DataModelProperties = typeof(DataModel).GetProperties().Select(s => s.Name).ToList();
}
public void AddDataToModel(string cmbx1Val,string cmbx2Val)
{
_DataModelList.Add(new DataModel() { Name = cmbx1Val, Code = cmbx2Val, Desc = "Desc1" });
}
public ObservableCollection<DataModel> DataModelList
{
get
{
return _DataModelList;
}
set
{
_DataModelList = value;
}
}
}