How to show values in Picker occupying MVVM with Xamarin - c#

I want to Bindar an ObservableCollection of the Manufacturer Object and show its Name property in a Picker occupying the MVVM pattern in Xamarin, but I'm not getting results (Blank Picker)
This Picker is displayed in the View FiltersSisquimView.xaml as follows
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
BindingContext="{Binding Main, Source={StaticResource Locator}}"
x:Class="AppValora.Views.Sisquim.FiltrosSisquimView"
Title="SISQUIM®">
<StackLayout
Orientation="Vertical"
BindingContext="{Binding Filtros}">
<Picker Title="Seleccione Fabricante"
Margin="15,5,15,5"
HorizontalOptions="FillAndExpand"
ItemsSource="{Binding Fabricantes, Mode=TwoWay}"
ItemDisplayBinding="{Binding Name}"
SelectedItem="{Binding Name}">
</Picker>
</StackLayout>
</ContentPage>
As noted, the StackLayout containing the Picker is declared as follows in the MainViewModel
public FiltrosViewModel Filtros { get; set; }
Then in my FiltersViewModel.CS I declare the Observable Collection which will have my Picker control (in addition to initializing it in the constructor) and filled with an array which has two Id and Name properties
public ObservableCollection<Fabricante> Fabricantes { get; set; }
public FiltrosViewModel()
{
Fabricantes = new ObservableCollection<Fabricante>();
LoadFabricantes();
}
async void LoadFabricantes()
{
IsRunning = true;
//CONSUMING API...
var list = response.Fabricantes;
Fabricantes = new ObservableCollection<Fabricante>(list);
IsRunning = false;
}
How to show values ​​in Picker occupying MVVM with Xamarin
I want to Bindar an ObservableCollection of the Manufacturer Object and show its Name property in a Picker occupying the MVVM pattern in Xamarin, but I'm not getting results (Blank Picker)
This Picker is displayed in the View FiltersSisquimView.xaml as follows
As noted, the StackLayout containing the Picker is declared as follows in the MainViewModel
Then in my FiltersViewModel.CS I declare the Observable Collection which will have my Picker control (in addition to initializing it in the constructor) and filled with an array which has two Id and Name properties
As you can see, I am correctly filling the Observable Collection "Fabricantes"
But how can I show the Name property in the Picker? I am new working with MVVM and I have in mind that I am correctly fitting the ViewModel to the Main (since my other controls work)
As once the user selected the name, can I rescue the ID of that Name, any help for me?

The problem is this line:
Fabricantes = new ObservableCollection<Fabricante>(list);
When you new up an ObservableCollection you lose the actual binding. You either need to set the BindingContext then again, but that is not what you would want. Instead, clear the existing collection and repopulate it. For example, like this:
var list = response.Fabricantes;
Fabricantes.Clear();
foreach (var item in list)
Fabricantes.Add(item);

Related

How to get ListView item index from custom ViewCell in Xamarin Forms?

I created a ListView that have a custom ViewCell as below:
<ListView x:Name="ListView1" ItemTapped="ListView1_ItemTapped"
SeparatorVisibility="None" RowHeight="192" HasUnevenRows="False"
FlowDirection="RightToLeft" CachingStrategy="RecycleElement" >
<ListView.ItemTemplate>
<DataTemplate>
<custom:ViewCell1 />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
and here is the XAML for the custom ViewCell
<ViewCell.View>
<StackLayout>
<Label Text="{Binding Name}" />
<Label Text="{Binding ID}" />
<Button x:Name="Button1" Text="Get index" Clicked="Button1_Clicked" />
</StackLayout>
</ViewCell.View>
All I need is when I click on Button1, I get the ListView1 item index (or ViewCell index)
The problem is I can't access ListView1 from Button1_Clicked event from the code behind in the custom ViewCell and gets tapped item index of ListView1 (or even gets ViewCell tapped item index).
I searched a lot, and found that it can be done by 3 ways:
1- Create an attached property for the ViewCell to get its index.
2- Use indexer for the ViewCell and get its index.
3- Use ITemplatedItemsView interface as mentioned in this question
But unfortunately I couldn't implement any of them from Button1_Clicked event in the custom ViewCell code behind because I'm not an expert in either MVVM or C#.
Can I have an expert assistance please.
Thanks
There are many ways which can implements it . If you are new to data binding and MVVM. I will provide the easiest way .
Firstly , add a property in the model of ItemSource .
public class YourModel
{
public int Index { get; }
//other properties like name and ID
public YourModel(int index)
{
Index = index;
}
}
And set the value of Index when init the ItemSource of ListView .
sources = new ObservableCollection<YourModel>() { };
for(int i=0;i<20;i++)
{
sources.Add(new YourModel(i) { /**other propertes**/});
}
In CustomCell
Get it like following
var model = this.BindingContext as YourModel;
int index = model.Index;
try using Button1.Parent.Parent.Parent... and so on, unless you get your listview's object.
Also pass BindingContext of viewcell in button's CommandParameter like CommandParameter={Binding} and then get the index of your received object from ItemsSource.

