Xamarin Forms Image not showing in listview - c#

I want to display a /!\ icon in my listview. Unfortunately, it's not showing up anywhere.
I am 100% positive it's in the right place in both the iOS and Android app. I have double checked that it is a Android resource (as per screenshot below)
The code used to initialize the image: alarmIcon = new Image { Source = "warning.png" };
After initialization I put it in an AlarmPageItem object which as an Image property. This gets added to the listview.
I have checked that I matched the bindings with the AlarmPageItem properties. (The text does get shown).
I am at a loss why it wouldn't work...
Things I tried
Checked case of image and source declaration
Checked the build action and copy to output directory
Checked binding matching
Tried building to phone instead of xamarin live player
Checked image location
Code:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="OSIApp.Pages.AlarmPage"
Title="Alarm Log">
<ContentPage.Content>
<StackLayout>
<ListView x:Name="listView"
ItemTapped="OnItemTapped" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<StackLayout>
<Label x:Name="alarmTimeLabel"
HorizontalOptions="Center"
HorizontalTextAlignment="Center"
Text="{Binding DateTime}"/>
<Image x:Name="alarmIconImage"
Source="{Binding AlarmImage}"/>
</StackLayout>
<Label x:Name="alarmTextLabel"
FontSize="14"
VerticalOptions="FillAndExpand"
VerticalTextAlignment="Center"
HorizontalOptions="FillAndExpand"
FontFamily="Avenir Next"
FontAttributes="Bold"
Text="{Binding AlarmText}"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
public partial class AlarmPage : ContentPage
{
private List<AlarmPageItem> listViewItems;
private Image alarmIcon;
public AlarmPage ()
{
listViewItems = new List<AlarmPageItem>();
alarmIcon = new Image { Source = "warning.png" };
PopulateList();
InitializeComponent();
listView.ItemsSource = listViewItems;
}
private void OnItemTapped(object sender, ItemTappedEventArgs e)
{
if (e == null) return; // has been set to null, do not 'process' tapped event
Debug.WriteLine("Tapped: " + e.Item);
((ListView)sender).SelectedItem = null; // de-select the row
}
private void PopulateList()
{
listViewItems.Add(new AlarmPageItem() {AlarmImage = alarmIcon, AlarmText = "The front is too heavy", DateTime = DateTime.Now.ToShortTimeString()});
}
}

You are binding as "Source" of your Image in the XAML an Image object you are creating in your code behind.
Update your AlarmPageItem class by making the AlarmImage property either an ImageSource or just a simple string.
Something like:
class AlarmPageItem
{
public string AlarmImage { get; set; }
public string AlarmText { get; set; }
//Just a suggestion: Change this property name for something different as it
//can create some confusion.
public DateTime DateTime { get; set; }
...
// Add any other properties your class might have.
}
Once the above is updated just change your code to set the name of the image in the PopulateList method
private void PopulateList()
{
listViewItems.Add(new AlarmPageItem() {AlarmImage = "warning.png", AlarmText = "The front is too heavy", DateTime = DateTime.Now.ToShortTimeString()});
}
Hope this helps.-

Related

Collection's View Label pass to modal upon tapping the collection view item

