I have a grouped ObservableCollection which is binded to a ListView.
I have a number of StackLayout inside the ListView who's visibility changes upon the ListView tapp.
All others properties inside reacts to the PropertyChange but the visibility of the StackLayout doesnot.
All this is done on UI Thread.
Can anyone else has/had this issue?
Updating the UI inside a loop
Device.BeginInvokeOnMainThread(() =>
{
item.Effort = SelectedTaskItem.Effort;
finalEffortDouble = SelectedTaskItem.Effort + finalEffortDouble - previousEffort;
item.IsSaved = true;
item.IsNotSaved = false;
item.EffortsString = SelectedTime.ToString("hh':'mm") + " h";
TotalEffortsHoursString = TimeSpan.FromHours(finalEffortDouble + TotalEffortsHours).ToString("hh'.'mm") + " hrs";
});
IsSaved , IsNotSaved are bool which are binded to the StackLayout visibility property inside a ListView.
<StackLayout Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" IsVisible="{Binding IsNotSaved,Mode=TwoWay}" Padding="0" Margin="0" HorizontalOptions="FillAndExpand">
<Label Text="{Binding TaskName}" FontAttributes="Bold" FontSize="16" LineBreakMode="TailTruncation" />
<Label Text="Enter time spent on this task daily" FontSize="12" />
</StackLayout>
<StackLayout Grid.Row="0" Grid.Column="1" IsVisible="{Binding IsNotSaved,Mode=TwoWay}" Orientation="Horizontal" VerticalOptions="CenterAndExpand" Spacing="10" HorizontalOptions="End" Padding="0" Margin="0">
<ffimageloading:CachedImage Source="tsplus">
<ffimageloading:CachedImage.GestureRecognizers>
<TapGestureRecognizer Tapped="EditTime_Tapped" CommandParameter="{Binding .}" />
</ffimageloading:CachedImage.GestureRecognizers>
</ffimageloading:CachedImage>
</StackLayout>
<StackLayout x:Name="Is1" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" IsVisible="{Binding IsSaved,Mode=TwoWay}" Padding="0" Margin="0" HorizontalOptions="FillAndExpand">
<Label Text="{Binding TaskName}" FontAttributes="Bold" FontSize="16" LineBreakMode="TailTruncation" />
<StackLayout Orientation="Horizontal" Padding="0" Margin="0" HorizontalOptions="FillAndExpand">
<Label Text="Tap on time to edit" FontSize="12" />
<ffimageloading:CachedImage Source="savedtag" WidthRequest="60" HeightRequest="20" />
</StackLayout>
</StackLayout>
<StackLayout x:Name="Is2" Grid.Row="0" Grid.Column="1" IsVisible="{Binding IsSaved,Mode=TwoWay}" VerticalOptions="CenterAndExpand" HorizontalOptions="End" Padding="0" Margin="0">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="EditTime_Tapped" CommandParameter="{Binding .}" />
</StackLayout.GestureRecognizers>
<Label Text="{Binding EffortsString}" FontSize="24" VerticalOptions="Center" TextColor="{x:Static common:ColorResources.NavigationBarColor}" />
</StackLayout>
Model of the binded ObservableCollection
public class ProjectListWithTasksModel : List<TaskModel>
{
public List<TaskModel> Tasks => this;
public string ProjectName { get; set; }
}
Task Model:
public class TaskModel : ObservableObject
{
public string TaskGUID { get; set; }
public string TaskName { get; set; }
public string EffortStatus { get; set; }
public string TaskStatus { get; set; }
private double _Effort;
public double Effort
{
get { return _Effort; }
set
{
_Effort = value;
RaisePropertyChanged("Effort");
}
}
public DateTime EffortDate { get; set; }
public DateTime EffortDay { get; set; }
//UI Models : Used for UI Changes
public string ProjectID { get; set; }
public DateTime LastWorkingDay { get; set; }
private bool _IsSaved;
public bool IsSaved
{
get
{
if (this.EffortStatus.Contains("10"))
_IsSaved = true;
else
_IsSaved = false;
return _IsSaved;
}
set
{
_IsSaved = value;
RaisePropertyChanged("IsSaved");
}
}
public bool IsSubmitted
{
get
{
if (this.EffortStatus.Contains("20"))
{
return true;
//&& LastWorkingDay.Date >= DateTime.Now.Date
}
else
return false;
}
}
public bool IsApproved
{
get
{
if (this.EffortStatus.Contains("30"))
{
return true;
}
else
return false;
}
}
public bool IsRejected
{
get
{
if (this.EffortStatus.Contains("40"))
{
return true;
}
else
return false;
}
}
public string _EffortsString;
public string EffortsString
{
get
{
_EffortsString = TimeSpan.FromHours(Effort).ToString("hh':'mm") + " h";
return _EffortsString;
}
set
{
_EffortsString = value;
RaisePropertyChanged("EffortsString");
}
}
private bool _IsNotSaved;
public bool IsNotSaved
{
get
{
if (this.EffortStatus.Contains("0 "))
_IsNotSaved = true;
else
_IsNotSaved = false;
return _IsNotSaved;
}
set
{
_IsNotSaved = value;
RaisePropertyChanged("IsNotSaved");
}
}
}
I think the problem is that IsSaved depends by EffortStatus, so you should not have a "Set" for "IsSaved". I think you should "RaiseProperty" IsSaved (and IsNotSaved) when EffortStatus i set. Something like:
public string EffortStatus
{
get
{
return _EffortStatus;
}
set
{
_EffortStatus= value;
if (_EffortStatus.Contains("10"))
IsSaved = true;
else
IsSaved = false;
RaisePropertyChanged("EffortStatus");
RaisePropertyChanged("IsSaved");
}
}
Related
I'm doing a cross platform app using Xamarin. I want to bind a ListView with a query.
My query looks like this :
public async Task<List<Meeting>> GetAllMeetings()
{
return await _database.QueryAsync<Meeting>("Select Client.Prenom,Meeting.Id_Client, Meeting.DateRDV, Meeting.HourRDV,Meeting.TypePose,Meeting.IsStudent from 'Meeting' JOIN 'Client' on Meeting.Id_Client = Client.Id");
}
In my ListView I'm able to bind every parameter, except Client.Prenom. I've tried only writing Prenom but nothing shows.
Here's how I bind :
listViewAllMeetings.ItemsSource = await App.Database.GetAllMeetings();
List<Meeting> list = listViewAllMeetings.ItemsSource as List<Meeting>;
And in the xaml file I do like this to bind Prenom:
<Label Text="{Binding Prenom}"
HorizontalOptions="Start"
TextColor="#000000" />
But nothing shows. Any idea ? If I bind the Id_Client it shows the correct Id...
For the record , here's my Meeting table :
public class Meeting {
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public int Id_Client { get; set; }
public String DateRDV { get; set; }
public String HourRDV { get; set; }
public String TypePose { get; set; }
public bool IsStudent { get; set; }
public bool SheCame { get; set; }
}
my xaml file :
<ListView x:Name="listViewAllMeetings" SeparatorVisibility="Default" SeparatorColor="Silver" HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout
Orientation="Vertical">
<StackLayout Orientation="Vertical">
<StackLayout Orientation="Horizontal">
<Label Text="{Binding DateRDV}"
HorizontalOptions="Start"
TextColor="#FD6C9E" />
<Label Text="à"
HorizontalOptions="Start"
TextColor="#FD6C9E" />
<Label Text="{Binding HourRDV}"
HorizontalOptions="Start"
TextColor="#FD6C9E" />
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Id_Client}"
HorizontalOptions="Start"
TextColor="#FD6C9E" />
<Label Text="Est étudiante ?"
HorizontalOptions="End"
TextColor="#FD6C9E" />
<Label Text="{Binding IsStudent}"
HorizontalOptions="End"
TextColor="#FD6C9E" />
</StackLayout>
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding TypePose}"
HorizontalOptions="Start"
TextColor="#FD6C9E" />
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
this line will take the results of your Query and map it into a Meeting object
return await _database.QueryAsync<Meeting>("Select Client.Prenom,Meeting.Id_Client, Meeting.DateRDV, Meeting.HourRDV,Meeting.TypePose,Meeting.IsStudent from 'Meeting' JOIN 'Client' on Meeting.Id_Client = Client.Id");
your Meeting class does not have any property that Client.Prenom can be mapped onto
Try putting ItemSource on your List,
Just edit this line in your xaml page, keep rest things as same
<ListView x:Name="listViewAllMeetings" ItemsSource="{Binding Path=Meetings}" SeparatorVisibility="Default" SeparatorColor="Silver" HasUnevenRows="True">
Just add this to your parent list ItemsSource="{Binding Path=Meeting}". I guess it will work for you.
Bind the Meeting with the query in your ViewModel.
EDIT ---------
//initialize these variables in your xaml.cs file
ObservableCollection<Meeting> _meetings { get; set; }
ObservableCollection<Meeting> Meetings { get { return _meetings} set { _meetings = value; NotifyPropertyChanged() } }
//Add this in your constructor of xaml.cs file
Meetings = App.Database.GetAllMeetings(); //Add
Correct your query as suggested by Jason
Add the Prenom refrence in Meeting.
public class Meeting {
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public int Prenom{ get; set; } //ADD here
.....
}
Let me know if it works or not.
Ihave this scenario: A ListView and every Cell is a frame. When showing the list all frames backgroudcolor is white when i click on frame i want the color to change in blie. I change the color but not refreshing. This is my code:
In xaml page
<pages:PopupPage.Resources>
<local1:ChangeFrameBackgroudColor x:Key="ChangeFrameBackgroudColor" />
</ResourceDictionary>-->
</pages:PopupPage.Resources>
<ListView x:Name="IzberiFirmaListView" HasUnevenRows="True" ItemsSource="{Binding KorisnikFirmi}" SelectedItem="{Binding IzbranaFirmaId } " Header="{Binding}" ItemTapped="IzberiFirmaListView_ItemTapped">
<ListView.ItemTemplate >
<DataTemplate>
<local:ExtendedViewCell SelectedBackgroundColor="#2188ff" >
<StackLayout Padding="20, 10" >
<Frame x:Name="frameLabel" BorderColor="#2188ff" BackgroundColor="{Binding IsActive, Converter={StaticResource ChangeFrameBackgroudColor}}">
<Label FontAttributes="Bold" FontSize="18" TextColor="Black" Text="{Binding Naziv}" ></Label>
</Frame>
</StackLayout>
</local:ExtendedViewCell>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.HeaderTemplate>
<DataTemplate>
<ContentView BackgroundColor="#006BE6" >
<Label Margin="10" HorizontalOptions="CenterAndExpand" Text="ОДБЕРЕТЕ ФИРМА" TextColor="White" FontSize="20" FontAttributes="Bold"/>
</ContentView>
</DataTemplate>
</ListView.HeaderTemplate>
</ListView>
In xaml.cs Page:
private void IzberiFirmaListView_ItemTapped(object sender, ItemTappedEventArgs e)
{
var vm = BindingContext as OdberiFirmaPopupViewModel;
var firm = e.Item as SysDashFirma;
vm.ChangeColorOnItemSelected(firm);
}
In viewModel
public List<SysDashFirma> KorisnikFirmi
{
get { return korisnikFirmi; }
set
{
if (korisnikFirmi != value)
{
korisnikFirmi = value;
SetProperty(ref korisnikFirmi, value);
OnPropertyChanged("KorisnikFirmi");
}
}
}
public void ChangeColorOnItemSelected(SysDashFirma firm)
{
if (_oldFirmSelected == firm)
{
//firm.BackColor = "#2188ff";
firm.IsActive = true;
UpdateSelectedFirmItemColor(firm);
}
else
{
if(_oldFirmSelected != null)
{
//_oldFirmSelected.BackColor = "#f5f5f5";
_oldFirmSelected.IsActive = false;
UpdateSelectedFirmItemColor(_oldFirmSelected);
}
// firm.BackColor = "#2188ff";
firm.IsActive = true;
UpdateSelectedFirmItemColor(firm);
}
_oldFirmSelected = firm;
}
private void UpdateSelectedFirmItemColor(SysDashFirma firm)
{
var index = KorisnikFirmi.IndexOf(firm);
KorisnikFirmi.Remove(firm);
KorisnikFirmi.Insert(index,firm);
}
Changes are made in the list ,flag is changed, probably cant refresh the bidning context to converter read all from start.
Use OnPropertyChanged for the IsActive property to notify the UI about its change.
public class Firm : INotifyPropertyChanged
{
private string name = "Unknown";
public string Name
{
get
{
return name;
}
set
{
name = value;
OnPropertyChanged();
}
}
private bool isActive;
public bool IsActive
{
get
{
return isActive;
}
set
{
isActive = value;
OnPropertyChanged();
}
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
I would suggest changing the IsActive property alone as removing and adding the item is not necessary.
<ListView
ItemTapped="ListView_ItemTapped"
ItemsSource="{Binding FirmCollection}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame
Padding="2"
BackgroundColor="{Binding IsActive, Converter={StaticResource converterTest}}">
<Label Text="{Binding Name}"/>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
ViewModel
public class ViewModel : INotifyPropertyChanged
{
....
....
private Firm oldSelectedFirm;
public Firm OldSelectedFirm
{
get
{
return oldSelectedFirm;
}
set
{
oldSelectedFirm = value;
OnPropertyChanged();
}
}
...
...
}
Xaml.cs
void ListView_ItemTapped(System.Object sender, Xamarin.Forms.ItemTappedEventArgs e)
{
var tappedFirm = (e.Item as Firm);
var vm = (BindingContext as ViewModel);
if (vm.OldSelectedFirm != null)
vm.OldSelectedFirm.IsActive = false;
tappedFirm.IsActive = true;
vm.OldSelectedFirm = tappedFirm;
}
I have a resuable control like this to display a loading spinner:
<?xml version="1.0" encoding="UTF-8"?>
<ContentView 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="Framework.Controls.Loading" x:Name="LoadingControl" IsVisible="{Binding LoadingIndicator}"
HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
<ContentView.Content>
<ActivityIndicator HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" Color="DarkBlue"
IsVisible="{Binding LoadingIndicator}"
IsRunning="{Binding LoadingIndicator}">
</ActivityIndicator>
</ContentView.Content>
</ContentView>
I am trying to consume it on a page like this:
<controls:Loading LoadingIndicator="{Binding IsLoading}"></controls:Loading>
However, the loading spinner fails to appear on-screen.
When I set the LoadingIndicator property to true, it appears just fine:
<controls:Loading LoadingIndicator="true"></controls:Loading>
My 'IsLoading' binding is definitely working properly, because if I place the following code directly in my XAML page it also works fine:
<ActivityIndicator HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"
Color="DarkBlue" IsVisible="{Binding IsLoading}" IsRunning="{Binding IsLoading}">
</ActivityIndicator>
Therefore, what is it about this that's wrong?
<controls:Loading LoadingIndicator="{Binding IsLoading}"></controls:Loading>
The 'IsLoading' property gets set on each of my pages from my view model. Here is a snippet from the view model:
public ICommand OnSave => new Command(async () =>
{
IsLoading = true;
await CreateItem();
IsLoading = false;
});
The code-behind for my control looks like this:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Loading : ContentView
{
public static readonly BindableProperty LoadingIndicatorProperty =
BindableProperty.Create(
propertyName: nameof(LoadingIndicator), typeof(bool),
typeof(Loading), default(string), BindingMode.OneWayToSource);
public bool LoadingIndicator
{
get => (bool)GetValue(LoadingIndicatorProperty);
set => SetValue(LoadingIndicatorProperty, value);
}
public Loading()
{
InitializeComponent();
BindingContext = this;
}
}
Do I need to write code to handle the change if the IsLoading binding gets updated?
This is the full code for the page where I am using the control:
ItemCreatePage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage Title="{Binding PageTitle}"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:userControls="clr-namespace:Framework.UserControls"
xmlns:converters="clr-namespace:Framework.ValueConverters"
xmlns:controls="clr-namespace:Framework.Controls;assembly=Framework.Android"
x:Class="Framework.Views.Item.ItemCreatePage">
<ContentPage.Resources>
<ResourceDictionary>
<converters:DoubleConverter x:Key="DoubleConverter"></converters:DoubleConverter>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<Grid>
<ScrollView>
<Grid RowSpacing="0" VerticalOptions="Start">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackLayout Grid.Row="1" Padding="20,20,20,0" VerticalOptions="Start">
<Label Text="Category" />
<userControls:BindablePicker
ItemsSource="{Binding Categories}"
SelectedItem="{Binding Path=Item.CategoryName, Mode=OneWay}"
DisplayMemberPath="Name"
SelectedValuePath="Id"
SelectedValue="{Binding Path=Item.CategoryId, Mode=TwoWay}"/>
<Label Text="Description" />
<Editor Text="{Binding Item.Description}" HeightRequest="100"/>
<Label Text="Area"/>
<Entry Text="{Binding Item.LineNumber}"/>
<Label Text="Identifier"/>
<Entry Text="{Binding Item.Identifier}"/>
<Label Text="Code"/>
<Entry Text="{Binding Item.Code}"/>
<Label Text="Priority" />
<userControls:BindablePicker
ItemsSource="{Binding Priorities}"
SelectedItem="{Binding Path=Item.ItemPriority, Mode=OneWay}"
DisplayMemberPath="Name"
SelectedValuePath="Id"
SelectedValue="{Binding Path=Item.ItemPriorityCode, Mode=TwoWay}"/>
<Label Text="Owner" />
<userControls:BindablePicker
ItemsSource="{Binding Users}"
SelectedItem="{Binding Path=Item.OwnerName, Mode=OneWay}"
DisplayMemberPath="Name"
SelectedValuePath="Id"
SelectedValue="{Binding Path=Item.OwnerId, Mode=TwoWay}"/>
<Label Text="Due Date" />
<DatePicker Date="{Binding Item.DateDue}" />
<Label Text="Date Identified" />
<DatePicker Date="{Binding Item.DateIdentified}" />
<Label Text="Status" />
<userControls:BindablePicker
ItemsSource="{Binding Statuses}"
SelectedItem="{Binding Path=Item.Status, Mode=OneWay}"
DisplayMemberPath="Name"
SelectedValuePath="Id"
SelectedValue="{Binding Path=Item.StatusCode, Mode=TwoWay}"/>
<Label Text="Comment" />
<Editor Text="{Binding Item.Comment}" HeightRequest="100"/>
<Label Text="IOM" />
<Entry Text="{Binding Item.OutcomeMeasurementInitial, Mode=TwoWay, Converter={StaticResource DoubleConverter}}" Keyboard="Numeric" />
<Label Text="FOM" />
<Entry Text="{Binding Item.OutcomeMeasurementFinal, Mode=TwoWay, Converter={StaticResource DoubleConverter}}" Keyboard="Numeric" />
<Label Text="Longitude" />
<Entry Text="{Binding Item.Longitude, Mode=TwoWay, Converter={StaticResource DoubleConverter}}" Keyboard="Numeric" />
<Label Text="Latitude" />
<Entry Text="{Binding Item.Latitude, Mode=TwoWay, Converter={StaticResource DoubleConverter}}" Keyboard="Numeric" />
<Button Margin="0,20,0,20" Command="{Binding OnSave}" BackgroundColor="{StaticResource Primary}"
BorderRadius="2" Text="Save" VerticalOptions="End" TextColor="White" ></Button>
</StackLayout>
</Grid>
</ScrollView>
<controls:Loading LoadingIndicator="{Binding IsLoading}"></controls:Loading>
</Grid>
</ContentPage.Content>
</ContentPage>
ItemCreatePage.xaml.cs
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ItemCreatePage : ContentPage
{
public ItemCreatePage ()
{
InitializeComponent ();
}
protected override async void OnAppearing()
{
var vm = BindingContext as ItemCreateViewModel;
vm.Item = new Data.Entities.Item();
await vm?.GetDeviceLocation();
base.OnAppearing();
}
}
The view model code:
public class ItemCreateViewModel : FormViewModel<Data.Entities.Item>
{
public async Task GetDeviceLocation()
{
this.Item = await this.Item.AddDeviceLocation();
OnPropertyChanged(nameof(this.Item));
}
public ILookupService LookupService { get; set; }
public IItemService ItemService { get; set; }
#region selectLists
public List<EnumListItem<ItemPriority>> Priorities => EnumExtensions.ToEnumList<ItemPriority>();
public List<EnumListItem<ItemStatus>> Statuses => EnumExtensions.ToEnumList<ItemStatus>();
public string PageTitle => $"{PageTitles.ItemCreate}{this.OfflineStatus}";
public List<Data.Entities.User> Users => UserService.GetAll(this.Offline);
public List<Data.Entities.Lookup> Categories => LookupService.GetLookups(this.Offline, LookupTypeCode.ItemCategories);
#endregion
public Data.Entities.Item Item { get; set; }
public ICommand OnSave => new Command(async () =>
{
await Loading(CreateItem);
});
private async Task CreateItem()
{
// ... Save logic is here
}
FormViewModel:
public class FormViewModel<T> : BaseViewModel
{
public IValidator<T> Validator => Resolve<IValidator<T>>();
public bool IsLoading { get; set; }
/// <summary>
/// Render a loading spinner whilst we process a request
/// </summary>
/// <param name="method"></param>
/// <returns></returns>
public async Task Loading(Func<Task> method)
{
IsLoading = true;
await method.Invoke();
IsLoading = false;
}
}
BaseViewModel:
public class BaseViewModel : IViewModelBase
{
public BaseViewModel()
{
if (this.GetCurrentUserToken() != null && !UserService.IsActive())
{
SettingService.ClearToken();
Bootstrapper.MasterDetailPage.IsPresented = false;
Application.Current.MainPage = new LoginPage();
}
}
public T Resolve<T>() => AutofacBootstrapper.Container.Resolve<T>();
public IUserService UserService => Resolve<IUserService>();
public INavigator Navigator => AutofacBootstrapper.Navigator;
public IDisplayAlertFactory DisplayAlert { get; set; }
public INavigation MasterNavigation => Bootstrapper.MasterDetailPage?.Detail?.Navigation;
public bool Offline => SettingService.GetSetting<bool>(CacheProperties.Offline);
public string OfflineStatus => this.Offline ? " - Offline" : string.Empty;
public Token GetCurrentUserToken() => SettingService.GetToken() ?? null;
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyname = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname));
}
}
You don't need to set your custom control's BindingContext here:
public Loading()
{
InitializeComponent();
BindingContext = this;//It's wrong!
//because the custom control's BindingContext will
//automatically be set to the BindingContext of
//the page where it's used which is what we usually want.
}
Here is a way to achieve what you want:
Your Custom Control's XAML:
<?xml version="1.0" encoding="UTF-8"?>
<ContentView 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="Framework.Controls.Loading" x:Name="LoadingControl"
HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
<ContentView.Content>
<ActivityIndicator x:Name="TheIndicator" HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand" Color="DarkBlue"/>
</ContentView.Content>
</ContentView>
And here is its code-behind:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Loading : ContentView
{
public static readonly BindableProperty LoadingIndicatorProperty =
BindableProperty.Create(propertyName:nameof(LoadingIndicator),
returnType: typeof(bool), declaringType: typeof(Loading), defaultValue: default(bool),
defaultBindingMode:BindingMode.Default, propertyChanged:LoadingBindingChanged);
private static void LoadingBindingChanged(BindableObject bindable, object oldvalue, object newvalue)
{
var view = (Loading)(bindable);
view.SetLoadingVisibility((bool)newvalue);
}
public Loading()
{
InitializeComponent();
IsVisible = false; // we do this because by default a view' IsVisible property is true
}
public bool LoadingIndicator
{
get => (bool)GetValue(LoadingIndicatorProperty);
set => SetValue(LoadingIndicatorProperty, value);
}
public void SetLoadingVisibility(bool show)
{
IsVisible = show;
TheIndicator.IsVisible = show;
TheIndicator.IsRunning = show;
}
}
You are not invoking PropertyChanged event when you change IsLoading property. If you want UI to refresh you need to invoke this event for the chosen property.
Change implementation of IsLoading property to:
private bool _isLoading;
public bool IsLoading
{
get=> _isLoading;
set
{
_isLoading=value;
OnPropertyChanged(nameof(IsLoading));
}
}
and it should work
I want to create a ListView which contains the department name. The ListView contains the CheckBox with department names. An user can check and unchecked the department and also the user can on clicking on select all check box user can select all department.
Which listview you want either a simple listview with textcell or imagecellits upto you, Here I'm posting code for listview with imagecell, also cell swipe option and just add Checkbox where you want to with its event and apply logics. Hope it works for you!
<AbsoluteLayout>
<ListView x:Name="Demolist" BackgroundColor="White" ItemSelected="Demolist_ItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ImageCell Height="30"
Text="{Binding deparment_name }"
Detail="{Binding department_description}"
ImageSource="ImageName.png">
<ImageCell.ContextActions>
<MenuItem x:Name="OnMore" Clicked="OnMore_Clicked" CommandParameter="{Binding .}" Text="More" />
<MenuItem x:Name="OnDelete" Clicked="OnDelete_Clicked" CommandParameter="{Binding .}" Text="Delete" IsDestructive="True" />
</ImageCell.ContextActions>
</ImageCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</AbsoluteLayout>
Checkbox is not a control present in XF framework, so I think you can not add checkbox in listview in Xamarin.form, but you can use different to display check and uncheck status.
<ContentPage
x:Class="test2.Page3"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:convert="clr-namespace:test2"
x:Name="ToDoPage">
<ContentPage.Resources>
<convert:converter1 x:Key="converterbool" />
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout>
<ListView x:Name="listview1" ItemsSource="{Binding todoList}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="4*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Text="{Binding ItemDescription}" VerticalOptions="Center" />
<Button
Grid.Column="1"
Command="{Binding Source={x:Reference ToDoPage}, Path=BindingContext.UpdateCheckBoxCommand}"
CommandParameter="{Binding Id}"
Opacity="0" />
<Image
Grid.Column="1"
HeightRequest="20"
HorizontalOptions="Center"
Source="{Binding IsDone, Converter={StaticResource converterbool}}"
VerticalOptions="Center" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
public class TodoItem:INotifyPropertyChanged
{
private string _Id;
public string Id
{
get { return _Id; }
set
{
_Id = value;
RaisePropertyChanged("Id");
}
}
private string _ItemDescription;
public string ItemDescription
{
get { return _ItemDescription; }
set
{
_ItemDescription = value;
RaisePropertyChanged("ItemDescription");
}
}
private bool _IsDone;
public bool IsDone
{
get { return _IsDone; }
set
{
_IsDone = value;
RaisePropertyChanged("IsDone");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
class ToDoViewModel:INotifyPropertyChanged
{
public ObservableCollection<TodoItem> todoList { get; set; }
public ICommand UpdateCheckBoxCommand { get; set; }
public ToDoViewModel()
{
todoList = new ObservableCollection<TodoItem>()
{
new TodoItem(){ Id = "1", ItemDescription = "Task 1", IsDone = false},
new TodoItem(){ Id = "2", ItemDescription = "Task 2", IsDone = false},
new TodoItem(){ Id = "3", ItemDescription = "Task 3", IsDone = false},
new TodoItem(){ Id = "4", ItemDescription = "Task 4", IsDone = false},
new TodoItem(){ Id = "5", ItemDescription = "Task 5",IsDone=false }
};
UpdateCheckBoxCommand = new Command((Id) => UpdateCheckBox(Id.ToString()));
}
private void UpdateCheckBox(string id)
{
IEnumerable<TodoItem> items = todoList.Where(x=>x.Id==id);
foreach(var item in items )
{
if (item.IsDone) item.IsDone = false;
else item.IsDone = true;
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
class converter1 : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool ischeck = (bool)value;
if(ischeck==false)
{
return "uncheck.png";
}
else
{
return "check.png";
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Page3 : ContentPage
{
public Page3 ()
{
InitializeComponent ();
this.BindingContext = new ToDoViewModel();
}
}
I want a structure like this :
Click this to see the desired output
But with my code i'm getting this : Click this to see the output
Here is my xaml code :
<ScrollView Orientation="Horizontal">
<StackLayout Orientation="Horizontal" VerticalOptions="Start">
<Grid x:Name="ImagesListViews" >
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
</Grid>
<local:BindableStackLayout x:Name="featuredEventsList">
<local:BindableStackLayout.ItemDataTemplate>
<DataTemplate>
<StackLayout Orientation="Vertical" Padding="0" Margin="-5,0,5,0" HorizontalOptions="Center" >
<StackLayout.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1" />
</StackLayout.GestureRecognizers>
<Image Source="{Binding ImageThumbURL}" Margin="0,0,0,0" WidthRequest="140" />
<Label Margin="0" Text="{Binding TitleInPrimaryLang}" FontSize="12" TextColor="Black" LineBreakMode="TailTruncation" WidthRequest="100"/>
</StackLayout>
</DataTemplate>
</local:BindableStackLayout.ItemDataTemplate>
</local:BindableStackLayout>
</StackLayout>
</ScrollView>
Any help would be highly appreciated. Thank you
you have to make customize control for this. Please go through this and let me know if any query.
1) Extend Scroll view with Customized template.
public class HorizontalListview : ScrollView
{
public static readonly BindableProperty ItemsSourceProperty =
BindableProperty.Create("ItemsSource", typeof(IEnumerable), typeof(HorizontalListview), default(IEnumerable));
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly BindableProperty ItemTemplateProperty =
BindableProperty.Create("ItemTemplate", typeof(DataTemplate), typeof(HorizontalListview), default(DataTemplate));
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
public event EventHandler<ItemTappedEventArgs> ItemSelected;
public static readonly BindableProperty SelectedCommandProperty =
BindableProperty.Create("SelectedCommand", typeof(ICommand), typeof(HorizontalListview), null);
public ICommand SelectedCommand
{
get { return (ICommand)GetValue(SelectedCommandProperty); }
set { SetValue(SelectedCommandProperty, value); }
}
public static readonly BindableProperty SelectedCommandParameterProperty =
BindableProperty.Create("SelectedCommandParameter", typeof(object), typeof(HorizontalListview), null);
public object SelectedCommandParameter
{
get { return GetValue(SelectedCommandParameterProperty); }
set { SetValue(SelectedCommandParameterProperty, value); }
}
public void Render()
{
if (ItemTemplate == null || ItemsSource == null)
return;
var layout = new StackLayout();
layout.Spacing = 0;
layout.Orientation = Orientation == ScrollOrientation.Vertical ? StackOrientation.Vertical : StackOrientation.Horizontal;
foreach (var item in ItemsSource)
{
var command = SelectedCommand ?? new Command((obj) =>
{
var args = new ItemTappedEventArgs(ItemsSource, item);
ItemSelected?.Invoke(this, args);
});
var commandParameter = SelectedCommandParameter ?? item;
var viewCell = ItemTemplate.CreateContent() as ViewCell;
viewCell.View.BindingContext = item;
viewCell.View.GestureRecognizers.Add(new TapGestureRecognizer
{
Command = command,
CommandParameter = commandParameter,
NumberOfTapsRequired = 1
});
layout.Children.Add(viewCell.View);
}
Content = layout;
}
}
2) Add Namespace top to your page.
xmlns:control="clr-namespace:**Projectname**.CustomControls"
3) Use Control,
<control:HorizontalListview Orientation="Horizontal">
<control:HorizontalListview.ItemTemplate>
<DataTemplate>
<ViewCell>
<!....Your Design.....>
</ViewCell>
</DataTemplate>
</control:HorizontalListview.ItemTemplate>
</control:HorizontalListview>
4) Bind your data.
**YourControlName**.ItemsSource = lLstPhotoGallery; // Your List
**YourControlName**.Render();