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.
Related
I'm using WinUI in combination with the microsoft MVVM toolkit.
However im experiencing some issues with Binding and can't figure out where the problem lies.
The ViewModel and models used within the ViewModel are of type observableObject. The Command is fired, and the data is fetched. However the binding is not showing a result in the UI, unless i change the xaml and hot reload the change.
My page:
<Page
x:Class="ThrustmasterGuide.Pages.WheelBasePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ThrustmasterGuide.Pages"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:model="using:ThrustmasterGuide.DataAccess.Context.Model"
xmlns:wheelbase="using:ThrustmasterGuide.ViewModel.Wheelbase"
xmlns:xaml="using:ABI.Microsoft.UI.Xaml"
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:converters="using:ThrustmasterGuide.Converters"
xmlns:wheelBase="using:ThrustmasterGuide.Model.WheelBase"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance wheelbase:WheelBaseViewModel, IsDesignTimeCreatable=True}">
<Page.Resources>
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
<converters:InvertBoolToVisibilityConverter x:Key="InvertBoolToVisibilityConverter" />
</Page.Resources>
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Loaded">
<core:EventTriggerBehavior.Actions>
<core:InvokeCommandAction Command="{x:Bind ViewModel.LoadWheelBaseCommand}" />
</core:EventTriggerBehavior.Actions>
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
<StackPanel Padding="16 16 16 16" Orientation="Vertical">
<StackPanel>
<TextBlock FontSize="18" Text="{x:Bind ViewModel.WheelBase.Name}" />
<TextBlock FontSize="18" Text="Symptomen:" />
<TextBlock Text="Kies hieronder een symptoom uit om te starten." />
<ProgressRing IsActive="true"
Visibility="{x:Bind ViewModel.LoadWheelBaseCommand.IsRunning, Converter={StaticResource BoolToVisibilityConverter}}" />
<TreeView ItemsSource="{x:Bind ViewModel.WheelBase.Symptoms, Mode=OneWay}">
<TreeView.ItemTemplate>
<DataTemplate x:DataType="wheelBase:SymptomModel">
<TreeViewItem ItemsSource="{x:Bind Children}" Content="{x:Bind Description}" />
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</StackPanel>
<Button VerticalAlignment="Bottom" Command="{x:Bind ViewModel.LoadWheelBaseCommand}"
Content="Refresh">
</Button>
</StackPanel>
My ViewModel:
public class WheelBaseViewModel : ObservableRecipient
{
public WheelBaseModel WheelBase { get; set; }
public string WheelBaseName { get; set; }
private readonly WheelBaseService _wheelBaseService;
public IAsyncRelayCommand LoadWheelBaseCommand { get; }
public WheelBaseViewModel(WheelBaseService wheelBaseService)
{
_wheelBaseService = wheelBaseService;
LoadWheelBaseCommand = new AsyncRelayCommand(FetchWheelBase);
}
public async Task FetchWheelBase()
{
WheelBase = await _wheelBaseService.GetWheelBase(WheelBaseName);
}
}
My model:
namespace ThrustmasterGuide.Model.WheelBase
{
public class WheelBaseModel : ObservableObject
{
public string Name { get; set; }
public ObservableCollection<SymptomModel> Symptoms { get; set; }
}
}
My Code behind:
public sealed partial class WheelBasePage : Page
{
public WheelBasePage()
{
this.InitializeComponent();
this.DataContext = App.Current.Services.GetService<WheelBaseViewModel>();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
this.ViewModel.WheelBaseName = e.Parameter as string;
}
public WheelBaseViewModel ViewModel => (WheelBaseViewModel)DataContext;
}
What is it that i missed to make the UI bind to the WheelBaseModel values?
Update I added mode=OneWay, but still not updating.
Should it be noted that im showing pages within a content frame after navigation?
{x:Bind} has a default mode of OneTime, unlike {Binding}, which has a default mode of OneWay.
I believe you need to use SetProperty so it's known when to raise such events?
https://learn.microsoft.com/en-us/windows/communitytoolkit/mvvm/observableobject
namespace ThrustmasterGuide.Model.WheelBase
{
public class WheelBaseModel : ObservableObject
{
public string Name
{
get => name;
set => SetProperty(ref name, value);
}
public ObservableCollection<SymptomModel> Symptoms { get; set; }
}
and also bind mode = OneWay
<TextBlock FontSize="18" Text="{x:Bind ViewModel.WheelBase.Name, Mode=OneWay}" />
No indication that your properties notify of their changes.
https://learn.microsoft.com/dotnet/api/system.componentmodel.inotifypropertychanged
While trying to show data over form in Xamarin Forms app, a property in the model which is nullable DateTime is freezing the app. I know this is the offending property because if I remove Xaml for this binding, everything works fine.
While stepping through the code, the LoginTime property from source (fetched from db) and the getter from ViewModel's User property are going into infinite loop. I can't possibly imagine why.
Model:
public class User : PersistentEntity
{
private string _userId;
public string UserId
{
get => _userId;
set => SetProperty(ref _userId, value);
}
private string _userName;
public string UserName
{
get => _userName;
set => SetProperty(ref _userName, value);
}
private string _email;
public string Email
{
get => _email;
set => SetProperty(ref _email, value);
}
private DateTime? _loginTime;
public DateTime? LoginTime
{
get => _loginTime;
set => SetProperty(ref _loginTime, value);
}
}
ViewModel:
public class SettingsViewModel : ViewModelBase
{
private readonly SettingsSession _settingsSession;
private User _user;
public User User
{
get => _user;
set => SetProperty(ref _user, value);
}
public SettingsViewModel(INavigationService navigationService, SettingsSession settingsSession)
: base(navigationService)
{
Title = "Settings";
_settingsSession = settingsSession;
}
private DelegateCommand _fetchUserCommand;
public DelegateCommand FetchUserCommand =>
_fetchUserCommand ?? (_fetchUserCommand = new DelegateCommand(ExecuteFetchUserCommand, CanExecuteFetchUserCommand));
async void ExecuteFetchUserCommand()
{
User = await _settingsSession.FetchUser();
}
bool CanExecuteFetchUserCommand()
{
return true;
}
}
View:
<TableView Intent="Settings">
<TableRoot>
<TableSection Title="User">
<ViewCell>
<StackLayout Orientation="Horizontal"
Padding="2">
<Label Text="User Id"
WidthRequest="150"
Margin="5,0,0,0"
VerticalOptions="CenterAndExpand"/>
<Entry WidthRequest="250"
HorizontalTextAlignment="Start"
VerticalTextAlignment="Center"
Placeholder="UserId"
Text="{Binding User.UserId}"/>
</StackLayout>
</ViewCell>
<ViewCell>
<StackLayout Orientation="Horizontal"
Padding="2">
<Label Text="User Name"
WidthRequest="150"
Margin="5,0,0,0"
VerticalOptions="CenterAndExpand"/>
<Entry WidthRequest="250"
HorizontalTextAlignment="Start"
VerticalTextAlignment="Center"
Placeholder="User Name"
Text="{Binding User.UserName}"/>
</StackLayout>
</ViewCell>
<ViewCell>
<StackLayout Orientation="Horizontal"
Padding="2">
<Label Text="Login Time"
WidthRequest="150"
Margin="5,0,0,0"
VerticalOptions="CenterAndExpand"/>
<Entry WidthRequest="250"
HorizontalTextAlignment="Start"
VerticalTextAlignment="Center"
Placeholder="Login Time"
Text="{Binding User.LoginTime, StringFormat='{0:dd-MM-yyyy HH:mm}'}"/>
</StackLayout>
</ViewCell>
</TableSection>
</TableRoot>
</TableView>
Any help is appreciated.
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.
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");
}
}
I would like to bind an Entry with a Slider and vice versa. I wrote something like this:
<Entry x:Name="myEntry" Text="{Binding Value, Mode=TwoWay}" BindingContext="{x:Reference slider}"/>
<Slider x:Name="slider" Maximum="100" Minimum="0" BindingContext="{x:Reference myEntry}"/>
When I use the slider, the value in the entry is updated, but when I put manually some value in the Entry, the value append a 0 or change to 0. What can be the problem. I am working on android.
You should bind both your Slider and Entry to string/ integer in a backing View Model.
class MyViewModel
{
private int _sliderValue;
public string EntryText
{
get => _sliderValue.ToString();
set => SetProperty(ref _sliderValue, int.Parse(value) );
}
public int SliderValue
{
get => _sliderValue;
set => (ref _sliderValue, value);
}
}
And in the view
<Entry Text="{Binding EntryText}" />
<Slider Value="{Binding SliderValue}" />
More MVVM Info
Fresh MVVM for Xamarin
Caliburn Micro for Xamarin
please refer the following xaml code
<Frame HorizontalOptions="FillAndExpand" VerticalOptions="StartAndExpand">
<StackLayout>
<Entry Text="{Binding Path=Value}"
FontSize="18"
x:Name="label"
BindingContext="{x:Reference Name=slider}"/>
<Slider x:Name="slider"
Maximum="1500"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</Frame>
The same can be achieved by using the view model please refer the code
Xaml
<Frame HorizontalOptions="FillAndExpand" VerticalOptions="StartAndExpand">
<StackLayout>
<Entry x:Name="NameEntry" Placeholder="Enter Name" Text="{Binding Forename,Mode=TwoWay}" />
<Slider Value="{Binding Forename}" Minimum="0" Maximum="10"/>
</StackLayout>
</Frame>
------------------- now see the model in a c# file-----------------------------------
public partial class MainPage : ContentPage
{
public class DetailsViewModel : INotifyPropertyChanged
{
int forename;
public int Forename
{
get
{
return forename;
}
set
{
if (forename != value)
{
forename = value;
OnPropertyChanged ("Forename");
}
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
var changed = PropertyChanged;
if (changed != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
public MainPage()
{
InitializeComponent();
BindingContext = new DetailsViewModel();
}
}