New Xamarin.Forms CollectionView doesn't allow multi pre-selection

I have a CollectionView in my Xamarin.Forms project:
<CollectionView ItemsSource="{Binding Categories}" ItemSizingStrategy="MeasureFirstItem" x:Name="CategoryColView"
SelectionMode="Multiple" SelectionChangedCommand="{Binding SelectionChangedCommand}"
SelectionChangedCommandParameter="{Binding Source={x:Reference CategoryColView}, Path=SelectedItems}"
SelectedItems="{Binding SelectedCategoryItems}">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout ...>
<BoxView .../>
<StackLayout ...>
<Label .../>
<Image .../>
</StackLayout>
<BoxView/>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
I included the entire XAML element, but the only important part is the SelectedItems property. It is bound to the following viewmodel implementation:
class ViewModel {
private ObservableCollection<object> selectedCategories { get; set; }
public ObservableCollection<object> SelectedCategories {
get => selectedCategories;
set {
selectedCategories = value;
OnPropertyChanged();
}
//...
ctor() {
//...
var alreadySelectedCategoryItems = alreadySelectedCategories.Select(pc => new CategoryItem { PlantCategory = pc, IsSelected = true }).Cast<object>();
SelectedCategoryItems = new ObservableCollection<object>(alreadySelectedCategoryItems);
//...
}
}
The rest of the implementation should be irrelevant. My aim is to have pre-selected values.
First: I noticed that if the T in ObservableCollection<T> is not object, everything is ignored. Just like in Microsoft's example here. If the T is e.g. of type CategoryItem, literally nothing happens, as if the ObserveableCollection were completely ignored.
Second: alreadySelectedCategoryItem contains 2 elements in debugger mode, but then the last line in the constructor throws a:
System.ArgumentOutOfRangeException
Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
Of course, since this is Xamarin.Forms and VS for Mac, the error is thrown on the Main function, not at its actual location...
Am I doing something wrong, or is CollectionView just still buggy?
The issue was that I was creating new CategoryItem instances as the pre-selected ones, which is invalid, as they weren't by default the same instances that were in the CollectionView.ItemsSource property. I should have filtered the ItemsSource instances and put them as the pre-selected ones. Like this:
var alreadySelectedCategoryItems = alreadySelectedCategories.Select(pc => new CategoryItem { PlantCategory = pc, IsSelected = true }).Cast<object>();
SelectedCategoryItems = Categories
.Where(sci =>
alreadySelectedCategoryItems.Any(alreadySelected =>
alreadySelected.PlantCategory.Id == sci.PlantCategory.Id);
So the items are selected off the ItemsSource itself and not created as new.
Although the error message was not as desired, so Xamarin.Forms team is going to fix that.
SelectedItems is read-only,you could not use like SelectedItems="{Binding SelectedCategoryItems}" in xaml
you could try to change in your behind code like:
CategoryColView.SelectedItems.Add(your selectItem 1);
CategoryColView.SelectedItems.Add(your selectItem 2);
CategoryColView.SelectedItems.Add(your selectItem 3);

How to display List<T> in List Box?

I try to display the List I create on List Box and i don't know how
I have a class in which I add a new book to List
C# code
public class Manager
{
static List<Book> lstBook = new List<Book>();
public void AddBookM(int isbn, string author, string des, string name, float price, ushort quantity, DateTime dateTime, string edition)
{
Book book = new Book(isbn, author, des, name, price, quantity,dateTime, edition, new List<string> { "" });
lstBook.Add(book);
}
}
now in XAML i want to see the books in the List in the ListBox and in the AutoSuggestBox i want to search for books in the list and that this will open up possibilities for completion
XAML code
<AutoSuggestBox Name="SBSearchBtn" HorizontalAlignment="Center" Width="500" FontSize="20" BorderBrush="Black" Header="Search" PlaceholderText="Write here!" Margin="0,90,0,0" VerticalAlignment="Top" TextChanged="SBSearchBtn_TextChanged" QuerySubmitted="SBSearchBtn_QuerySubmitted" SuggestionChosen="SBSearchBtn_SuggestionChosen"/>
<ListBox Name="SearchList" Width="500" Margin="140,194,860,400" Background="WhiteSmoke" FontSize="25"/>
For displaying the items in the ListBox you need to do the following :
Firstly, you need to define a item template for your ListBox so that you can specify how your list items will look like. For simplicity I have only bound name and author properties from Book class.
XAML
<ListBox Name="SearchList">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding author}"></TextBlock>
<TextBlock Text="{Binding name}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I would recommend using a ObservableCollection instead of just using a List, since you want to update the list items based on what user searches.
private ObservableCollection<Book> lstBook = new ObservableCollection<Book>();
Next, in the Page's Loaded event (or any event that you might want based on your requirement) set the ItemSource of the ListBox to the ObservableCollection you created (listBook).
private void Page_Loaded(object sender, RoutedEventArgs e)
{
//Setting item source of the list box
SearchList.ItemsSource = lstBook;
//Adding an entry to lstBook
AddBookM(1, "auth", "des", "name", 2.55f, 10, DateTime.Now, "edition");
}
You should now see an entry added to the list view . The advantage of using ObservableCollection in this scenario is that whenever you make changes(add/remove) any item in the ObservableCollection<Book> lstBook your UI will automatically be updated.
Thus when you call AddBookM() again, you will see that the new entry is also added to the ListBox.
Hope this helps .
You need to write it manually:
lstBook.Items.Add(book); // of course this is working only a ToString command to the Book class
You didn't say what ListBox you were talking about so I just assumed you were talking about the ListBox in the Windows Forms library.
Hoped I helped you!

Binding a List<string> to a ComboBox for display in a view (MVVM)

I am attempting to bind a list to a combobox. I want to display this list of options within the Combobox itself. (Later to allow the user to select an item 'SelectedItem', I'll cross that bridge when I get there)
MyCode.cs
// List of values for 'Type' dropdown
private static readonly List<string> MarkerTypeList = new List<string>(new string[]
{
"Analog",
"Digital"
});
// Binding for viewing list in window
public List<string> TypeOptions
{
get { return MarkerTypeList; }
}
MyCode.xaml
<ComboBox x:Name="myCombobox" HorizontalAlignment="Left" Margin="125,26,0,0" VerticalAlignment="Top" Width="70" Height="23" SelectedItem="" ItemsSource="{Binding TypeOptions}" />
The solution boiled down to changing this:
ItemsSource="{Binding TypeOptions}"
to this:
ItemsSource="{Binding Marker.TypeOptions}"
Thanks for the input, sorry you didnt have a whole lot to go on.
If you bind you should have your INotifyPropertyChanged interface added to your class which is the actual ViewModel for your ComboBox.
So - 1. Add the Interface to the class. 2. Create RaisePropertyChanged function. 3. Call the function through the setter of the property. This will push the updated value of the property through the binding and you will see the combobox populated.

C# WPF Checkbox Databinding

I'm looking for the best way to populate a check boxes from the following code. I have looked into Binding but not really sure where to go.
Here is the edited code that is working
private void dpDateSelection_SelectedDateChanged(object sender, SelectionChangedEventArgs e)
{
DateTime? date = dpDateSelection.SelectedDate;
logDate = date != null ? date.Value.ToString("yyyy-MM-dd") : null;
dpDateSelection.ToolTip = logDate;
LoadLogs(logDate);
}
private void LoadLogs(string ldate)
{
string[] logs = Directory.GetFiles(logPath + ldate, "*.ininlog");
InitializeComponent();
logList = new ObservableCollection<String>();
logList.Clear();
foreach (string logName in logs)
{
string s = logName.Substring(logName.IndexOf(ldate) + ldate.Length + 1);
int extPos = s.LastIndexOf(".");
s = s.Substring(0, extPos);
logList.Add(s);
}
this.DataContext = this;
}
<ListBox x:Name="Logs" ItemsSource="{Binding logList}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding}" ToolTip="{Binding}" Tag="{Binding}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You will want to start by using an ItemsControl instead of a StackPanel, since ItemsControls are automatically set up to display collections of things:
<ItemsControl ItemsSource="{Binding Logs}"/>
Note the use of ItemsSource. With the accompanying binding string, it basically says "Look for a property on the DataContext called "Logs" and put everything in it into this control".
Next you said you wanted this displayed as checkboxes, so we use an item template:
<ItemsControl ItemsSource="{Binding Logs}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content={Binding .}/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
This says "Use a checkbox for each Item in the ItemsSource". The DataTemplate can be a Grid or other collection control as well, so this is a really powerful feature in WPF. The "Binding ." just binds to the object itself (a string in this case, so we don't need a special path).
Finally, you need to set up a property to bind to in your view model:
ObservableCollection<String> Logs {get; set;}
You want an ObservableCollection so that when anything is added to or removed from the list, it automatically updates the UI. If you are going to be completely replacing the list (assignment), then you need to implement INotifyPropertyChanged and invoke the PropertyChanged event in that properties setter.
In your posted loop, you would add each log file to this property.
Also, make sure that somewhere you set the DataContext property of the XAML file (View) to your view model object. If everything is in code behind, use DataContext = this. Note that doing this is considered bad practice, and you should use a separate class (ViewModel).
You didn't mention what you wanted the CheckBoxes to do, so I haven't included anything related to that in my answer. You will likely want to abstract your logs into an object with a "Selected" property you can then bind the IsChecked property of the CheckBoxes to.
Obviously this is a lot to take in, please let me know if I can clarify anything or help further!
Update
You put the property in your ViewModel (DataContext). Whatever class that is, you write:
ObservableCollection<String> Logs {get; set;}
private void LoadLogs()
{
string[] logs = Directory.GetFiles(logPath + logDate, "*.ininlog");
foreach(string logName in logs)
{
string s = logName.Substring(logName.IndexOf(logDate) + logDate.Length + 1);
int extPos = s.LastIndexOf(".");
s = s.Substring(0, extPos);
//MessageBox.Show(s);
Logs.Add(s); //Add the parsed file name to the list
}
}

Categories

Resources