Xamarin.Forms Image Source binding to string (MVVM) - c#

I am intending to bind the source for an image from a URL I am loading in my ViewModel
In the xaml file, it works just fine if I use:
<Image Source="https://example.com/image.jpg"/>
However, it won't work if I use
ViewModel
public string Image = {get; set;}
Image = "https://example.com/image.jpg";
XAML - I am setting the BindingContext to the ViewModel
<Image Source="{Binding Image}"/>
Any ideas?
Thanks

The code below should work. I set the binding in Xaml.
Xaml:
<ContentPage.BindingContext>
<local:ViewModel></local:ViewModel>
</ContentPage.BindingContext>
<ContentPage.Content>
<StackLayout>
<Image Source="{Binding Image}"></Image>
</StackLayout>
</ContentPage.Content>
ViewModel:
public class ViewModel
{
public string Image { get; set; }
public ViewModel()
{
Image = "https://aka.ms/campus.jpg"; //"https://example.com/image.jpg"
}
}
Or you could set the binding in code behind:
Xaml:
<ContentPage.Content>
<StackLayout>
<Image Source="{Binding Image}"></Image>
</StackLayout>
</ContentPage.Content>
Code behind:
public partial class Page2 : ContentPage
{
public Page2()
{
InitializeComponent();
this.BindingContext = new ViewModel();
}
}
public class ViewModel
{
public string Image { get; set; }
public ViewModel()
{
Image = "https://aka.ms/campus.jpg"; //"https://example.com/image.jpg"
}
}

Related

MAUI: ListView Binding With Custom ViewCell

