Is there a way to use embedded images from a view model - xaml

I have a view model that gets and sets an Image Source. I don't know if this is correct or not.
public class animalsmodel
{
public int id { get; set; }
public string AnimalNames { get; set; }
public Button Animalselect { get; set; }
public Button Back { get; set; }
public ImageSource AnimalPhoto { get; set; }
}
}
I then use that in my list view. But it feels like I should call it from resource?
private void SearchBar_TextChanged(object sender, TextChangedEventArgs e)
{
alistview.ItemsSource = GetAnimals(e.NewTextValue);
}
IEnumerable<animalsmodel> GetAnimals(string searchText = null)
{
var animals = new List<animalsmodel>
{
new animalsmodel() { id = 1, AnimalNames = "Aardvark", **AnimalPhoto** = ("AppSFari.Images.Aardvark.jpg")},
And then I use it in my XAML file. The name displays and I can see where the image should be but it is blank. And I have set the image in my project to Embedded Resource.
<ListView x:Name="alistview" BackgroundColor ="AliceBlue" HasUnevenRows="True" ItemSelected="alistview_ItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ImageCell ImageSource="{Binding AnimalPhoto}" Text="{Binding AnimalNames}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Your code is totally incorrect .
About how to use embedded images pls check here.
ProvideValue methods only trigers when we set static string in xaml with the following way
<Image Source="{local:ImageResource WorkingWithImages.beach.jpg}" />
In another word , embedded images can only be hard-corded in xaml but not in code .
Correct way
Place the image into specific platform project , iOS -> image assets/Resources , Android ->Resources .
Do not use embedded image, use local image, you could refer to
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/images?tabs=windows#local-images
https://learn.microsoft.com/en-us/samples/xamarin/xamarin-forms-samples/userinterface-listview-customcells/
Update
Change the type of parameters of Aardvark's constructor .
public Aardvark(String AnimalNames, Button Animalselect, Button Back, string PhotoSource)
{
InitializeComponent();
AnimalNameCall.Text = AnimalNames;
addanimal = Animalselect;
terug = Back;
dierfoto.Image = PhotoSource;
}

Thanks for the help so far. So this is what I have done, I use this as my view model for the list view.
class ImageResourceExtention : IMarkupExtension
{
public string PhotoSource { get; set; }
public string AnimalNames { get; set; }
public Button Back { get; set; }
public Button Animalselect { get; set; }
public int id { get; set; }
public object ProvideValue(IServiceProvider serviceProvider)
{
if (PhotoSource == null)
{
return null;
}
var imageSource = ImageSource.FromResource(PhotoSource, typeof(ImageResourceExtention).GetTypeInfo().Assembly);
return imageSource;
}
}
Then my list view will have a id, animal name, and lastly a image. This is how I did it. I don't know if the image part is correct?
public partial class AnimalsPage : ContentPage
{
private void SearchBar_TextChanged(object sender, TextChangedEventArgs e)
{
alistview.ItemsSource = GetAnimals(e.NewTextValue);
}
IEnumerable<ImageResourceExtention> GetAnimals(string searchText = null)
{
var animals = new List<ImageResourceExtention>
{
new ImageResourceExtention() { id = 1, AnimalNames = "Aardvark", PhotoSource = "AppSFari.Images.Aardvark.jpg"},
new ImageResourceExtention() { id = 2, AnimalNames = "Aardwolf", PhotoSource = "AppSFari.Images.Aardwolf.jpg" },
new ImageResourceExtention() { id = 3, AnimalNames = "Baboon" },
new ImageResourceExtention() { id = 4, AnimalNames = "Bat-eared Fox" },
new ImageResourceExtention() { id = 5, AnimalNames = "Black Backed Jackel" },
new ImageResourceExtention() { id = 6, AnimalNames = "Black Rhino" },
new ImageResourceExtention() { id = 7, AnimalNames = "Black Wildebeest" },
new ImageResourceExtention() { id = 8, AnimalNames = "Brown Hyena" },
new ImageResourceExtention() { id = 9, AnimalNames = "Buffalo" },
new ImageResourceExtention() { id = 10, AnimalNames = "Bush Pig" },
new ImageResourceExtention() { id = 11, AnimalNames = "Bushbuck" },
new ImageResourceExtention() { id = 12, AnimalNames = "Cape Grysbok" },
new ImageResourceExtention() { id = 13, AnimalNames = "Caracal" },
new ImageResourceExtention() { id = 14, AnimalNames = "Cheetah" },
new ImageResourceExtention() { id = 15, AnimalNames = "Common Duiker" },
new ImageResourceExtention() { id = 16, AnimalNames = "Eland" },
new ImageResourceExtention() { id = 17, AnimalNames = "Gemsbok/Oryx" },
new ImageResourceExtention() { id = 18, AnimalNames = "Giraffe" },
new ImageResourceExtention() { id = 19, AnimalNames = "Hippo" },
new ImageResourceExtention() { id = 20, AnimalNames = "Impala" },
};
if (String.IsNullOrWhiteSpace(searchText))
return animals;
var lowerAnimals = searchText.ToLower();
return animals.Where(c => c.AnimalNames.ToLower().StartsWith(lowerAnimals));
}
public AnimalsPage()
{
InitializeComponent();
alistview.ItemsSource = GetAnimals();
BindingContext = this;
}
async private void Button_Clicked(object sender, EventArgs e)
{
await myScrollview.ScrollToAsync(1, alistview.Y, true);
}
async private void alistview_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
var animals = e.SelectedItem as ImageResourceExtention;
await Navigation.PushAsync(new Aardvark(animals.AnimalNames, animals.Animalselect, animals.Back));
}
async private void Button_Clicked_1(object sender, EventArgs e)
{
await Navigation.PopAsync();
}
Then in my content page the list and search bar all works but where the image should be it is blank.
x:Class="AppSFari.Kwandwe_Pakage.AnimalsPage"
xmlns:local="clr-namespace:AppSFari.Kwandwe_Pakage"
NavigationPage.HasNavigationBar="False">
<ContentPage.Content>
<ScrollView HeightRequest="3000" x:Name="myScrollview">
<AbsoluteLayout>
<StackLayout>
<SearchBar
TextChanged="SearchBar_TextChanged"
Placeholder="Search Animal"
PlaceholderColor="Gray"
HorizontalTextAlignment="Center"
FontSize="Small"
FontAttributes="Italic"
VerticalOptions="Center" HorizontalOptions="End"/>
<ListView x:Name="alistview" BackgroundColor ="AliceBlue"
HasUnevenRows="True"
ItemSelected="alistview_ItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Image Source="{Binding PhotoSource}" Aspect="AspectFit"
HeightRequest="60"
WidthRequest="60"/>
<Label Text="{Binding AnimalNames}" TextColor="Black"
FontFamily="appfontM"
VerticalTextAlignment="Center"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Text="Back to top" Clicked="Button_Clicked"/>
</StackLayout>
<Button Text="Back" BackgroundColor="Transparent" FontSize="8"
AbsoluteLayout.LayoutBounds="0,0,0.2,0.05" AbsoluteLayout.LayoutFlags="All"
Clicked="Button_Clicked_1"/>
</AbsoluteLayout>
</ScrollView>
</ContentPage.Content>
</ContentPage>
Sorry if I am making silly mistakes I am new to this.

Related

Xamarin forms DateTime Picker

I have found a custom renderer online but there is an error. Does anyone know how can I make a date time control? Currently I am using separate date picker and time picker but I want it to be combined.
I will post the code below that I have found from another post. This is the link to the post Xamarin Forms date and time picker
using System;
using Foundation;
using Test;
using Test.Droid;
using UIKit;
using ObjCRuntime;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly:ExportRenderer(typeof(MyPicker), typeof(MyPickerRenderer))]
namespace Test.Droid
{
public class MyPickerRenderer : PickerRenderer
{
string SelectedValue;
[Obsolete]
public MyPickerRenderer()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
if (Control != null)
{
SetTimePicker();
}
}
void SetTimePicker()
{
UIDatePicker picker = new UIDatePicker
{
Mode = UIDatePickerMode.DateAndTime
};
picker.SetDate(NSDate.Now, true);
picker.AddTarget(this, new Selector("DateChange:"), UIControlEvent.ValueChanged);
Control.InputView = picker;
UIToolbar toolbar = (UIToolbar)Control.InputAccessoryView;
UIBarButtonItem done = new UIBarButtonItem("Done", UIBarButtonItemStyle.Done, (object sender, EventArgs click) =>
{
Control.Text = SelectedValue;
toolbar.RemoveFromSuperview();
picker.RemoveFromSuperview();
Control.ResignFirstResponder();
MessagingCenter.Send<Object, string>(this, "pickerSelected", SelectedValue);
});
UIBarButtonItem empty = new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace, null);
toolbar.Items = new UIBarButtonItem[] { empty, done };
}
[Export("DateChange:")]
void DateChange(UIDatePicker picker)
{
NSDateFormatter formatter = new NSDateFormatter();
formatter.DateFormat = "MM-dd HH:mm aa"; //you can set the format as you want
Control.Text = formatter.ToString(picker.Date);
SelectedValue = formatter.ToString(picker.Date);
MessagingCenter.Send<Object, string>(this, "pickerSelected", SelectedValue);
}
}
}
Make the DateTimePicker inherit a ContentView instead of just an Entry, and then creates the Stacklayout which add the Entry and the date and time pickers to content.
See the DateTimePicker2.cs:
public class DateTimePicker2 : ContentView, INotifyPropertyChanged
{
public Entry _entry { get; private set; } = new Entry();
public DatePicker _datePicker { get; private set; } = new DatePicker() { MinimumDate = DateTime.Today, IsVisible = false };
public TimePicker _timePicker { get; private set; } = new TimePicker() { IsVisible = false };
string _stringFormat { get; set; }
public string StringFormat { get { return _stringFormat ?? "dd/MM/yyyy HH:mm"; } set { _stringFormat = value; } }
public DateTime DateTime
{
get { return (DateTime)GetValue(DateTimeProperty); }
set { SetValue(DateTimeProperty, value); OnPropertyChanged("DateTime"); }
}
private TimeSpan _time
{
get
{
return TimeSpan.FromTicks(DateTime.Ticks);
}
set
{
DateTime = new DateTime(DateTime.Date.Ticks).AddTicks(value.Ticks);
}
}
private DateTime _date
{
get
{
return DateTime.Date;
}
set
{
DateTime = new DateTime(DateTime.TimeOfDay.Ticks).AddTicks(value.Ticks);
}
}
BindableProperty DateTimeProperty = BindableProperty.Create("DateTime", typeof(DateTime), typeof(DateTimePicker2), DateTime.Now, BindingMode.TwoWay, propertyChanged: DTPropertyChanged);
public DateTimePicker2()
{
BindingContext = this;
Content = new StackLayout()
{
Children =
{
_datePicker,
_timePicker,
_entry
}
};
_datePicker.SetBinding<DateTimePicker2>(DatePicker.DateProperty, p => p._date);
_timePicker.SetBinding<DateTimePicker2>(TimePicker.TimeProperty, p => p._time);
_timePicker.Unfocused += (sender, args) => _time = _timePicker.Time;
_datePicker.Focused += (s, a) => UpdateEntryText();
GestureRecognizers.Add(new TapGestureRecognizer()
{
Command = new Command(() => _datePicker.Focus())
});
_entry.Focused += (sender, args) =>
{
Device.BeginInvokeOnMainThread(() => _datePicker.Focus());
};
_datePicker.Unfocused += (sender, args) =>
{
Device.BeginInvokeOnMainThread(() =>
{
_timePicker.Focus();
_date = _datePicker.Date;
UpdateEntryText();
});
};
}
private void UpdateEntryText()
{
_entry.Text = DateTime.ToString(StringFormat);
}
static void DTPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var timePicker = (bindable as DateTimePicker2);
timePicker.UpdateEntryText();
}
}
Usage in Xaml:
<StackLayout>
<local:DateTimePicker2></local:DateTimePicker2>
</StackLayout>
To make it easier for anyone wanting to use the DateTimePicker with XAML integration (with a Binding), I merged answers from the original post of Wendy Zhang, the comments below it and the question (Custom control not being set to content width) from csharpdude77.
Code:
public class DateTimePicker : ContentView, INotifyPropertyChanged
{
private Entry _entry { get; set; } = new Entry() { WidthRequest = 300 };
private DatePicker _datePicker { get; set; } = new DatePicker() { MinimumDate = DateTime.Today, IsVisible = false };
private TimePicker _timePicker { get; set; } = new TimePicker() { IsVisible = false };
private string _stringFormat { get; set; }
private TimeSpan _time
{
get { return TimeSpan.FromTicks(DateTime.Ticks); }
set { DateTime = new DateTime(DateTime.Date.Ticks).AddTicks(value.Ticks); }
}
private DateTime _date
{
get { return DateTime.Date; }
set { DateTime = new DateTime(DateTime.TimeOfDay.Ticks).AddTicks(value.Ticks); }
}
public string StringFormat { get { return _stringFormat ?? "dd/MM/yyyy HH:mm"; } set { _stringFormat = value; } }
public DateTime DateTime
{
get { return (DateTime)GetValue(DateTimeProperty); }
set { SetValue(DateTimeProperty, value); OnPropertyChanged(nameof(DateTime)); }
}
public static BindableProperty DateTimeProperty = BindableProperty.Create(nameof(DateTime), typeof(DateTime), typeof(DateTimePicker), DateTime.Now, BindingMode.TwoWay, propertyChanged: DTPropertyChanged);
public DateTimePicker()
{
Content = new StackLayout()
{
Children =
{
_datePicker,
_timePicker,
_entry
}
};
_datePicker.SetBinding(DatePicker.DateProperty, nameof(_date));
_timePicker.SetBinding(TimePicker.TimeProperty, nameof(_time));
_timePicker.Unfocused += (sender, args) => _time = _timePicker.Time;
_datePicker.Focused += (s, a) => UpdateEntryText();
GestureRecognizers.Add(new TapGestureRecognizer()
{
Command = new Command(() => _datePicker.Focus())
});
_entry.Focused += (sender, args) =>
{
Device.BeginInvokeOnMainThread(() => _datePicker.Focus());
};
_datePicker.Unfocused += (sender, args) =>
{
Device.BeginInvokeOnMainThread(() =>
{
_timePicker.Focus();
_date = _datePicker.Date;
UpdateEntryText();
});
};
}
private void UpdateEntryText()
{
_entry.Text = DateTime.ToString(StringFormat);
}
private static void DTPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var timePicker = bindable as DateTimePicker;
timePicker.UpdateEntryText();
}
}
Sample XAML usage:
<StackLayout>
<local:DateTimePicker DateTime="{Binding Source={RelativeSource AncestorType={x:Type class:NewRitViewModel}}, Path=DateTime}" StringFormat="dd-MM-yyyy HH:mm"></local:DateTimePicker>
</StackLayout>
On Xamarin.forms Android, you could try the code below.
Create a DateTimePicker.cs class.
public class DateTimePicker : Entry, INotifyPropertyChanged
{
public DatePicker _datePicker { get; private set; } = new DatePicker() { MinimumDate = DateTime.Today, IsVisible = false };
public TimePicker _timePicker { get; private set; } = new TimePicker() { IsVisible = false };
string _stringFormat { get; set; }
public string StringFormat { get { return _stringFormat ?? "dd/MM/yyyy HH:mm"; } set { _stringFormat = value; } }
public DateTime DateTime
{
get { return (DateTime)GetValue(DateTimeProperty); }
set { SetValue(DateTimeProperty, value); OnPropertyChanged("DateTime"); }
}
private TimeSpan _time
{
get
{
return TimeSpan.FromTicks(DateTime.Ticks);
}
set
{
DateTime = new DateTime(DateTime.Date.Ticks).AddTicks(value.Ticks);
}
}
private DateTime _date
{
get
{
return DateTime.Date;
}
set
{
DateTime = new DateTime(DateTime.TimeOfDay.Ticks).AddTicks(value.Ticks);
}
}
BindableProperty DateTimeProperty = BindableProperty.Create("DateTime", typeof(DateTime), typeof(DateTimePicker), DateTime.Now, BindingMode.TwoWay, propertyChanged: DTPropertyChanged);
public DateTimePicker()
{
BindingContext = this;
_datePicker.SetBinding<DateTimePicker>(DatePicker.DateProperty, p => p._date);
_timePicker.SetBinding<DateTimePicker>(TimePicker.TimeProperty, p => p._time);
_timePicker.Unfocused += (sender, args) => _time = _timePicker.Time;
_datePicker.Focused += (s, a) => UpdateEntryText();
GestureRecognizers.Add(new TapGestureRecognizer()
{
Command = new Command(() => _datePicker.Focus())
});
Focused += (sender, args) =>
{
Device.BeginInvokeOnMainThread(() => _datePicker.Focus());
};
_datePicker.Unfocused += (sender, args) =>
{
Device.BeginInvokeOnMainThread(() =>
{
_timePicker.Focus();
_date = _datePicker.Date;
UpdateEntryText();
});
};
}
private void UpdateEntryText()
{
Text = DateTime.ToString(StringFormat);
}
static void DTPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var timePicker = (bindable as DateTimePicker);
timePicker.UpdateEntryText();
}
}
Uasge in App.xaml.cs
var dtPicker = new DateTimePicker()
{
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
StringFormat = "HH:mm dd/MM/yyyy"
};
MainPage = new ContentPage
{
Content = new StackLayout
{
VerticalOptions = LayoutOptions.Center,
Children = {
dtPicker._datePicker,
dtPicker._timePicker,
dtPicker
},
BackgroundColor = Color.Aqua
}
};
This is my Code Behind
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
void OnDateSelected(object sender, DateChangedEventArgs args)
{
Recalculate();
}
void OnSwitchToggled(object sender, ToggledEventArgs args)
{
Recalculate();
}
void Recalculate()
{
TimeSpan timeSpan = endDatePicker.Date - startDatePicker.Date +
(includeSwitch.IsToggled ? TimeSpan.FromDays(1) : TimeSpan.Zero);
resultLabel.Text = String.Format("{0} day{1} between dates",
timeSpan.Days, timeSpan.Days == 1 ? "" : "s");
}
}
I have attached code for date time picker
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DaysBetweenDates"
x:Class="DaysBetweenDates.MainPage">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS" Value="0, 20, 0, 0" />
</OnPlatform>
</ContentPage.Padding>
<StackLayout Margin="10">
<Label Text="Days Between Dates"
Style="{DynamicResource TitleStyle}"
Margin="0, 20"
HorizontalTextAlignment="Center" />
<Label Text="Start Date:" />
<DatePicker x:Name="startDatePicker"
Format="D"
Margin="30, 0, 0, 30"
DateSelected="OnDateSelected" />
<Label Text="End Date:" />
<DatePicker x:Name="endDatePicker"
MinimumDate="{Binding Source={x:Reference startDatePicker},
Path=Date}"
Format="D"
Margin="30, 0, 0, 30"
DateSelected="OnDateSelected" />
<StackLayout Orientation="Horizontal"
Margin="0, 0, 0, 30">
<Label Text="Include both days in total: "
VerticalOptions="Center" />
<Switch x:Name="includeSwitch"
Toggled="OnSwitchToggled" />
</StackLayout>
<Label x:Name="resultLabel"
FontAttributes="Bold"
HorizontalTextAlignment="Center" />
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DaysBetweenDates"
x:Class="DaysBetweenDates.MainPage">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS" Value="0, 20, 0, 0" />
</OnPlatform>
</ContentPage.Padding>
<StackLayout Margin="10">
<Label Text="Days Between Dates"
Style="{DynamicResource TitleStyle}"
Margin="0, 20"
HorizontalTextAlignment="Center" />
<Label Text="Start Date:" />
<DatePicker x:Name="startDatePicker"
Format="D"
Margin="30, 0, 0, 30"
DateSelected="OnDateSelected" />
<Label Text="End Date:" />
<DatePicker x:Name="endDatePicker"
MinimumDate="{Binding Source={x:Reference startDatePicker},
Path=Date}"
Format="D"
Margin="30, 0, 0, 30"
DateSelected="OnDateSelected" />
<StackLayout Orientation="Horizontal"
Margin="0, 0, 0, 30">
<Label Text="Include both days in total: "
VerticalOptions="Center" />
<Switch x:Name="includeSwitch"
Toggled="OnSwitchToggled" />
</StackLayout>
<Label x:Name="resultLabel"
FontAttributes="Bold"
HorizontalTextAlignment="Center" />
</StackLayout>

