Why is Picker control not displaying items populated to it? - c#

I have a Picker which i have populated with object data from the model. The challenge am facing is that when i tap on the picker to select an item, the list is empty. And also when i specify the ItemsDisplayBinding to one of the model properties. The items are displayed with weird names in the form of app-name.Model for example if app name is cool App, items are displayed as cool-app.Model.ModelName...
What could i be doing wrong? This is my code please.
// xaml file
<ContentPage
xmlns:model="clr-namespace:SoftFashions.Model"
xmlns:viewmodel="clr-namespace:SoftFashions.ViewModel"
x:DataType="viewmodel:ShoppingBagViewModel">
<Grid RowDefinitions="Auto,200,*"
RowSpacing="0"
ColumnDefinitions="*,*">
<CollectionView
ItemsSource="{Binding ShoppingBagCollection}"
Grid.Row="1"
Grid.ColumnSpan="2"
EmptyView="NO ITEMS ADDED">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="model:Cloth">
<!-- controls displaying shoppingBagCollection data-->
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<Grid
Grid.ColumnSpan="2"
Grid.Row="2"
Padding="0"
Grid.ColumnDefinitions="*,*"
Grid.RowDefinitions="*,*,*,*"
ColumnSpacing="0"
HorizontalOptions="FillAndExpand"
Margin="20">
<VerticalStackLayout
Grid.RowSpan="4"
Grid.Column="0">
<Label Grid.Row="0"
Text="LOCATION"
TextColor="{StaticResource Primary}"
Style="{StaticResource MediumLabel}"
FontAttributes="Bold"/>
<!-- picker to display items from the PickupStations collection-->
<Picker
x:Name="picker"
TextColor="Black"
Grid.Row="1"
Title="SELECT LOCATION"
FontAttributes="Italic"
ItemsSource="{Binding PickupStations}"
ItemDisplayBinding="{Binding Location}"
SelectedItem="{Binding SelectedItem}"
/>
<!-- entry to display text depending on selected item from the picker-->
<Entry x:Name="shippingfeeLabel"
Grid.Row="2"
Text="{Binding SelectedItem.Shippingfee}"
WidthRequest="120"
HorizontalOptions="Start"
Placeholder="$0.00"
Margin="0,15,0,0"
IsReadOnly="True"/>
<Entry Grid.Row="2"
Text="shipping fee"
IsReadOnly="True"
Margin="0,0,0,0"/>
</VerticalStackLayout>
</Grid>
</ContentPage>
// view model
/*variables*/
[ObservableProperty]
Pickupstations selectedItem;
public ObservableCollection<Pickupstations> PickupStations { get; } = new();
ClothService clothService;
//Constructor
public ShoppingBagViewModel()
{
this.clothService = new();
}
// getting pickup stations from the service
[RelayCommand]
public async Task<ObservableCollection<Pickupstations>> DisplayPickupstationsAsync()
{
try
{
var pickuplists = await clothService.GetPickupstations();
if (PickupStations.Count > 0)
PickupStations.Clear();
foreach (var pickuplist in pickuplists)
{
PickupStations.Add(pickuplist);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
return PickupStations;
}
}
// Model
public class Pickupstations
{
public string Name { get; }
public string Location { get; }
public string Shippingfee { get; }
}
Ofcourse the service fetches data from the internet from where the viewmodel picks up the data
please help me out with this...Many thanks

You can directly assign the PickupStations via the ClothService in the ViewModel constructor. I write a pseudo ClothService.cs to generate the data source of the picker and extract the key element for your reference below:
Xaml:
<Picker
x:Name="picker"
TextColor="Black"
Title="SELECT LOCATION"
FontAttributes="Italic"
ItemsSource="{Binding PickupStations}"
ItemDisplayBinding="{Binding Location}"
SelectedItem="{Binding SelectedItem}"/>
ViewModel:
public partial class MainViewModel : ObservableObject
{
[ObservableProperty]
Pickupstations selectedItem;
public ObservableCollection<Pickupstations> PickupStations { get; } = new();
public MainViewModel()
{
PickupStations = clothService.GetPickstations();
}
}
ClothService.cs:
public class clothService
{
public static ObservableCollection<Pickupstations> GetPickstations()
{
var locations = new ObservableCollection<Pickupstations>()
{
new Pickupstations{Name="Bob", Location="Italy"},
new Pickupstations{Name="Rock", Location="Greek"},
new Pickupstations{Name="John", Location="Argetina"},
};
return locations;
}
}
Model:
public class Pickupstations
{
public string Name { get; set; }
public string Location { get; set; }
}

Related

Xamarin Forms TapGestureRecognizer Is not issuing command

So I have a xamarin forms app that currently only implemented for android. I am attempting to implement a tap event. When tapped though this never hits the command in the ViewModel. I'm not sure if I have something the matter with my code or I am just implementing it wrong.
ViewModel Code:
private RelayCommand<object> _OnClickableLabel;
public RelayCommand<object> OnClickableLabel
{
get { return _OnClickableLabel ?? (_OnClickableLabel = new RelayCommand<object>((currentObject) => Test(currentObject))); }
}
private void Test(object currentObject)
{
Application.Current.MainPage.DisplayAlert("Alert", "were going down cap", "OK");
}
Page Xaml:
<ListView Grid.Row="1" ItemsSource="{Binding Notifications}" RowHeight="100">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Vertical" BackgroundColor="{Binding BackgroundColor}">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding OnClickableLabel }" />
</StackLayout.GestureRecognizers>
<Label FontSize="Large" Text="{Binding Title}"></Label>
<Label FontSize="Small" Text="{Binding Text}"></Label>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I have tested it using a method in the page's cs code and that works fine but it has to be implemented in the ViewModel because it affects that data.
From your description, you want to add a tap gesture recognizer in ListView, and want to pass ListView current row data to TapGestureRecognizer event, am I right?
If yes, as Jason's opinion, you need to take a look Xamarin.Forms Relative Bindings firstly,name ListView as listview1, then take a look the following code:
<ListView
x:Name="listview1"
Grid.Row="1"
ItemsSource="{Binding Notifications}"
RowHeight="100">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout BackgroundColor="{Binding BackgroundColor}" Orientation="Vertical">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding BindingContext.OnClickableLabel, Source={x:Reference listview1}}" CommandParameter="{Binding .}" />
</StackLayout.GestureRecognizers>
<Label FontSize="Large" Text="{Binding Title}" />
<Label FontSize="Small" Text="{Binding Text}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
public partial class Page16 : ContentPage
{
public ObservableCollection<Notclass> Notifications { get; set; }
public ICommand OnClickableLabel { get; set; }
public Page16()
{
InitializeComponent();
Notifications = new ObservableCollection<Notclass>()
{
new Notclass(){Title="title 1",Text="notification 1"},
new Notclass(){Title="title 2",Text="notification 2"},
new Notclass(){Title="title 3",Text="notification 3"},
new Notclass(){Title="title 4",Text="notification 4"},
new Notclass(){Title="title 5",Text="notification 5"}
};
OnClickableLabel = new Command(n=> {
var vm = (Notclass)n;
Application.Current.MainPage.DisplayAlert("Alert",vm.Title , "OK");
});
this.BindingContext = this;
}
}
public class Notclass
{
public string Title { get; set; }
public string Text { get; set; }
public Color BackgroundColor { get; set; } = Color.White;
}