I use FreshMvvm to develop and run MAUI project on Windows.
But I have some binding issues with ListView and my custom template.
The following is my code:
Model:
public class BaseModel
{
public string Code{ get; set; }
}
public class NameModel: BaseModel
{
public string Name{ get; set; }
}
ViewModel:
public class MainPageModel : FreshBasePageModel
{
private readonly IApiService _apiService;
private List<NameModel> _nameModelList;
public List<NameModel> NameModelList
{
get => _nameModelList;
private set
{
_nameModelList= value;
RaisePropertyChanged(nameof(NameModelList));
}
}
public MainPageModel(IApiService apiService)
{
_apiService = apiService;
}
protected override void ViewIsAppearing(object sender, EventArgs e)
{
base.ViewIsAppearing(sender, e);
Task.Run(() => GetNameData());
}
private async Task GetNameData()
{
var result = await _apiService.GetNameData();
NameModelList= result.GetRange(1, 10);
}
}
I create a list and use an api service to get a name model list data.
If api service gets the data, NameModelList will be updated.
NameModelList is the property which will be bind on Listview.ItemsSource
MainPage.xmal:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MyNamespace.ViewCells.CustomListViewCell"
x:Class="MyNamespace.Pages.MainPage"
BackgroundColor="{DynamicResource SecondaryColor}">
<Grid RowSpacing="25"
RowDefinitions="Auto"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand">
<ListView
x:Name="MyListView"
ItemsSource="{Binding NameModelList}"
Grid.Row="0"
WidthRequest="800"
HeightRequest="800"
BackgroundColor="Gray"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand">
<ListView.ItemTemplate>
<DataTemplate>
<local:MyCustomViewCell/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</ContentPage>
Custom ViewCell (.xml):
<ViewCell xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyNamespace.ViewCells.CustomListViewCell.MyCustomViewCell">
<Grid RowSpacing="100" WidthRequest="100" HeightRequest="100">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100*" />
</Grid.ColumnDefinitions>
<StackLayout
GridLayout.Row="0"
GridLayout.Column="0">
<Label
Text="{Binding Code}"
FontSize="30"/>
<Label
Text="{Binding Name}"
FontSize="30"/>
</StackLayout>
</Grid>
</ViewCell>
Custom ViewCell (.cs)
public partial class MyCustomViewCell: ViewCell
{
public static readonly BindableProperty CodeProperty =
BindableProperty.Create("Code", typeof(string), typeof(MyCustomViewCell), "");
public string Code
{
get { return (string)GetValue(CodeProperty); }
set { SetValue(CodeProperty, value); }
}
public static readonly BindableProperty NameProperty =
BindableProperty.Create("Name", typeof(string), typeof(MyCustomViewCell), "");
public string Name
{
get { return (string)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
}
I define a custom ViewCell files and put this ViewCell in the Listview of MainPage.
Now my question is my Listview can't show data successfully.
I'm sure that NameModelList has value and its count is more than 1.
But I can see nothing.
The output log has no error, and the breakpoints in MyCustomViewCell.cs are never triggered.
So I think I have some binding issues, but I can't find it out.
To get to the bottom of this I took your code and put it in a project so I could have a little play with it. You can find the repo here. Not to be rude here or anything, but might be a good idea for a next question to do that yourself, that will help speed things up :)
Anyway, the problem is much more subtle. Because you're using XAML for your layout, you'll have to call InitializeComponent in the constructor. So adding this to your MyCustomViewCell made it work:
public MyCustomViewCell()
{
InitializeComponent();
}

New elements added to ObservableCollection from external Page are not persisted

I have two pages. On the first I want to see every elements from ObservableCollection and the second I want to add new element to ObservableCollection. When I give breakpoint I see that the ObservableCollection has new element, but when I back to the first page new element dosen't exist. What should I do?
There is code
public ListViewFlascardViewModel()
{
GoToAddFlashcard = new Command(goToAddFlashcard);
Fiszka = new ObservableCollection<Flashcard>();
Fiszka.Add(new Flashcard {Name = "hello" });
}
public Command GoToAddFlashcard { get; }
Flashcard flashcard = new Flashcard();
async void goToAddFlashcard()
{
await Shell.Current.GoToAsync(nameof(View.NoweFiszki));;
}
public ObservableCollection<Flashcard> Fiszka { get; set; }
}
And there is second page:
class NoweFiszkiViewModel
{
public Command SaveFlashcard { get; }
public NoweFiszkiViewModel()
{
SaveFlashcard = new Command(save);
}
ListViewFlascardViewModel list = new ListViewFlascardViewModel();
private async void save()
{
list.Fiszka.Add(new Flashcard { Name = "Bye" });
await Shell.Current.GoToAsync("..");
}
}
I try a lot of things, but nothing help. I am new in C# and I will be appreciated for every help.
I add whole code.
There is view
ListViewFlashcard
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewmodels="clr-namespace:MVVM.ViewModel"
xmlns:model="clr-namespace:MVVM.Model"
x:Class="MVVM.View.ListViewFlashcard"
x:DataType="viewmodels:ListViewFlascardViewModel"
>
<ContentPage.BindingContext>
<viewmodels:ListViewFlascardViewModel/>
</ContentPage.BindingContext>
<StackLayout>
<Button
Command="{Binding GoToAddFlashcard}"
Text="Click Me"/>
<ListView
ItemsSource="{Binding Fiszka}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="model:Flashcard">
<TextCell Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
NoweFiszki
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewmodels="clr-namespace:MVVM.ViewModel"
x:Class="MVVM.View.NoweFiszki">
<ContentPage.BindingContext>
<viewmodels:NoweFiszkiViewModel/>
</ContentPage.BindingContext>
<ContentPage.Content>
<StackLayout>
<Entry Text="{Binding Text, Mode=TwoWay}"/>
<Button Text="Save"
Command="{Binding SaveFlashcard}"/>
</StackLayout>
</ContentPage.Content>
And Model
Flashcard
public class Flashcard
{
public string Name { get; set; }
}
And AppShell
<Shell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MVVM.AppShell"
xmlns:local="clr-namespace:MVVM.View"
>
<FlyoutItem Title="Add Flashcard" Icon="icon_about.png">
<ShellContent ContentTemplate="{DataTemplate local:ListViewFlashcard}"/>
</FlyoutItem>
public partial class AppShell : Xamarin.Forms.Shell
{
public AppShell()
{
InitializeComponent();
Routing.RegisterRoute(nameof(NoweFiszki), typeof(NoweFiszki));
}
}
It is everything what I wrote.
A couple of things have to be corrected on your code:
In NoweFiszkiViewModel you are creating a new instance of ListViewFlascardViewModel every time, and adding elements on that new instance, which does not affect the instance to which ListViewFlashcard is actually bound.
To fix this, you will have to create a public static ListViewFlascardViewModel in ListViewFlashcard.xaml.cs and set the binding context to it, as follows
ListViewFlashcard.xaml.cs
public static ListViewFlascardViewModel vm { get; set; }
public ListViewFlashcard()
{
InitializeComponent();
vm = new ListViewFlascardViewModel();
}
protected override void OnAppearing()
{
base.OnAppearing();
BindingContext = vm;
}
then correct ListViewFlashcard.xaml as follows
ListViewFlashcard.xaml
<!--<ContentPage.BindingContext>
<viewmodels:ListViewFlascardViewModel/>
</ContentPage.BindingContext>-->
<StackLayout>
<Button
Command="{Binding GoToAddFlashcard}"
Text="Click Me"/>
<ListView ItemsSource="{Binding Fiszka}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="viewmodels:Flashcard">
<TextCell Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
Finally you have to correct NoweFiszkiViewModel.cs as follows, adding the new elements to the public static view model:
NoweFiszkiViewModel.cs
public Command SaveFlashcard { get; }
public NoweFiszkiViewModel()
{
SaveFlashcard = new Command(save);
}
//ListViewFlascardViewModel list = new ListViewFlascardViewModel();
private async void save()
{
ListViewFlashcard.vm.Fiszka.Add(new Flashcard { Name = "Bye" });
await Shell.Current.GoToAsync("..");
}