Dynamic Presentation of Label as per the Data coming from web services Xamarin Forms

i am trying to render list of information inside a frame the information which comes up from database is in two parts ie "Description" : "Value"
so the row inside list make i group of description and value and some may not have
so can anyone help me how can i add the label to grid or stack as per the data which i get from my services because it could be 3 sometimes and it could be 5 sometime and creating label and binding them would be a static job
There is a very clean answer if you can use xamarin forms3.5 or greater, BindableLayout.
Xaml file
<ListView
x:Name="listView"
HasUnevenRows="True"
ItemsSource="{Binding Category}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text="{Binding Name}" TextColor="Blue" />
<StackLayout BindableLayout.ItemsSource="{Binding users}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Label Text="{Binding Name}" />
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
In my ViewModel
public class DynamicSwitchViewModel:BaseViewModel
{
public DynamicSwitchViewModel(ContentPage view):base(view)
{
ObservableCollection<User> users1 = new ObservableCollection<User>();
users1.Add(new User() { Name = "karan3" });
users1.Add(new User() { Name = "karan4" });
users1.Add(new User() { Name = "karan5" });
ObservableCollection<User> users2 = new ObservableCollection<User>();
users2.Add(new User() { Name = "karan1" });
users2.Add(new User() { Name = "karan2" });
users2.Add(new User() { Name = "karan3" });
users2.Add(new User() { Name = "karan4" });
users2.Add(new User() { Name = "karan5" });
ObservableCollection<User> users3 = new ObservableCollection<User>();
users3.Add(new User() { Name = "karan1" });
users3.Add(new User() { Name = "karan2" });
users3.Add(new User() { Name = "karan3" });
Category = new ObservableCollection<Category>();
Category.Add(new Category() { Name = "1",users=users1 });
Category.Add(new Category() { Name = "2",users=users2 });
Category.Add(new Category() { Name = "3",users=users3 });
}
private ObservableCollection<Category> category;
public ObservableCollection<Category> Category
{
get { return category; }
set { SetProperty(ref category, value); }
}
}
public class Category
{
public ObservableCollection<User> users { get; set; }
public string Name { get; set; }
}
public class User
{
public string Name { get; set; }
}