Changing Data in CollectionView (Xamarin)

I use CollectionView to show data on screen, but when I change data, UI is not changing, although I am using OnPropertyChanged. Here is the code:
Xaml
<CollectionView ItemsSource="{Binding GridData}" Margin="15">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Margin="15" Padding="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0"
Grid.Column="0"
HorizontalTextAlignment="Start"
Text="{Binding Title}"
FontSize="Small"/>
<Label Grid.Row="0"
Grid.Column="1"
HorizontalTextAlignment="End"
Text="{Binding Data}"
TextColor="Black"
FontSize="Medium">
<Label.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding Source={x:Reference Page} , Path=BindingContext.TapCommand}"
CommandParameter="{Binding Title}" />
</Label.GestureRecognizers>
</Label>
<BoxView Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
BackgroundColor="LightGray"
CornerRadius="2"
HorizontalOptions="FillAndExpand"
HeightRequest="1"></BoxView>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
ViewModel
private List<CollectionEntity> _gridData;
public List<CollectionEntity> GridData
{
get => _gridData;
set
{
if (_gridData != value)
{
_gridData = value;
OnPropertyChanged(nameof(GridData));
}
}
}
public ICommand TapCommand
{
get
{
return new Command<CollectionView>((commandParameters) =>
{
OpenEditing(commandParameters.ToString());
OnPropertyChanged(nameof(GridData));
});
}
}
Model (is in the same file, as is ViewModel)
public class CollectionEntity: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public string Title { get; set; }
public string Data { get; set; }
}
So, when I tap on the Label, UI does not react. I tried to write it according to this answer, but cannot understand, what is incorrect.
UPD: new command
public ICommand TapCommand => new Command<object>((commandParameters) =>
{
OpenEditing(commandParameters.ToString()); // changing data
});
Though you had write the code about INotifyPropertyChanged in your model but you didn't implement it on the property Title and Data . Modify the code like following
private string title;
public string Title
{
get => title;
set
{
if (title!= value)
{
title = value;
OnPropertyChanged(nameof(Title));
}
}
}
private string data;
public string Data
{
get => data;
set
{
if (data!= value)
{
data= value;
OnPropertyChanged(nameof(Data));
}
}
}
In addition, the code in TapCommand seems will not change the value of source . You could binding the whole model to the command and set the title or data in command as you want .
CommandParameter="{Binding .}"
public ICommand TapCommand
{
get
{
return new Command<CollectionView>((arg) =>
{
var model = arg as CollectionEntity;
// model.Title = "xxx";
});
}
}