Xamarin.Forms Compiled Bindings does not work on DataTemplate

I am having trouble with Use compiled bindings in a DataTemplate.
I added XamlComilation in App.xaml.cs
// https://learn.microsoft.com/en-us/xamarin/xamarin-forms/xaml/xamlc
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace Solution.Project
And I changed my DataTemplate to
<DataTemplate x:Key="RectLayerDataTemplate" x:DataType="{x:Type viewmodels:RectLayerViewModel}">
<forms:RectLayerView forms:ValueX="{Binding ValueX}"
forms:ValueY="{Binding ValueY}"
forms:ValueWidth="{Binding ValueWidth}"
forms:ValueHeight="{Binding ValueHeight}"
forms:Color="{Binding Color}" />
</DataTemplate>
However, DataTemplate does not applied to BindableLayout.ItemSource.
<AbsoluteLayout x:Name="CanvasLayout"
BindableLayout.ItemsSource="{Binding LayerViewModels}" />
Did you use AbsoluteLayout to make the layout overlap, so you think it didn't work?
I create a simple sample,with the DataTemplate and set it to the StackLayout BindableLayout,it works well.
the page.xaml:
<StackLayout x:Name="CanvasLayout" Orientation="Vertical"
BindableLayout.ItemsSource="{Binding LayerViewModels}" />
page.xam.cs:
public MyPage()
{
InitializeComponent();
BindingContext = new MyData();
LayerDataTemplate s = new LayerDataTemplate();
BindableLayout.SetItemTemplate(CanvasLayout, s.template);
}
MyData :
public class MyData
{
public ObservableCollection<RectLayerViewModel> LayerViewModels { set; get; } = new ObservableCollection<RectLayerViewModel>();
public MyData()
{
for (var x = 0; x < 6; x++)
{
LayerViewModels.Add(new RectLayerViewModel { Name = $"Added at {LayerViewModels.Count}", Type = 1 });
}
}
}
RectLayerViewModel:
public class RectLayerViewModel
{
public string Name { get; set; }
public int Type { get; set; }
}
DataTemplate:
<?xml version="1.0" encoding="UTF-8"?>
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local ="clr-namespace:EntryCa"
x:Class="EntryCa.LayerDataTemplate">
<DataTemplate x:Key="RectLayerDataTemplate" x:DataType="local:RectLayerViewModel">
<Label Text="{Binding Name}"
TextColor="Red"
FontAttributes="Bold" />
</DataTemplate>
Sample usage of compiled bindings in a DataTemplate. Below ItemViewModel is the type of the items in a ListView.ItemsSource:
XAML:
xmlns:vm="clr-namespace:MyViewModels"
...
<DataTemplate x:Key="ListViewItemTemplate" x:DataType="vm:ItemViewModel" >
...
<ListView ... ItemTemplate="{StaticResource ListViewItemTemplate}"/>
ItemViewModel:
namespace MyViewModels
{
public class ItemViewModel : ViewModelPropertyBase
...
public abstract class ViewModelPropertyBase : INotifyPropertyChanged
...

Xamarin.Forms ImageCell not displaying image version 4.7.0.xx

I already tried, string to ImageSource in my model but still I did not get image to display in ImageCell.
If I try this line of code <Image Source="https://img.icons8.com/carbon-copy/2x/user.png"></Image>, image displays fine.
Model
public class Contact
{
public string Name { get; set; }
public string Status { get; set; }
public ImageSource ImageUri { get; set; }
}
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:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="App1.ListPage">
<ContentPage.Content>
<StackLayout>
<ListView x:Name="listView" SeparatorVisibility="Default" SeparatorColor="Blue">
<ListView.ItemTemplate>
<DataTemplate>
<!--<TextCell Text="{Binding Name}" Detail="{Binding Status}" TextColor="Accent" DetailColor="Beige"></TextCell>-->
<!--<TextCell Text="{Binding Name}" Detail="{Binding Status}"></TextCell>-->
`<ImageCell Text="{Binding Name}" Detail="{Binding Status}" ImageSource="{Binding ImageUri}`"></ImageCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<!--<Image Source="https://img.icons8.com/carbon-copy/2x/user.png"></Image>-->
</StackLayout>
</ContentPage.Content>
</ContentPage>
XAML.cs
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ListPage : ContentPage
{
public ListPage()
{
InitializeComponent();
listView.ItemsSource = new List<Contact>
{
new Contact{ Name="Monkey D. Luffy", ImageUri=ImageSource.FromUri(new Uri("https://img.icons8.com/carbon-copy/2x/user.png"))},
new Contact{ Name="Monkey D. Dragon",
ImageUri=ImageSource.FromUri(new Uri("https://img.icons8.com/carbon-copy/2x/user.png"))},
new Contact{ Name="Portgas D. Ace",
ImageUri=ImageSource.FromUri(new Uri("https://img.icons8.com/carbon-copy/2x/user.png")), Status="Hello World!"}
};
}
}
just did two tests on my playground.
I kicked off with a blank Xamarin.Forms project (only one MainPage there) in VS2019, then
updated the XF nuget package to 4.7.0.1142.
pasted your code.
remove two extra ` characters in your xaml code snippet.
It's working.
Then, I tried the string property in the model class like this:
public string ImageUri { get; set; }
And also set string value in xaml.cs class like this:
new Contact{ Name="", ImageUri="https://xxx.png"}
It's also working.
Not sure about the issue, but your code is fine anyway.
Try to use this nuget https://github.com/luberda-molinet/FFImageLoading for images. Is the best !!

Data Binding using Mvvm Helpers

I'm trying to bind some data using MvvmHelpers by creating an object and binding that object.
https://github.com/jamesmontemagno/mvvm-helpers
In my model I have several data to bind so I just made a quick template of what I'm working on.
If I move what is inside NameModel to NameViewModel it does work however I'm trying separate my data.
Model:
public class NameModel : BaseViewModel
{
string name;
public string Name
{
get { return name; }
set { SetProperty(ref name, value); }
}
}
View Model:
public class NameViewModel : BaseViewModel
{
NameModel nameModel;
public NameViewModel()
{
nameModel = new NameModel { name="Jon Doe" };
}
}
Page.xaml.cs
public partial class NamePage : ContentPage
{
public NamePage()
{
InitializeComponent();
BindingContext = new NameViewModel();
}
}
Page.xaml:
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="NameProj.NamePage">
<StackLayout
Orientation="Vertical" >
<Label
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
BackgroundColor="Transparent"
Text={Binding Name}/>
</StackLayout>
</ContentPage>
Have you tried changing it to nameModel.Name?
<Label
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
BackgroundColor="Transparent"
Text="{Binding nameModel.Name}"/>
Hope it helps!
NameModel nameModel;
I think should be public
public NameModel nameModel {get; set;}
and Yes, I think #mindOfAi is correct

Categories

Resources