How to bind list view with chat data in Xamarin.forms xaml

I am unable to Bind ListView: and I am successfully bind list view when I have used code behind designing but I want to change designing in XAML but Now I am unable to Bind my list view, Data successfully send to server but unable to bind listbiew.
XAML: <ListView Grid.Row="0" ItemsSource="{Binding TextContainer}" x:Name="ListView"
SeparatorVisibility="None">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout VerticalOptions="FillAndExpand" Orientation="Horizontal" Padding="10">
<Label Text="{Binding Text}" YAlign="Center" Font="Large" TextColor="Red" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
CS:
private ObservableCollection<MessageText> TextContainer = new ObservableCollection<MessageText>();
public ListView listView = new ListView();
public event OnMessageSent MessageSent;
public delegate void OnMessageSent(string message);
public Chat()
{
MessageSent += (message) =>
{
var text= "Me: " + message;
TextContainer.Add(new MessageText { Text = text });
SignalRClient.SendMessage(UsernameTextbox.Text, message);
};
SignalRClient.OnMessageReceived += (username, message) => {
if (username == UsernameTextbox.Text)
{
}
else
{
var cc = username + ": " + message;
TextContainer.Add(new MessageText { Text = cc });
}
};
}
public void AddText(string text)
{
TextContainer.Add(new MessageText { Text = text });
}
private void EnterButton_Clicked(object sender, EventArgs e)
{
try
{
var messageSent = MessageSent;
if (messageSent != null)
messageSent(Messagebox.Text);
Messagebox.Text = string.Empty;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex);
}
}
}
public class MessageText
{
public string Text { get; set; }
}
}
I am using chat application. and unable to bind local message as well
Your TextContainer field should be public and a property.
Change this:
private ObservableCollection<MessageText> TextContainer = new ObservableCollection<MessageText>();
To:
public ObservableCollection<MessageText> TextContainer { get; set; } = new ObservableCollection<MessageText>();
Note how I changed private to public and added { get; set; } to indicate this is a property with automatic getters and setters.
More information on data-binding can be found on the new Microsoft docs pages for this.