how binding textcolor in GroupHeaderTemplate label xamarin

it's possible to binding TextColor property label in GroupHeaderTemplate?
this is my source code:
<ListView.GroupHeaderTemplate>
<DataTemplate>
<ViewCell Height="25">
<StackLayout VerticalOptions="FillAndExpand" Padding="5"
BackgroundColor="#0477B3">
<Label Text="{Binding Key, Converter={StaticResource dateConverter}}"
TextColor="#ffffff" VerticalOptions="Center"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.GroupHeaderTemplate>
br
Max
The BindingContext for a group is the grouping itself. Without knowing more about your code here is an example of how to bind to the grouping object in the group header template:
<ListView ItemsSource="{Binding}"
IsGroupingEnabled="true" GroupDisplayBinding="{ Binding Name }"
GroupShortNameBinding ="{ Binding ShortName }">
<ListView.GroupHeaderTemplate>
<DataTemplate>
<ViewCell Height="25">
<StackLayout VerticalOptions="FillAndExpand" Padding="5"
BackgroundColor="#0477B3">
<!-- Name refers to Group.Name, depeding on your data you can use the key of your grouping here -->
<Label Text="{Binding Name}" VerticalOptions="Center"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.GroupHeaderTemplate>
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{ Binding Title }" Detail="{ Binding Description }" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
And the code:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
ObservableCollection<Group> groupedItems = new ObservableCollection<Group>();
Group group = new Group("First Group");
groupedItems.Add(group);
Item item = new Item("First Item", "First Item Description");
group.Add(item);
this.BindingContext = groupedItems;
}
}
public class Item
{
public String Title { get; private set; }
public String Description { get; private set; }
public Item(String title, String description)
{
Title = title;
Description = description;
}
}
public class Group : ObservableCollection<Item>
{
public String Name { get; private set; }
public Group(String Name)
{
this.Name = Name;
}
}
Example taken and modified from here

CarouselView with a different template for each slide in Xamarin forms

I need to make a CarouselView in Xamarin forms where every slide has a specific template.
Currently I have done so:
XAML:
xmlns:control="clr-namespace:Xamarin.Forms;assembly=Xamarin.Forms.CarouselView"
.......
<ContentView HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<control:CarouselView x:Name="carouselView">
<control:CarouselView.ItemTemplate>
<DataTemplate>
<Label Text="{Binding Testo}" />
</DataTemplate>
</control:CarouselView.ItemTemplate>
</control:CarouselView>
</ContentView>
CODEBEHIND :
List<CustomCell> myCarousel = new List<CustomCell>();
myCarousel.Add(new CustomCell { Testo = "ciao" });
myCarousel.Add(new CustomCell { Testo = "ciao due" });
carouselView.ItemsSource = myCarousel;
CustomCell:
public class CustomCell
{
public string Testo { get; set; }
}
All this works, my problem is that I'd have a different template for each slide, for example, a grid different graphically each slide, this is because I have to display data differently graphically speaking.
Can you recommend a solution? Thank you
You can use a data template selector to customize the look of different items in the CarouselView. A simple example:
MyDataTemplateSelector.cs
public class MyDataTemplateSelector : DataTemplateSelector
{
public DataTemplate SimpleTemplate { get; set; }
public DataTemplate ComplexTemplate { get; set; }
public MyDataTemplateSelector()
{
SimpleTemplate = new DataTemplate(typeof(SimpleView));
ComplexTemplate = new DataTemplate(typeof(ComplexView));
}
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
CustomCell cell = (CustomCell)item;
if (cell.Testo.Length > 5) {
return ComplexTemplate;
} else {
return SimpleTemplate;
}
}
}
SimpleView.xaml
<ContentView>
<StackLayout BackgroundColor="Red">
<Label Text="{Binding Testo}" VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand" />
</StackLayout>
</ContentView>
ComplexView.xaml
<ContentView>
<StackLayout BackgroundColor="Yellow" >
<Label Text="{Binding Testo}" VerticalOptions="CenterAndExpand" HorizontalOptions="Center" />
<Label Text="I lied about this being complex" />
</StackLayout>
</ContentView>
And in the page where your CarouselView is:
<ContentPage.Resources>
<ResourceDictionary>
<local:MyDataTemplateSelector x:Key="templateSelector"></local:MyDataTemplateSelector>
</ResourceDictionary>
</ContentPage.Resources>
....
<control:CarouselView x:Name="carouselView" ItemTemplate="{StaticResource templateSelector}" />