My problem is I want to pass the label in my collection view to a modal page upon tapping a collection view items.
this is my homepage.xml.cs code:
`
async private void TapGestureRecognizer_Tapped(object sender, EventArgs e)
{
await PopupNavigation.Instance.PushAsync(OrderPageModal);
}
This is my homepage.xml
<StackLayout
Style="{StaticResource itemsInsideCardViewStackLayoutStyle}">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped"/>
</StackLayout.GestureRecognizers>
<Frame
Style="{StaticResource stationImageFrameStyle}">
<Image
Style="{StaticResource stationImageStyle}"
Source="{Binding ImageURL}" />
</Frame>
<StackLayout
Style="{StaticResource detailsStackLayout}">
<Label
Text="{Binding storename}"
Style="{StaticResource NameLabel}"
HorizontalOptions="CenterAndExpand"
Margin="-40,0,0,0"/>
`
that Text="{Binding storename}" is I get here;( in one file,I just put together my station/store model and the IEnumerable Get().
`
public static IEnumerable<WRSinfo> Get()
{
return new List<WRSinfo>
{
new WRSinfo()
{
storename="Aqua Refilling Station", status="Open", ImageURL="water_ref.png", distance="2km"
},
};
}
public string storename { get; set; }
public string distance { get; set; }
public string status { get; set; }
`
this is my popupmodal page design:
`
<StackLayout BackgroundColor="Green"
HorizontalOptions="Center"
VerticalOptions="Start"
WidthRequest="100">
<Label Text="{Binding storename}" FontSize="20" TextColor="Black" x:Name="lblTextStorname" />
</StackLayout>
`
So what ouput I expect is. If I click collection view item which a name of store, then upon clicking it, a popupmodal will appear then also the name of the store I clicked from collectionview item.
Im newbie to this project and tech, pls help me sir. All comment will be appreciated.
output should be like this. enter image description here
According to the picture you provide, I recommend you to use the Xamarin.Forms Shell navigation.
You can change the TapGestureRecognizer_Tapped method to pass the Name of the WRSinfo to the SecondPage.
async private void TapGestureRecognizer_Tapped(object sender, EventArgs e)
{
string WRSinfoName = (e.CurrentSelection.FirstOrDefault() as WRSinfo).Name;
await Shell.Current.GoToAsync($"SecondPage?name={WRSinfoName}");
}
Then in the SecondPage.xmal.cs you should use the code [QueryProperty(nameof(Name), "name")] to accept the data.
[QueryProperty(nameof(Name), "name")]
public partial class SecondPage : ContentPage
{
public string Name
{
set
{
LoadWRSinfo(value);
}
}
void LoadWRSinfo(string name)
{
try
{
WRSinfo wRSinfo = WRSinfoData.WRSinfo.FirstOrDefault(a => a.storename== name);
BindingContext = wRSinfo;
}
catch (Exception)
{
Console.WriteLine("Failed to load. ");
}
}
}
This is a sample you can refer to. I am not clear about the data structure you use. So you can follow the sample here the Microsoft provided Download the sample.

ListView not deleting items in Xamarin.Forms. I have assigned an ObservableCollection to the ListView itemsource. MVVM

In my Xamarin.Forms app I have a simple Contact class [Model]. In the UI [View] there exists a ListView that displays the Contacts. In my model view class I have a list of Contacts (_listOfContacts) that is assigned to the itemSource property of the ListView. This list of Contacts is an ObservableCollection. My issue is the when user clicks Delete from ContextActions I can see that the _listOfContacts is updated but the ListView is not.
The ListView is only updated when I reassign its itemsource to the _listOfContacts. This should not be needed if _listOfContacts is an ObservableCollection of Contacts.
I am new to MVVM so I need to clear these basic MVVM concepts before I go on to learn more advanced techniques.
Here is my code:
Model
class Contact
{
public String Name { get; set; }
public String Status { get; set; }
public String ImageUrl { get; set; }
}
Model View
public partial class ContactListPage : ContentPage
{
private ObservableCollection<Contact> _listOfContacts;
public ContactListPage()
{
InitializeComponent();
_listOfContacts = new ObservableCollection<Contact>
{
new Contact {Name="Item1", ImageUrl="http://lorempixel.com/100/100/people/1" , Status="Hey"},
new Contact { Name = "Item2", ImageUrl = "http://lorempixel.com/100/100/people/2", Status="Hey" },
};
contactList.ItemsSource = _listOfContacts.ToList();
}
private void EditContactClick(object sender, EventArgs e)
{
DisplayAlert("Alert", "Clicked Edit", "Cancel");
}
private void DeleteContactClick(object sender, EventArgs e)
{
var contact = (sender as MenuItem).CommandParameter as Contact;
_listOfContacts.Remove(contact);
//following line of code should not be needed since _listOfContacts is
//an ObservableCollection and removing an item should update the bound control automatically
**contactList.ItemsSource = _listOfContacts.ToList();**
}
}
View
<ContentPage.Content>
<StackLayout>
<ListView x:Name="contactList" HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal" Padding="10">
<Image Source="{Binding ImageUrl}"/>
<StackLayout HorizontalOptions="StartAndExpand">
<Label Text="{Binding Name}" Margin="0,2,0,2"/>
<Label Text="{Binding Status}" Margin="0,2,0,2" />
</StackLayout>
</StackLayout>
<ViewCell.ContextActions>
<MenuItem Text="Edit" Clicked="EditContactClick" CommandParameter="{Binding .}"/>
<MenuItem Text="Delete" Clicked="DeleteContactClick" IsDestructive="True" CommandParameter="{Binding .}"/>
</ViewCell.ContextActions>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
I have tested your code, it is just the method ToList()which caused this question:
contactList.ItemsSource = _listOfContacts.ToList();
At the beginning, the type of _listOfContacts is ObservableCollection, but when you use the method ToList(),then it will been converted to List again.
So just delete the method 'ToList()', your code will work properly, just as follows:
contactList.ItemsSource = _listOfContacts;
Remove .toList() from contactList.ItemsSource = _listOfContacts.ToList(); and try again.
_listOfContacts is an ObservableCollection which should be used as your ItemsSource directly. Maybe go and check out the ObservableCollection documentation.

Xamarin.forms MVVM. Listview remains empty

I'm very new to Xamarin.Forms and MVVM and posting questions here on StackOverflow so bear with me please. I'm trying to fill a listview in Xamarin.Forms. I first programmed it without MVVM and it all worked like I wanted it to, but now I wanted to get in in MVVM and that is where it went wrong, now my list won't fill up anymore.
I made a viewmodel and only put all the binding in the viewmodel, I have not yet implemented eventhandlers.
This is a part of the code behind (I have a couple more eventhandlers but that is not relevant right now):
namespace CCXamarinApp
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class PatientsWithTagPage : ContentPage
{
public PatientsWithTagPage()
{
BindingContext = new PatientsWithTagViewModel();
InitializeComponent();
(BindingContext as PatientsWithTagViewModel).GetAllPatients();
if((BindingContext as PatientsWithTagViewModel).IsEmptyPatientList)
HandleEmptyList();
else
(BindingContext as PatientsWithTagViewModel).SortAndShowPatients();
}
private void SearchBar_OnTextChanged(object sender, TextChangedEventArgs e)
{
(BindingContext as PatientsWithTagViewModel).Searching(e.NewTextValue);
}
...
This is my XAML page:
<SearchBar x:Name="SearchBar" Placeholder="Zoek op naam of plaats..." HeightRequest="25" Margin="10"
TextChanged="SearchBar_OnTextChanged"/>
<Label Text="{Binding LastRefreshed}" FontAttributes="Italic" FontSize="15" />
<Label x:Name="LabelEmptyList" FontSize="17" Text="Geen gegevens gevonden" FontAttributes="Bold"
IsVisible="False" />
<ListView x:Name="PatientListView" HasUnevenRows="True" SeparatorColor="Accent"
VerticalOptions="FillAndExpand" IsPullToRefreshEnabled="True" IsRefreshing="{Binding IsFetchingData, Mode=TwoWay}"
Refreshing="PatientListView_OnRefreshing" SelectedItem="{Binding SelectedPatient, Mode=TwoWay}"
ItemSelected="PatientListView_OnItemSelected" ItemsSource="{Binding Patients}"
IsVisible="True" BackgroundColor="Aqua">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Spacing="4">
<StackLayout Orientation="Horizontal" Margin="10,7,10,1">
<Label Text="{Binding FullName}" FontAttributes="Bold" FontSize="16" />
<Label Text="{Binding DisplayTimeOfLastScan, StringFormat='{0}'}"
HorizontalOptions="EndAndExpand" />
</StackLayout>
<StackLayout Orientation="Horizontal" Margin="10,0,10,7">
<Label Text="{Binding LastLocation}" HorizontalOptions="Start" />
<Label Text="{Binding DisplayDurationSinceLastScan, StringFormat='al {0}'}"
HorizontalOptions="EndAndExpand" />
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
This is my viewmodel (not all the code but the code that is most relevant). The BaseViewModel it derives from is from the nuget package "Refractored.Mvvmhelpers":
class PatientsWithTagViewModel : BaseViewModel
{
public ObservableCollection<PatientViewModel> Patients { get; private set; } = new ObservableCollection<PatientViewModel>();
private PatientViewModel selectedPatient;
public PatientViewModel SelectedPatient
{
get => selectedPatient;
set => SetProperty(ref selectedPatient, value);
}
private readonly JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings
{
DateFormatString = "dd-MM-yyyTH:mm",
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
};
public bool IsEmptyPatientList => Patients.Count == 0;
private string testJson = "[{\"firstName\":\"P.\",\"lastName\":\"Selie\",\"tag\":{\"tagId\":\"124\",\"tagSerialNumber\":\"ABC135\"},\"scans\":[{\"location\":\"Tuin\",\"dateTime\":\"May01,2018,10:10\"},{\"location\":\"Eetzaal\",\"dateTime\":\"May02,2018,10:15\"},{\"location\":\"Gang\",\"dateTime\":\"May02,2018,11:10\"},{\"location\":\"Kamer23\",\"dateTime\":\"May02,2018,12:09\"}],\"id\":\"dcc4fe9929b3681f\"}," +
"{\"firstName\":\"W.\",\"lastName\":\"Janssens\",\"tag\":{\"tagId\":\"132\",\"tagSerialNumber\":\"ABC167\"},\"scans\":[{\"location\":\"Kamer23\",\"dateTime\":\"May01,2018,23:39\"},{\"location\":\"Gang\",\"dateTime\":\"May02,2018,04:10\"},{\"location\":\"Eetzaal\",\"dateTime\":\"May02,2018,04:11\"},{\"location\":\"Gang\",\"dateTime\":\"May02,2018,04:20\"},{\"location\":\"Kamer23\",\"dateTime\":\"May02,2018,04:22\"}],\"id\":\"a6dac28475327922\"}]";
public void GetAllPatients()
{
IsFetchingData = true;
try
{
Patients = new ObservableCollection<PatientViewModel>(
JsonConvert.DeserializeObject<ObservableCollection<PatientViewModel>>(testJson,
jsonSerializerSettings));
}
catch(Exception e)
{
Console.WriteLine("*****ERROR kon API niet ophalen");
Console.WriteLine(e.Message);
}
finally
{
IsFetchingData = false;
}
}
This is the model I use:
public class Patient : ObservableObject
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Tag Tag { get; set; }
public List<Scan> Scans { get; set; }
public string Id { get; set; }
public override string ToString()
{
return LastName + ", " + FirstName;
}
}
It also has its own viewmodel with properties like DisplayDurationSinceLastScan, but I don't think it is relevant here, if you think it is, let me know.
So with this code I get my page but there seems to be no items in the list, if I debug, Patients is filled with items so it is not empty at all, but something goes wrong with the binding I guess but no error is given.
Here is a picture of what I get: the listview is shown (I added a blue background so I would know if the listview was visible or not), but there are no items in there. Still Patients is filled when I debug the app.
Does someone see the mistake I made?
I see some issues with your code here from a maintainability point of view. Try to keep all your code inside the view model instead of both the view model and code behind. Ideally, your code behind contains nothing and if anything it strictly has to do with visual things.
Anyway, in regard to you problem: you are creating a new ObservableCollection each time. That breaks the binding. Just keep the new ObservableCollection at the top and then when new data comes in, clear that and repopulate it. Like this:
public void GetAllPatients()
{
IsFetchingData = true;
try
{
var resultPatients = JsonConvert.DeserializeObject<ObservableCollection<PatientViewModel>>(testJson, jsonSerializerSettings);
Patients.Clear();
foreach (var patient in resultPatients)
Patients.Add(patient);
}
catch(Exception e)
{
Console.WriteLine("*****ERROR kon API niet ophalen");
Console.WriteLine(e.Message);
}
finally
{
IsFetchingData = false;
}
}
Your data should now show up.

Changing a ViewCell element on click

I have built a List<> of item objects, which are being used as the ItemsSource to a ListView. on my ListView's ItemSelected event, I am trying to change one of the elements of that particular item. The original values are bound from the object.
Please consider the following example:
itemClass.cs
class itemClass
{
public itemClass()
{
}
public string valueIWantToChange {get; set;}
}
MyPage.cs
public MyPage()
{
InitializeComponent();
List<itemClass> listOfItems= new List<itemClass>();
itemClass item1= new itemClass { valueIWantToChange = "UnClicked"};
listOfItems.Add(item1);
BindingContext = this;
lstView.ItemsSource = listOfItems;
lstView.ItemSelected += (sender, e) =>
{
if (e.SelectedItem == null)
{
return;
}
// The below does nothing but highlights what I am trying to achieve
((itemClass)((ListView)sender).SelectedItem).valueIWantToChange = "Clicked" ;
((ListView)sender).SelectedItem = null;
};
}
My XAML
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:myProject="clr-namespace:myProject"
x:Class="myProject.MyPage">
<StackLayout>
<ListView x:Name="lstView">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Padding="0" Orientation="Vertical" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<Label Text="{Binding valueIWantToChange }" TextColor="Black" FontSize="16" VerticalOptions="Center" HorizontalOptions="Center" Margin="5,5,5,5"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
I have attempted to use Binding commands and PropertyChangedEventHandler but with no success. Any assistance in this would be greatly appreciated.
Edit
I have attempted to use PropertyChangedEventHandlers in a similar way, elsewhere in the app, please see below:
public double FontXLarge
{
set
{
someFontsize = value;
OnPropertyChanged("FontXLarge");
}
get
{
someFontsize = scalableFont(10);
return someFontsize;
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
The above allows me to bind a fontsize to that scaling font, however if I were to use something similar for string, would it not effect all of the items in the ListView?
I think the best way to implement what you want, it's to use a ViewModel with INotifyPropertyChanged. You can find a lot of example if you're googling a bit. One good link for generic baseclass is this.
Let me know if you find everything you need.

Load an image in a listview from URI xamarin forms

ok, so I have this listView which is supposed to show a bunch of items, each of which containing at least one photo.
The idea is to show the main photo in the listCell, and when an item is selected, its details are shown in a different Forms Page, and there its supposed to be able to access all its photos.
When the item doesn't have a photo, it will show a placeholder one from resources.
Problem: can't load the image from URI either binding the image source to a list property (from the viewModel) that contains the specific URI obj, or by binding it to the same property containing now strings, or by means of
<Image.Source>
<UriImageSource Uri="{Binding MainPhotoSource}" />
</Image.Source>
no matter. none of these seems to work.
already asked the Xamarin Team for help, and their answer was to come here, or go to the forums (which I already did, been waiting for almost two months, now, and the work needs to be delivered)...
any help, please?
EDIT:
Here's a piece of the ViewModel code.
In this first method, for each item I receive from the WCF, I add an equivalence in the format of this ItemDto obj to this ObservableCollection List.
// Sets this List observable collection to a new ItemDto obj,
// with certain properties from the Item.Find result[].
ObservableCollection<ItemDto> SetList(Item.Find[] result)
{
ObservableCollection<ItemDto> collection = new ObservableCollection<ItemDto>();
foreach (Item.Find item in result)
{
collection.Add(GetDto(item));
}
return collection;
}
// Gets a new ItemDto obj with the required fields from Item.Find obj.
ItemDto GetDto(Item.Find item)
{
return new ItemDto()
{
ID = item.ID,
UserID = item.User_ID,
MainPhotoSource = new Uri(_serverInbox + item.MediaItems[0].RelativeUrl),
Title = item.Title,
Description = item.Description,
Category = item.Category_Name,
DateCreated = GetFormatedDateCreated(item.DateCreated)
};
}
Uri property of UriImageSource requires an Uri rather than a string. But you can use a URI Bindable property in your View Model and bind to it:
Check this code
View Model
public string ProductNo
{
get { return _productNo}
set
{
if (_productNo != value)
{
_productNo = value;
RaisePropertyChanged();
RaisePropertyChanged(() => ThumbnailImageUri);
}
}
}
public Uri ThumbnailImageUri
{
get
{
if (_thumbnailImageUri == null)
{
_thumbnailImageUri = new Uri(String.Format("http://www.YOURWEBSITE.com/{0}.jpg", _productNo));
}
return _thumbnailImageUri;
}
}
View
<StackLayout BindingContext="{Binding SelectedProduct}">
<StackLayout Orientation="Horizontal">
<Image HorizontalOptions="EndAndExpand"
VerticalOptions="Center">
<Image.Source>
<UriImageSource Uri="{Binding ThumbnailImageUri}"/>
</Image.Source>
</Image>
<Label Text="{Binding ProductNo}"
Font="Bold, Large"
HorizontalOptions="StartAndExpand"
VerticalOptions="Center"/>
</StackLayout>
</StackLayout>
Here is, what works for me - hope this helps you
First my BindingContext:
public class ItemContainer
{
public ItemContainer()
{
Collection = SetList(new[] { "1", "2", "3", "4" });
}
ObservableCollection<ItemDto> SetList(string[] result)
{
ObservableCollection<ItemDto> collection = new ObservableCollection<ItemDto>();
foreach (string item in result)
{
collection.Add(GetDto(item));
}
return collection;
}
public ObservableCollection<ItemDto> Collection { get; set; }
ItemDto GetDto(string item)
{
return new ItemDto() { MainPhotoSource = new Uri(_serverInbox + item) };
}
}
My Page1.xaml looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="App1gcm.Page1">
<ListView ItemsSource="{Binding Collection}" VerticalOptions="Center" HorizontalOptions="Center" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Image Source="{Binding MainPhotoSource}" />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
And I merge them on creating as MainPage in App.cs:
public App()
{
// The root page of your application
MainPage = new Page1
{
BindingContext = new ItemContainer()
};
}

Categories