MVVM INotifyPropertyChanged not working

Please assist:
I have implemented the MVVM design on a simple app using Xamarin.
I have one Model (User) and one ViewModel (UserViewModel).
Please note that this app is my first Xamarin/MVVM app and that I am new to this.
The issue that i have is that adding or removing a User, the View does NOT update.
When I add or remove a user I can confirm that the Database is updated, but not my view.
Please see my code below, what am i missing?
User Model:
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool IsActive { get; set; }
public List<Role> RolesList { get; set; }
}
UserViewModel Code:
public class UsersViewModel : INotifyPropertyChanged
{
private UserServices UserServ { get; set; }
public User UserSelected { get; set; }
private ObservableCollection<User> userList;
public ObservableCollection<User> UserList
{
get
{
return userList;
}
set
{
if (userList != value)
{
userList = value;
NotifyPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public UsersViewModel()
{
UserServ = new UserServices();
UsersLoadAsync();
}
public async void UsersLoadAsync()
{
UserList = await UserServ.UsersGetAsync();
}
}
User Helper Service code (Added for completeness)
public class UserServices
{
public async Task<ObservableCollection<User>> UsersGetAsync()
{
ObservableCollection<User> UserList = await App.UserService.GetAsync();
return UserList;
}
public async Task<bool> UsersAddAsync(User user)
{
bool success = await App.UserService.PostAsync(user);
return success;
}
public async Task<bool> UsersRemoveAsync(User user)
{
bool success = await App.UserService.DeleteAsync(user.Id, user);
return success;
}
}
View Xaml Code:
<?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:local="clr-namespace:PB_Logbook"
x:Class="PB_Logbook.MainPage"
xmlns:ViewModels="clr-namespace:PB_Logbook.ViewModels;assembly:PB_Logbook">
<ContentPage.BindingContext>
<ViewModels:UsersViewModel/>
</ContentPage.BindingContext>
<StackLayout>
<ListView ItemsSource="{Binding UserList, Mode=TwoWay}" HasUnevenRows="True" ItemSelected="Item_SelectedAsync" IsPullToRefreshEnabled="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Vertical" Padding="12,6">
<Label Text="{Binding Username}" FontSize="24"/>
<Label Text="{Binding FirstName}" FontSize="18" Opacity="0.6"/>
<Label Text="{Binding LastName}" FontSize="18" Opacity="0.6"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Text="Add" Clicked="AddButton_ClickedAsync"></Button>
<Button Text="Remove" Clicked="RemoveButton_ClickedAsync"></Button>
</StackLayout>
</ContentPage>
View code behind:
public partial class MainPage : ContentPage
{
private UserServices UserServices { get; set; }
private UsersViewModel UsersVM { get; set; }
public MainPage()
{
InitializeComponent();
UserServices = new UserServices();
UsersVM = new UsersViewModel();
}
private async void AddButton_ClickedAsync(object sender, EventArgs e)
{
await AddUserAsync();
}
private async void RemoveButton_ClickedAsync(object sender, EventArgs e)
{
await RemoveUserAsync();
}
private async void Item_SelectedAsync(object sender, EventArgs e)
{
UsersVM.UserSelected = ((User)((ListView)sender).SelectedItem);
}
private async void Pull_RefreshAsync(object sender, EventArgs e)
{
//UsersVM.UsersLoadAsync();
}
private async Task AddUserAsync()
{
Random rnd = new Random();
int rndNumber = rnd.Next(1, 100);
User user = new User()
{
Username = "User " + rndNumber,
FirstName = "Firstname " + rndNumber,
LastName = "Surname " + rndNumber,
IsActive = true
};
bool success = await UserServices.UsersAddAsync(user);
if (success)
{
if (!UsersVM.UserList.Contains(user))
UsersVM.UserList.Add(user);
}
}
private async Task RemoveUserAsync()
{
bool success = await UserServices.UsersRemoveAsync(UsersVM.UserSelected);
if (success)
{
if (UsersVM.UserList.Contains(UsersVM.UserSelected))
UsersVM.UserList.Remove(UsersVM.UserSelected);
}
}
}
The issue is with adding/removing users that does not update in my view.
Thank you.
If you're new to Xamarin MVVM, this link will help you understand the basics of MVVM in Xamarin Forms
https://deanilvincent.github.io/2017/06/03/basic-understanding-of-mvvm-and-databinding-in-xamarin-forms/
I would suggest as well, please lessen your behind the codes and just implement everything including the commands in your ViewModel.
You've written that your codes are working when saving and updating but not reflecting the view right? You should put your method in fetching the list right after your save command.
Like this in your xaml
<Button Text="Save" Command="{Binding SaveCommand}"/>
In your ViewModel, you should use Command from Xamarin
public Command SaveCommand{
get{
return new Command(async()=>{
// your command save here
// then put your method for fetching the updated list: your UsersLoadAsync();
});
}
}
If you're new to MVVM, you can also check this link. It uses Xamarin MVVM. When you finish, you'll have simple weather app with simple mvvm implementations
I hope it helps you

Can't bind IsBusy property

I have Page with an Activity Indicator with the following code:
<?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="MyApp.ClientSearch" Title="Search">
<ContentPage.Content>
<StackLayout Orientation="Vertical">
<StackLayout.Children>
<SearchBar x:Name="txtSearchClient" TextChanged="OnTextChanged"></SearchBar>
<ActivityIndicator IsVisible="{Binding IsBusy}" IsRunning="{Binding IsBusy}" x:Name="indicadorCargando" />
<ListView x:Name="lstClients"></ListView>
</StackLayout.Children>
</StackLayout>
</ContentPage.Content>
</ContentPage>
In the partial class associated to this xaml, I have:
namespace MyApp
{
public partial class ClientSearch: ContentPage
{
public BusquedaClientes()
{
InitializeComponent();
}
async void OnTextChanged(object sender, TextChangedEventArgs e)
{
if (this.txtSearchClient.Text.Length >= 3)
{
var list_clients = App.ClientsManager.GetTasksAsync(txtSearchClient.Text);
this.IsBusy = true;
var template = new DataTemplate(typeof(TextCell));
template.SetBinding(TextCell.DetailProperty, "name_ct");
template.SetBinding(TextCell.TextProperty, "cod_ct");
lstClients.ItemTemplate = template;
lstClients.ItemsSource = await list_clients;
this.IsBusy = false;
}
}
}
}
As you can see, this.IsBusy is setting the Page property, so tried to bind to that property in the XAML. Unfortunetly it doesn't work:
<ActivityIndicator IsVisible="{Binding IsBusy}" IsRunning="{Binding IsBusy}" x:Name="indicadorCargando" />
How can I bind the values of the ActivityIndicator to the IsBusy page property?
I already know that setting the values like this:
this.IsBusy = true;
indicadorCargando.IsVisible=true;
indicadorCargando.IsRunning=true;
But I don't want to do that, I want to set one value instead of three.
You certainly could go the route of a separate view model, which is not a bad idea. However for the specific question, it doesn't look like you're setting the BindingContext anywhere, so it isn't going to get the IsBusy property that you want.
I haven't tried setting the BindingContext for a control to itself, but something like this ought to work:
<ActivityIndicator
IsVisible="{Binding IsBusy}"
IsRunning="{Binding IsBusy}"
x:Name="indicadorCargando"
BindingContext="{x:Reference indicadorCargando}" />
You need to give your page a name (x:Name="myPage") and then declare the binding using a reference to it using {Binding Source={x:Reference myPage}, Path=IsBusy} for the ActivityIndicator's IsVisible and IsRunning values. More info on this answer.
Here is how I would rewrite it:
public class ClientSearch : ContentPage
{
public ClientSearch()
{
BindingContext = new ClientSearchViewModel();
var stack = new StackLayout();
var searchBar = new SearchBar();
searchBar.SetBinding<ClientSearchViewModel>(SearchBar.TextProperty, x => x.Text);
var actInd = new ActivityIndicator();
actInd.SetBinding<ClientSearchViewModel>(ActivityIndicator.IsVisibleProperty, x => x.IsBusy);
actInd.SetBinding<ClientSearchViewModel>(ActivityIndicator.IsRunningProperty, x => x.IsBusy);
var lv = new ListView
{
ItemTemplate = new DataTemplate(() =>
{
var txtCell = new TextCell();
txtCell.SetBinding<MyItemModel>(TextCell.TextProperty, x => x.Name_Ct);
txtCell.SetBinding<MyItemModel>(TextCell.TextProperty, x => x.Cod_Ct);
return txtCell;
})
};
lv.SetBinding<ClientSearchViewModel>(ListView.ItemsSourceProperty, x => x.items);
stack.Children.Add(searchBar);
stack.Children.Add(actInd);
stack.Children.Add(lv);
Content = stack;
}
}
public class ClientSearchViewModel : BaseViewModel
{
public string Text { get; set; }
public bool IsBusy { get; set; }
public IEnumerable<MyItemModel> items { get; set; }
protected override async void OnPropertyChanged(string propertyName = null)
{
if (propertyName != nameof(Text) || Text.Length < 3) return;
IsBusy = true;
items = await App.ClientsManager.GetTasksAsync(Text);
IsBusy = false;
}
}
[ImplementPropertyChanged]
public abstract class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}