How to bind the ViewModel for a windows phone 8 messaging app

My model:
public class MyMessageModel
{
public string DisplaySender { get; set; }
//how does the below observable collection needs to be changed ,
//if I want to add another field to itemssource template.
//e.g. public DateTime Timestamp { get; set; }
public ObservableCollection<string> MessagesExchanged { get; set; }
public string NewMessage { get; set; }
}
Chat.xaml:
<TextBlock Name="lblFromUserName" Text="{Binding DisplaySender ,Mode=TwoWay}" Height="65" Style="{StaticResource PhoneTextNormalStyle}" FontSize="35"/>
<ItemsControl ItemsSource="{Binding Path=MessagesExchanged}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ????,Mode=TwoWay}" />
<TextBlock Text="{Binding Path=Timestamp}" HorizontalAlignment="Right" VerticalAlignment="Bottom" Grid.Row="1"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl
<StackPanel Orientation="Horizontal" Grid.Row="1">
<TextBox Grid.Column="0" Name="txtNewMessage" Text="{Binding NewMessage,Mode=TwoWay}" Margin="0,0,0,0" Width="350"/>
<Button Grid.Column="1" Command="{Binding SendClickCommand,Mode=TwoWay}" Name="btnSend" Content="Send" Width="100" />
</StackPanel>
Chat.xaml.cs looks like below:
public class Chat: PhoneApplicationPage
{
private MyMessageViewModel _MyMessageViewModel;
public Conversation()
{
InitializeComponent();
_MyMessageViewModel = new MyMessageViewModel();
this.DataContext = _MyMessageViewModel;
}
}
My ViewModel MyMessageViewModel looks like below:
public System.Windows.Input.ICommand SendClickCommand
{
get
{
return new DelegateCommand((o) =>
{
Task.Factory.StartNew(() =>
{
//loop through the selected items and clear everything
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
try
{
//DO YOUR WORK HERE: TAKE THE NEW MESSAGE AND APPEND IT TO THE MESSAGES EXCHANGED
}
catch (Exception)
{
throw;
}
});
});
});
}
}
Now when user is in the above view called Chat.xaml (user will come to this page from Home page) i want to load it with the DisplaySender value on the top which will be fixed during the entire conversation.Value for this field be passed as navigation parameter from the home page.
And everytime user clicks on the Send button , in the SendClickCommand only update the MessagesExchanged collection by adding the new message from the txtNewMessage field and later clear this field.
I have two questions here:
When user first comes to the Chat.xaml how do i bind the data for the three fields e.g. DisplaySender(non empty value will be passed as navigation parameter ),MessagesExchanged(initially this would be empty when initiating the new conversation, otherwise it will have a non empty value from the navigation parameter) and NewMessage (initially this would be empty always).
Secondly in SendClickCommand notified property how do i take the text from txtNewMessage and update the ObservableCollection MessagesExchanged and at the end clear the value of txtNewMessage .And how to bind the values of MessagesExchanged to the datatemplate textblock field ?
I guess you are trying to pass Object of Class MyMessageModel while navigating from HomePage to ChatPage.
So define a property
private MyMessageModel currentMessageModel;
public MyMessageModel CurrentMessageModel
{
get { return currentMessageModel; }
set { currentMessageModel = value; }
}
and in OnNavigatedTo method of ChatPage set
CurrentMessageModel=PassedObjectOfMessageModel
xaml:
<TextBlock Name="lblFromUserName" Text="{Binding CurrentMessageModel.DisplaySender ,Mode=TwoWay}" Height="65" Style="{StaticResource PhoneTextNormalStyle}" FontSize="35"/>
<ItemsControl ItemsSource="{Binding Path=CurrentMessageModel.MessagesExchanged}">
//No need for data template as collection only contains string
<ItemsControl
<StackPanel Orientation="Horizontal" Grid.Row="1">
<TextBox Grid.Column="0" Name="txtNewMessage" Text="{Binding NewMessage,Mode=TwoWay}" Margin="0,0,0,0" Width="350"/>
<Button Grid.Column="1" Command="{Binding SendClickCommand,Mode=TwoWay}" Name="btnSend" Content="Send" Width="100" />
</StackPanel>
//C#
CurrentMessageModel.MessagesExchanged.Add(txtNewMessage.Text);
and you donot need any text block to show the ObservableCollection as your collection only contains string so just by setting the ItemsSource to the collection the data would be displayed.

Categories