Today I've updated Xamarin Forms to 4.1 version and the images from web are not loading anymore. I've tried to restart VS (for mac), cleaned the solution and rebuilt it, completly remove the app from my cellphone and even restart the device but nothing seems to work.
Here is the XAML markup
<ListView
x:Name="campaignsListView"
HasUnevenRows="True"
RelativeLayout.WidthConstraint="{ConstraintExpression
Type=RelativeToParent,
Property=Width,
Factor=1}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame CornerRadius="10"
RelativeLayout.WidthConstraint="{ConstraintExpression
Type=RelativeToParent,
Property=Width,
Factor=1}">
<StackLayout Orientation="Vertical">
<Image
BackgroundColor="Aqua"
HeightRequest="150"
HorizontalOptions="FillAndExpand"
Source="{Binding CampaignImage}"></Image>
<Label
HorizontalOptions="CenterAndExpand"
Text="{Binding CampaignImage}"></Label>
</StackLayout>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I don'be believe that any binding error is occuring, as I ended upfor debugging purpose, binding the Image URL to a label and it works fine
Image I'm trying to load
https://www.editorajuspodivm.com.br/cdn/imagens/produtos/original/produto-teste-marcador-de-paginas-1154410cb043c754d9e2ada9fed04650.png
EDIT:
I noticed that my VS updates were coming from Preview Channel, I changed to Stable channel, did all the updates and now everything is fine again. As the builds where Previews I believe that maybe something were still buggy
CampaignImage must set as uri CampaignImageUri
CampaignImageUri = New Uri(CampaignImage):
I have created a demo to test your code with Xamarin.forms 4.1 and it works well.
Here is the code.
ImageViewModel.cs
class ImageViewModel : INotifyPropertyChanged
{
public string _image;
public string image
{
get
{
return _image;
}
set
{
if (_image != value)
{
_image = value;
NotifyPropertyChanged();
}
}
}
public string _imageName;
public string imageName
{
get
{
return _imageName;
}
set
{
if (_imageName != value)
{
_imageName = value;
NotifyPropertyChanged();
}
}
}
protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
page.xaml.cs
ObservableCollection<ImageViewModel> imageViewModels = new
ObservableCollection<ImageViewModel>();
public Page1()
{
InitializeComponent();
imageViewModels.Add(new ImageViewModel
{
image = "https://www.editorajuspodivm.com.br/cdn/imagens/produtos/original/produto-teste-marcador-de-paginas-1154410cb043c754d9e2ada9fed04650.png",
imageName = "Image1"
});
campaignsListView.ItemsSource = imageViewModels;
}
xaml is the same with yours.
<StackLayout Orientation="Vertical">
<Image BackgroundColor="Aqua" HeightRequest="150"
HorizontalOptions="FillAndExpand" Source="{Binding image}"></Image>
<Label HorizontalOptions="CenterAndExpand" Text="{Binding imageName}"></Label>
</StackLayout>
Note: Define image url to string type.
Related
I have 2 ContentView in MyWordPage.Xaml which are MyWordListView and AddWordsView
My MyWordPage.Xaml looks like this :
<ContentView x:Name="MyWordListView" >
<CollectionView x:Name="ListOfWords" IsVisible="False"
SelectionMode="Single" >
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout >
<Label Text="{Binding .}" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ContentView>
<ContentView IsVisible="False" x:Name="AddWordsView" />
<pv:PancakeView HorizontalOptions="End" VerticalOptions="End" Margin="0,0,10,150" Padding="10" CornerRadius="10">
<Image HeightRequest="30" WidthRequest="30" Aspect="AspectFit" />
<pv:PancakeView.GestureRecognizers>
<TapGestureRecognizer Tapped="OnChangeViewButton" />
</pv:PancakeView.GestureRecognizers>
</pv:PancakeView>
<Grid/>
If MyWordListView is visible, AddWordsView is not visible and vise versa.
The App starts with MyWordListPage visible and MyWordPage.xaml.cs looks like this:
public MyWordPage()
{
InitializeComponent();
// My second content view AddWordsView take content form a content page
// this content page name is MyAddWordPage
AddWordsView.Content = new MyAddWordPage().Content;
//My list of words
ListOfWords.ItemsSource = new List<string>()
{
"New York",
"London",
"Mumbai",
"Chicago"
};
}
//I navigate between the 2 View With an Overlay Button that make
//each one of View visible thanks to a boolean
bool ViewChange=false;
void OnChangeViewButton(System.Object sender, System.EventArgs e)
{
if (ViewChange==false)
{
AddWordsView.IsVisible=true;
MyWordListView.IsVisible=false;
ViewChange=!ViewChange;
}
else
{
AddWordsView.IsVisible=false;
MyWordListView.IsVisible=true;
ViewChange=!ViewChange;
}
}
OnUpdateMyList()
{
// Here I do things to refresh my list
}
MyAddWordPage.xaml.cs looks like this :
public MyWordPage()
{
InitializeComponent();
}
void OnInsertWord(System.Object sender, System.EventArgs e)
{
}
What I would like to do :
In MyAddWordPage.xaml.cs when clicking on a button to launch the function OnInsertWord() I would like to launch the function OnUpdateMyList() in MyWordPage.xaml.cs in order to refresh My collectionView in MyWordListView
Thanks for your help
According to your requirement, you can achieve this by overriding OnAppearing method.
The OnAppearing method is executed after the ContentPage is laid out, but just before it becomes visible.So, you can rebind the list to the collectionview in this method. Therefore, this is a good place to set the content of Xamarin.Forms views.
Here is the code in MyWordPage.xaml.cs:
public MainPage()
{
InitializeComponent();
// My second content view AddWordsView take content form a content page
// this content page name is MyAddWordPage
AddWordsView.Content = new MyAddWordPage().Content;
//My list of words
}
//Create a list
List<string> list = new List<string>()
{
"New York",
"London",
"Mumbai",
"Chicago"
};
protected override void OnAppearing()
{
base.OnAppearing();
ListOfWords.ItemsSource = list;
}
//I navigate between the 2 View With an Overlay Button that make
//each one of View visible thanks to a boolean
bool ViewChange = false;
void OnChangeViewButton(System.Object sender, System.EventArgs e)
{
if (ViewChange == false)
{
AddWordsView.IsVisible = true;
MyWordListView.IsVisible = false;
ViewChange = !ViewChange;
}
else
{
AddWordsView.IsVisible = false;
MyWordListView.IsVisible = true;
ViewChange = !ViewChange;
}
}
}
Here is the code in MyWordPage.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"
x:Class="App5.MainPage">
<StackLayout>
<StackLayout x:Name="MyWordListView">
<CollectionView x:Name="ListOfWords" SelectionMode="Single" >
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout >
<Label Text="{Binding .}" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
<ContentView IsVisible="False" x:Name="AddWordsView" >
<Label Text="Here is the text."></Label>
</ContentView>
<StackLayout>
<Button Clicked="OnChangeViewButton"></Button>
</StackLayout>
</StackLayout>
</ContentPage>
I'm using a CarouselView that I've bound to a IEnumerable<T> list to display a preview of my files in that list with the filename and using the filepath as a image preview.
This binding works as expected when I debug my application with the filename and image preview showing as intended according to each file. However, when I test my application in Release mode, the binding for the filename fails while the binding for my filepath still works.
To show you what I mean, here are some screenshots from both Debug and Release:
Release // Debug
The application should function like the debug screenshot where the filename is visible at the top of the frame.
Here is my XAML and code-behind to better understand what is going wrong:
XAML:
<!--Carousel of files selected preview with binding to the Files variable in the codebehind-->
<CarouselView x:Name="preview" IsVisible="True" ItemsSource="{Binding Files}" PeekAreaInsets="20" >
<CarouselView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Frame HasShadow="True"
IsVisible="True"
BorderColor="Black"
CornerRadius="5"
Margin="5,10,5,10"
HeightRequest="500"
WidthRequest="500"
HorizontalOptions="Center"
VerticalOptions="Start">
<StackLayout>
<Label TextColor="Black" Text="{Binding FileName}" IsVisible="True"></Label>
<Image Source="{Binding FullPath}"
Aspect="AspectFill"
HeightRequest="500"
WidthRequest="500"
VerticalOptions="Center"/>
</StackLayout>
</Frame>
</StackLayout>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
Code-behind:
//<--Summary: Gives users a preview of the files they selected before uploading-->
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class FileView : ContentPage
{
public static string fileID; //Upload filename
private IEnumerable<FileResult> pickedFiles; //List of picked files
public IEnumerable<FileResult> Files {
get => pickedFiles;
}
public string SelectedTime { get; set; } //Binding for user selected transfer time
public bool SelectedCompression { get; set; } //Binding for user selected compression type
public FileView( IEnumerable<FileResult> pF)
{
InitializeComponent();
SelectedTime = "1"; //Sets default transfer time to 1 minute
this.pickedFiles = pF;
this.BindingContext = this;
}
My question boils down to why won't my observable collection binding work? I am attempting to bind an image url to a listview, but the binding will not work, pretty much just make a list of product images. I can generate the image if I just do a stack layout and declare the image based on the list position (ie image.source = list[i].url).
Below is the xaml
<ListView ItemsSource="{Binding ActImage}">
<ListView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Image Source="{Binding ImageUrl}"/>
</StackLayout>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Below is the c behind the xaml where "PicListPull" is the model and pictures is the previously generated list of image urls.
private ObservableCollection<PicListPull> actimage;
public ObservableCollection<PicListPull> ActImage;
ActImage = new ObservableCollection<PicListPull>();
for (int i = 0; i < pictures.Count(); i++)
{
var pics = new PicListPull()
{
ImageUrl = ImageSource.FromUri(pictures[i].Url)
};
ActImage.Add(pics);
}
If I look at the ActImage at an index, it has a value for the image source, so I can't figure out why it won't display the image.
Thanks in advance.
Update
Below is the PicListPull
public class PicListPull:INotifyPropertyChanged
{
private ImageSource imageurl;
//#region public properties
public ImageSource ImageUrl
{
get { return imageurl; }
set
{
this.imageurl = value;
RaisePropertyChanged("ImageUrl");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(String name)
{
if (PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
The cell of ListView should be one of these type :
TextCell, ImageCell, SwitchCell, EntryCell or CustomCell (ViewCell)
In this case if you want to use custom cell then please update like this:
<ListView ItemsSource="{Binding ActImage}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Image Source="{Binding ImageUrl}"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
For more details you can check here : Custom Cells
there are three issues you need to fix
first, you can only bind to public properties
ItemsSource="{Binding ActImage}"
for this to work, ActImage needs to be a public property
public ObservableCollection<PicListPull> ActImage { get; set; }
second, you need to use a Cell in your template (as #Qwerty noted)
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Image Source="{Binding ImageUrl}"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
finally, when binding the Image, you can just use a string containing the url
I have a Switch bound to a property of an element in a List. I want to bind IsVisible of a button to the same property, but the button's visibility is not changed when the property is changed by the Switch. What am I missing?
XAML:
<StackLayout>
<ListView HorizontalOptions="FillAndExpand" ItemsSource="{Binding EquipmentList}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Name}" />
<Switch IsToggled="{Binding State}" />
<Button
Command="{Binding BindingContext.DoCommand, Source={x:Reference TestPage}}"
CommandParameter="{Binding .}"
IsVisible="{Binding State}"
Text="Click" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
ViewModel:
private Command<Equipment> _doCommand;
public Command<Equipment> DoCommand => _doCommand ??
(_doCommand = new Command<Equipment>((Equipment obj) => HandleEquipment(obj)));
// Outputs correct Name and State of the list item
private void HandleEquipment(Equipment obj)
{
System.Diagnostics.Debug.WriteLine(obj.Name + ", " + obj.State);
}
Model:
class Equipment
{
public int Id { get; set; }
public string Name { get; set; }
public bool State { get; set; }
public Equipment(int Id, string Name, bool State)
{
this.Id = Id;
this.Name = Name;
this.State = State;
}
}
As Gerald wrote in his first comment: You have to implement the INotifyPropertyChanged interface on your Equipment model (and not just in the ViewModel).
Without this implementation, the elements in the view have no chance to know, that the state changed (in your case the button).
Implementation:
public class Equipment: INotifyPropertyChanged
{
public bool State
{
get => _state;
set =>
{
_state = value;
OnPropertyChanged();
}
}
private bool _state;
// OTHER PROPERTIES
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
The call of the method OnPropertyChanged() is important. The IsVisible property of the button recognizes the change and updates his value.
Instead of binding two things to a property, why not have the single item bound (i.e. the switch) and use XAML to show or hide the button:
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibility" />
</Window.Resources>
<StackLayout>
<ListView HorizontalOptions="FillAndExpand" ItemsSource="{Binding EquipmentList}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Name}" />
<Switch Name="toggleSwitch" IsToggled="{Binding State}" />
<Button
Command="{Binding BindingContext.DoCommand, Source={x:Reference TestPage}}"
CommandParameter="{Binding .}"
IsVisible="{Binding ElementName=toggleSwitch, Path=IsToggled, Converter={StaticResource BooleanToVisibilityConverter}"
Text="Click" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
It may not be a Window that your StackLayout is in, but if you place a BooleanToVisibilityConverter in your Resources section you'll then be able to use it in your XAML file.
This will mean that if the property name changes in the future you only have one place you need to update in the user interface and you're also using the power of the XAML language.
Also as correctly pointed out by everyone, you need to implement INotifyPropertyChanged in the model in order for the Switch to be updated too.
Nested list view is not working, I have a list which contains another list in it. To show it in View I am using Nested listview; But the code is not working,and i am not able to identify on where it went wrong... Below is my code
Main Page
<ContentPage.Content>
<StackLayout>
<ListView x:Name="outerListview" ItemsSource="{Binding lst}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell x:Name="outerListviewCell">
<ViewCell.View>
<ContentView>
<Label Text="{Binding ItemName}"/>
<StackLayout>
<ListView ItemsSource="{Binding ItemList}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell x:Name="InnerListviewCell">
<Grid>
<Label Text="{Binding stockQty}"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentView>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
ViewModel
public MainPageViewModel()
{
lst = new ObservableCollection<A>()
{
new A()
{
ItemName="Item1", ItemList=new ObservableCollection<ItemDetails>()
{
new ItemDetails() { stockQty="2"},
new ItemDetails(){ stockQty="3"}
}
},
new A()
{
ItemName="Item2", ItemList=new ObservableCollection<ItemDetails>()
{
new ItemDetails() { stockQty="3"},
new ItemDetails(){ stockQty="4"}
}
}
};
}
Model ( Class A and Class Itemdetails)
class A:INotifyPropertyChanged
{
public A()
{
ItemName = string.Empty;
ItemList = new ObservableCollection<ItemDetails>();
}
private string _ItemName;
public string ItemName
{
get { return _ItemName; }
set { _ItemName = value; OnPropertyChanged(); }
}
private ObservableCollection<ItemDetails> _itemlist;
public ObservableCollection<ItemDetails> ItemList
{
get { return _itemlist; }
set { _itemlist = value; OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
class ItemDetails:INotifyPropertyChanged
{
private string _stockQty;
public string stockQty
{
get { return _stockQty; }
set { _stockQty = value; OnPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
When I Run the above code I am getting below output in screen
2
3
What is expected actually is
Item1
2
3
Item2
3
4
What is wrong in above code? could anyone can help me?
Nesting Listview inside another Listview is not a good idea and it is not a supported on Xamarin.Forms.
ListView is very "sensitive" and it can easialy cause problems with scrolling and of course there are problems with poor performance of your app.
So I strongly recommend you to rethink about your layout and take a look at Grouping with ListView more about it here, maybe you can achieve what you want with Grouping.
After checking your code , I found something need to modify in your code.
In order to show the ItemName , you should wrap Label inside StackLayout .
In order to get Uneven Row, you should set listview.HasUnevenRows = true.
Modify your code as below:
<ContentPage.Content>
<ListView x:Name="outerListview" HasUnevenRows="True" ItemsSource="{Binding lst}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell x:Name="outerListviewCell">
<ViewCell.View>
<ContentView>
<StackLayout>
<Label Text="{Binding ItemName}"/>
<ListView ItemsSource="{Binding ItemList}" RowHeight="20">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell x:Name="InnerListviewCell">
<Grid>
<Label Text="{Binding stockQty}"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentView>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>
That is absolutely wrong. You must use one ListView with IsGroupingEnabled set to True.
Follow instuctions here to make it work correct: https://xamarinhelp.com/xamarin-forms-listview-grouping/