Show JSON response through binding (Xamarin forms) - xamarin

i showed json response now i want to it to show like in a proper format in the editor section. Below is my code: (i am trying this from past two days but i dont know how to show this in a proper format)
Page 1 from where i passed the json data:
public partial class MainPage : ContentPage
{
private static readonly HttpClient client = new HttpClient();
public String test;
public MainPage()
{
InitializeComponent();
// Task.Run(async () => await GetinfoAsync());
GetinfoAsync();
}
public async Task GetinfoAsync()
{
var responseString = await client.GetStringAsync("https://reqres.in/api/users/2");
UserResponse result = JsonConvert.DeserializeObject<UserResponse>(responseString);
if (result != null)
{
Device.BeginInvokeOnMainThread(() =>
{
test = dis.Text = "Id:- " + result.Data.id + "\nEmail:- " + result.Data.email + "\nFirst Name:- " + result.Data.first_name + "\nLast Name:- " + result.Data.last_name + "\nImage:- " + result.Data.avatar; dis.Text = test;
});
}
}
private async void click_me(Object sender, EventArgs args)
{
await this.Navigation.PushAsync(new Data(test));
}
Page 2 where i get the json data from page 1
public partial class Data : ContentPage
{
public MyUser obj;
public String show;
public Data(String test)
{
show = test;
InitializeComponent();
displaylabel.Text = test;
}
}
Page 2 Xaml
<StackLayout Padding="20">
<Editor Text="{Binding obj.id}" IsReadOnly="True"/>
<Editor Text="{Binding obj.first_name}" IsReadOnly="True"/>
<Editor Text="{Binding obj.last_name}" IsReadOnly="True"/>
<Editor Text="{Binding obj.email}" IsReadOnly="True"/>
<Editor Text="Image" IsReadOnly="True"/>
<Image HeightRequest="100" WidthRequest="100" Source="{Binding obj.avatar}"/>
<Label Text="show json"
x:Name="displaylabel"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
</StackLayout>
myuser.cs
public class MyUser
{
public int id { get; set; }
public string email { get; set; }
public string first_name { get; set; }
public string last_name { get; set; }
public string avatar { get; set; }
}
public class UserResponse
{
public MyUser Data { get; set; }
}

Create new page send json data from your last page and populate here by belo code.
XAML code
<Grid Margin="20,50">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Editor Grid.Row="0" Grid.Column="0" Text="id" IsReadOnly="True"/>
<Editor x:Name="txtId" Grid.Row="0" Grid.Column="1" IsReadOnly="True"/>
<Editor Grid.Row="1" Grid.Column="0" Text="First name" IsReadOnly="True"/>
<Editor x:Name="txtFirstname" Grid.Row="1" Grid.Column="1" IsReadOnly="True"/>
<Editor Grid.Row="2" Grid.Column="0" Text="Last name" IsReadOnly="True"/>
<Editor x:Name="txtLastname" Grid.Row="2" Grid.Column="1" IsReadOnly="True"/>
<Editor Grid.Row="3" Grid.Column="0" Text="Email" IsReadOnly="True"/>
<Editor x:Name="txtEmail" Grid.Row="3" Grid.Column="1" IsReadOnly="True"/>
<Editor Grid.Row="4" Grid.Column="0" Text="Image" IsReadOnly="True"/>
<Image x:Name="imgUser" Grid.Row="4" Grid.Column="1" HeightRequest="100" WidthRequest="100" />
</Grid>
Code behind
public partial class Homepage : ContentPage
{
private static readonly HttpClient client = new HttpClient();
myuser _Data;
public Homepage(myuser Data)
{
InitializeComponent();
//this.BindingContext = new MainPageViewModel();
_Data = Data;
}
protected override void OnAppearing()
{
base.OnAppearing();
txtId.Text = _Data.id.ToString();
txtFirstname.Text = _Data.first_name;
txtLastname.Text = _Data.last_name;
txtEmail.Text = _Data.email;
imgUser.Source = _Data.avatar;
}
}
Hope it will help you
Thanks

First, you can follow the MVVM pattern to make this easier in the long run.
But if you want to proceed with your existing code then you can try this:
First, setup your binding context in your Page 2 xaml:
<ContentPage x:Name="MyPage" ... />
<StackLayout Padding="20">
<Editor BindingContext="{x:Reference Name=MyPage}" Text="{Binding obj.id}" IsReadOnly="True"/>
<Editor BindingContext="{x:Reference Name=MyPage}" Text="{Binding obj.first_name}" IsReadOnly="True"/>
<Editor BindingContext="{x:Reference Name=MyPage}" Text="{Binding obj.last_name}" IsReadOnly="True"/>
<Editor BindingContext="{x:Reference Name=MyPage}" Text="{Binding obj.email}" IsReadOnly="True"/>
<Editor BindingContext="{x:Reference Name=MyPage}" Text="Image" IsReadOnly="True"/>
<Image x:Name="myImage" HeightRequest="100" WidthRequest="100"/>
<Label Text="show json"
x:Name="displaylabel"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
</StackLayout>
In your code behind:
Instead of passing string test, you can declare a UserReponse object:
private static readonly HttpClient client = new HttpClient();
public String test; //instead of this
public UserReponse result; //do this
Then modify this:
public async Task GetinfoAsync()
{
var responseString = await client.GetStringAsync("https://reqres.in/api/users/2");
result = JsonConvert.DeserializeObject<UserResponse>(responseString); //modify to this
if (result != null)
{
Device.BeginInvokeOnMainThread(() =>
{
test = dis.Text = "Id:- " + result.Data.id + "\nEmail:- " + result.Data.email + "\nFirst Name:- " + result.Data.first_name + "\nLast Name:- " + result.Data.last_name + "\nImage:- " + result.Data.avatar; dis.Text = test;
});
}
}
Then pass it:
await this.Navigation.PushAsync(new Data(result));
Replace your page 2 code behind with:
public partial class Data : ContentPage
{
private MyUser _obj;
public MyUser obj
{
get{return _obj;}
set
{
_obj = value;
OnPropertyChanged();
}
}
public String show;
public Data(UserReponse result)
{
show = test;
InitializeComponent();
obj = result.Data;
myImage.Source = ImageSource.FromUri(new Uri(obj.avatar));
displaylabel.Text = test;
}
}

Try to pass the UserResponse to the Data Page directly,and binding it to the page:
public partial class MainPage : ContentPage
{
private static readonly HttpClient client = new HttpClient();
public UserResponse test;
public MainPage()
{
InitializeComponent();
// Task.Run(async () => await GetinfoAsync());
GetinfoAsync();
}
public async Task GetinfoAsync()
{
var responseString = await client.GetStringAsync("https://reqres.in/api/users/2");
UserResponse result = JsonConvert.DeserializeObject<UserResponse>(responseString);
if (result != null)
{
Device.BeginInvokeOnMainThread(() =>
{
test = result;
});
}
}
private async void click_me(Object sender, EventArgs args)
{
await this.Navigation.PushAsync(new Data(test));
}
}
the Data page:
public partial class Data : ContentPage
{
public UserResponse obj;
public String show;
public Data(UserResponse test)
{
obj= test;
InitializeComponent();
BindingContext = obj;
}
}
the xaml :
<StackLayout Padding="20">
<Editor Text="{Binding Data.id}" IsReadOnly="True"/>
<Editor Text="{Binding Data.first_name}" IsReadOnly="True"/>
<Editor Text="{Binding Data.last_name}" IsReadOnly="True"/>
<Editor Text="{Binding Data.email}" IsReadOnly="True"/>
<Editor Text="Image" IsReadOnly="True"/>
<Image HeightRequest="100" WidthRequest="100" Source="{Binding Data.avatar}"/>
<Label Text="show json"
x:Name="displaylabel"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
</StackLayout>

Related

PropertyChanged Event doesn't reflect new Image.Source on first run

I'm working on mobile app that kinda simulates how to build a Desktop Computer by dragging and dropping an image in order. I work with DropGestureRecognizer with AllowDrop set to True for the Drop Zone Image controls and DragGestureRecognizer with CanDrag set to True for the Drag Image Objects.
The idea is the Drag Image Objects will get dragged and dropped to a Drop Zone Image Control and if they dropped the correct Drag Image, the Drop Zone will accept this as the Image.Source and another Drop Zone will be stacked in front of it to work with the next Desktop Computer component. Otherwise, it would get rejected and the Image.Source will be set to empty or null.
However, i'm encountering a weird issue that, on the first incorrect image dragged, the Image.Source gets reset to empty or null by property but on the actual UI, it doesn't. On the succeeding EventHandler execution, it does get reset. I have a TapGestureRecognizer on the Drop Zone to check if there is an Image.Source set or not.
I'm sorry if my explanation is a bit confusing because English is not my native language and i'm having a hard time explaining it. So please refer to below example in GIF and my code:
View (.xaml):
<ContentPage.Content>
<Grid RowDefinitions="Auto,60,*"
ColumnDefinitions="*">
<RelativeLayout Grid.Row="0" Grid.Column="0" HorizontalOptions="Center">
<Image x:Name="imgCaseDropZone"
RelativeLayout.XConstraint="0"
RelativeLayout.YConstraint="0"
HeightRequest="{Binding ScreenWidth}"
WidthRequest="{Binding ScreenWidth}"
PropertyChanged="CaseDropZone_PropertyChanged"
BackgroundColor="LightGray">
<Image.GestureRecognizers>
<DropGestureRecognizer AllowDrop="True" />
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped" />
</Image.GestureRecognizers>
</Image>
</RelativeLayout>
<Label Grid.Row="1" Grid.Column="0"
x:Name="lblDirections"
Text="Directions: Drag the components below in its proper order to the drop zone above."
TextColor="Black"
Padding="10"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
<ScrollView Grid.Row="2"
Orientation="Horizontal"
Margin="5">
<StackLayout Orientation="Horizontal">
<!--#region Case -->
<Grid RowDefinitions="*,Auto"
IsVisible="{Binding CaseIsVisible}">
<Image Grid.Row="0"
x:Name="imgCaseDragObject"
Source="{Binding CaseImgSource}">
<Image.GestureRecognizers>
<DragGestureRecognizer CanDrag="True" />
</Image.GestureRecognizers>
</Image>
<Label Grid.Row="1"
Text="{Binding CaseLabel}"
TextColor="White"
FontSize="Medium"
BackgroundColor="Gray"
HorizontalTextAlignment="Center" />
</Grid>
<!--#endregion-->
<!--#region Case Cover -->
<Grid RowDefinitions="*,Auto"
IsVisible="{Binding CaseCoverIsVisible}">
<Image Grid.Row="0"
Source="{Binding CaseCoverImgSource}">
<Image.GestureRecognizers>
<DragGestureRecognizer CanDrag="True" />
</Image.GestureRecognizers>
</Image>
<Label Grid.Row="1"
Text="{Binding CaseCoverLabel}"
TextColor="White"
FontSize="Medium"
BackgroundColor="Gray"
HorizontalTextAlignment="Center" />
</Grid>
<!--#endregion-->
<!--#region Case Screw -->
<Grid RowDefinitions="*,Auto"
IsVisible="{Binding CaseScrewIsVisible}">
<Image Grid.Row="0"
Source="{Binding CaseScrewImgSource}">
<Image.GestureRecognizers>
<DragGestureRecognizer CanDrag="True" />
</Image.GestureRecognizers>
</Image>
<Label Grid.Row="1"
Text="{Binding CaseScrewLabel}"
TextColor="White"
FontSize="Medium"
BackgroundColor="Gray"
HorizontalTextAlignment="Center" />
</Grid>
<!--#endregion-->
<!--#region Hard Disk Drive -->
<Grid RowDefinitions="*,Auto"
IsVisible="{Binding HardDiskDriveIsVisible}">
<Image Grid.Row="0"
Source="{Binding HardDiskDriveImgSource}">
<Image.GestureRecognizers>
<DragGestureRecognizer CanDrag="True" />
</Image.GestureRecognizers>
</Image>
<Label Grid.Row="1"
Text="{Binding HardDiskDriveLabel}"
TextColor="White"
FontSize="Medium"
BackgroundColor="Gray"
HorizontalTextAlignment="Center" />
</Grid>
<!--#endregion-->
<!--#region Heatsink -->
<Grid RowDefinitions="*,Auto"
IsVisible="{Binding HeatsinkIsVisible}">
<Image Grid.Row="0"
Source="{Binding HeatsinkImgSource}">
<Image.GestureRecognizers>
<DragGestureRecognizer CanDrag="True" />
</Image.GestureRecognizers>
</Image>
<Label Grid.Row="1"
Text="{Binding HeatsinkLabel}"
TextColor="White"
FontSize="Medium"
BackgroundColor="Gray"
HorizontalTextAlignment="Center" />
</Grid>
<!--#endregion-->
<!--#region Memory Module -->
<Grid RowDefinitions="*,Auto"
IsVisible="{Binding MemoryModuleIsVisible}">
<Image Grid.Row="0"
Source="{Binding MemoryModuleImgSource}">
<Image.GestureRecognizers>
<DragGestureRecognizer CanDrag="True" />
</Image.GestureRecognizers>
</Image>
<Label Grid.Row="1"
Text="{Binding MemoryModuleLabel}"
TextColor="White"
FontSize="Medium"
BackgroundColor="Gray"
HorizontalTextAlignment="Center" />
</Grid>
<!--#endregion-->
<!--#region Motherboard -->
<Grid RowDefinitions="*,Auto"
IsVisible="{Binding MotherboardIsVisible}">
<Image Grid.Row="0"
Source="{Binding MotherboardImgSource}">
<Image.GestureRecognizers>
<DragGestureRecognizer CanDrag="True" />
</Image.GestureRecognizers>
</Image>
<Label Grid.Row="1"
Text="{Binding MotherboardLabel}"
TextColor="White"
FontSize="Medium"
BackgroundColor="Gray"
HorizontalTextAlignment="Center" />
</Grid>
<!--#endregion-->
<!--#region Motherboard Screw -->
<Grid RowDefinitions="*,Auto"
IsVisible="{Binding MotherboardScrewIsVisible}">
<Image Grid.Row="0"
Source="{Binding MotherboardScrewImgSource}">
<Image.GestureRecognizers>
<DragGestureRecognizer CanDrag="True" />
</Image.GestureRecognizers>
</Image>
<Label Grid.Row="1"
Text="{Binding MotherboardScrewLabel}"
TextColor="White"
FontSize="Medium"
BackgroundColor="Gray"
HorizontalTextAlignment="Center" />
</Grid>
<!--#endregion-->
<!--#region Power Supply -->
<Grid RowDefinitions="*,Auto"
IsVisible="{Binding PowerSupplyIsVisible}">
<Image Grid.Row="0"
Source="{Binding PowerSupplyImgSource}">
<Image.GestureRecognizers>
<DragGestureRecognizer CanDrag="True" />
</Image.GestureRecognizers>
</Image>
<Label Grid.Row="1"
Text="{Binding PowerSupplyLabel}"
TextColor="White"
FontSize="Medium"
BackgroundColor="Gray"
HorizontalTextAlignment="Center" />
</Grid>
<!--#endregion-->
<!--#region Processor -->
<Grid RowDefinitions="*,Auto"
IsVisible="{Binding ProcessorIsVisible}">
<Image Grid.Row="0"
Source="{Binding ProcessorImgSource}">
<Image.GestureRecognizers>
<DragGestureRecognizer CanDrag="True" />
</Image.GestureRecognizers>
</Image>
<Label Grid.Row="1"
Text="{Binding ProcessorLabel}"
TextColor="White"
FontSize="Medium"
BackgroundColor="Gray"
HorizontalTextAlignment="Center" />
</Grid>
<!--#endregion-->
</StackLayout>
</ScrollView>
</Grid>
</ContentPage.Content>
View Code (.xaml.cs):
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class AssemblyPage : ContentPage
{
string caseSource;
public AssemblyPage()
{
InitializeComponent();
}
int dragCount = 0;
private void CaseDropZone_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Source")
{
caseSource = imgCaseDropZone.Source.ToString().Split(':').Last().Trim();
if (string.IsNullOrEmpty(caseSource))
{
return;
}
if (caseSource != "img_assembly_case.png")
{
imgCaseDropZone.Source = string.Empty;
lblDirections.Text = $"Drag Count: {++dragCount}";
}
}
}
private void TapGestureRecognizer_Tapped(object sender, EventArgs e)
{
Application.Current.MainPage.DisplayAlert("", $"{imgCaseDropZone.Source}", "OK");
}
}
ViewModel (.cs):
public class AssemblyViewModel : BaseVM
{
public double ScreenWidth => Xamarin.Forms.Application.Current.MainPage.Width;
#region Case
bool caseIsVisible;
public bool CaseIsVisible
{
get => caseIsVisible;
set => SetProperty(ref caseIsVisible, value);
}
public string CaseImgSource { get; }
public string CaseLabel { get; }
#endregion
#region CaseCover
bool caseCoverIsVisible;
public bool CaseCoverIsVisible
{
get => caseCoverIsVisible;
set => SetProperty(ref caseCoverIsVisible, value);
}
public string CaseCoverImgSource { get; }
public string CaseCoverLabel { get; }
#endregion
#region HardDiskDrive
bool hardDiskDriveIsVisible;
public bool HardDiskDriveIsVisible
{
get => hardDiskDriveIsVisible;
set => SetProperty(ref hardDiskDriveIsVisible, value);
}
public string HardDiskDriveImgSource { get; }
public string HardDiskDriveLabel { get; }
#endregion
#region CaseScrew
bool caseScrewIsVisible;
public bool CaseScrewIsVisible
{
get => caseScrewIsVisible;
set => SetProperty(ref caseScrewIsVisible, value);
}
public string CaseScrewImgSource { get; }
public string CaseScrewLabel { get; }
#endregion
#region Heatsink
bool heatsinkIsVisible;
public bool HeatsinkIsVisible
{
get => heatsinkIsVisible;
set => SetProperty(ref heatsinkIsVisible, value);
}
public string HeatsinkImgSource { get; }
public string HeatsinkLabel { get; }
#endregion
#region MemoryModule
bool memoryModuleIsVisible;
public bool MemoryModuleIsVisible
{
get => memoryModuleIsVisible;
set => SetProperty(ref memoryModuleIsVisible, value);
}
public string MemoryModuleImgSource { get; }
public string MemoryModuleLabel { get; }
#endregion
#region Motherboard
bool motherboardIsVisible;
public bool MotherboardIsVisible
{
get => motherboardIsVisible;
set => SetProperty(ref motherboardIsVisible, value);
}
public string MotherboardImgSource { get; }
public string MotherboardLabel { get; }
#endregion
#region MotherboardScrew
bool motherboardScrewIsVisible;
public bool MotherboardScrewIsVisible
{
get => motherboardScrewIsVisible;
set => SetProperty(ref motherboardScrewIsVisible, value);
}
public string MotherboardScrewImgSource { get; }
public string MotherboardScrewLabel { get; }
#endregion
#region PowerSupply
bool powerSupplyIsVisible;
public bool PowerSupplyIsVisible
{
get => powerSupplyIsVisible;
set => SetProperty(ref powerSupplyIsVisible, value);
}
public string PowerSupplyImgSource { get; }
public string PowerSupplyLabel { get; }
#endregion
#region Processor
bool processorIsVisible;
public bool ProcessorIsVisible
{
get => processorIsVisible;
set => SetProperty(ref processorIsVisible, value);
}
public string ProcessorImgSource { get; }
public string ProcessorLabel { get; }
#endregion
public AssemblyViewModel()
{
CaseIsVisible = true;
CaseImgSource = "img_assembly_case.png";
CaseLabel = "Case";
CaseCoverIsVisible = true;
CaseCoverImgSource = "img_assembly_case_cover.png";
CaseCoverLabel = "Case Cover";
CaseScrewIsVisible = true;
CaseScrewImgSource = "img_assembly_case_screw.png";
CaseScrewLabel = "Case Screw";
HardDiskDriveIsVisible = true;
HardDiskDriveImgSource = "img_assembly_hard_disk_drive.png";
HardDiskDriveLabel = "Hard Disk Drive";
HeatsinkIsVisible = true;
HeatsinkImgSource = "img_assembly_heat_sink.png";
HeatsinkLabel = "Heatsink";
MemoryModuleIsVisible = true;
MemoryModuleImgSource = "img_assembly_memory_module.png";
MemoryModuleLabel = "Memory Module";
MotherboardIsVisible = true;
MotherboardImgSource = "img_assembly_motherboard.png";
MotherboardLabel = "Motherboard";
MotherboardScrewIsVisible = true;
MotherboardScrewImgSource = "img_assembly_motherboard_screw.png";
MotherboardScrewLabel = "Motherboard Screw";
PowerSupplyIsVisible = true;
PowerSupplyImgSource = "img_assembly_power_supply.png";
PowerSupplyLabel = "Power Supply";
ProcessorIsVisible = true;
ProcessorImgSource = "img_assembly_processor.png";
ProcessorLabel = "Processor";
}
}
Current Build demo in GIF: https://imgur.com/a/oLeM9DV (i can't directly link the GIF because its too big)
Because when you trigger the PropertyChanged event, the drag and drop action has already been completed and the image has been automatically set to the target.
We could make that judgment as soon as we drag it over the target with DragOver event.Then make an action when the drag and drop is complete with Drop event.
For example,change the codes of the Image (imgCaseDropZone):
<Image x:Name="imgCaseDropZone"
RelativeLayout.XConstraint="0"
RelativeLayout.YConstraint="0"
RelativeLayout.WidthConstraint="400"
RelativeLayout.HeightConstraint="400"
BackgroundColor="LightGray"
>
<Image.GestureRecognizers>
<DropGestureRecognizer AllowDrop="{Binding CaseIsVisible}" DragOver="CaseDropZone_DragOver" Drop="CaseDropZone_Drop"/>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped" />
</Image.GestureRecognizers>
</Image>
in the code behind:
private void CaseDropZone_DragOver(object sender, DragEventArgs e)
{
FileImageSource file = (FileImageSource)e.Data.Image;
string name = file.File;
if (name != "img_assembly_case.png")
{
e.Data.Image = string.Empty; // set the value string.Empty when drag it over the target,then it will fill the empty source to the image.
}
}
private async void CaseDropZone_Drop(object sender, DropEventArgs e)
{
var ctx = (BindingContext as AssemblyViewModel);
FileImageSource source = (FileImageSource)await e.Data.GetImageAsync();
string name = source.File;
if (name == "img_assembly_case.png")
{
ctx.CaseIsVisible = false;
imgMotherboardDropZone.IsVisible = true;
}
else
{
Global.Score.PCAssembly -= 5;
DisplayAlert($"Score: {Global.Score.PCAssembly}", "The pre-requisite component for this part has not yet been placed.", "OK");
}
}
Other Images are modified like this.

How to implement a command to delete an item from my database in xamarinforms mvvm style

Currently i have a clicked function that delete an items :
async void DeleteButtonClicked(object sender, EventArgs e)
{
ImageButton button = sender as ImageButton;
var agenda = button.BindingContext as Agenda;
await App.Database.DeleteAgendaAsync(agenda);
await Navigation.PopAsync();
}
I want to implement this MVVM style with a command, the basic of my app is that i have an AgendaPage that loads items in a collection view, right now i want to be able to call the command on this icon and so it delete the item.
Thanks for your help.
Currently in my PageViewModel i only have this
I have to pass the current agenda to the DeleteAgendaAsync() method but it's unclear to me where to get it.
public Command DeleteAgendaCommand { get; set; }
public AgendaPageViewModel()
{
DeleteAgendaCommand = new Command(async () => await DeleteAgenda());
}
async Task DeleteAgenda()
{
await App.Database.DeleteAgendaAsync();
}
AgendaDatabase.cs in the Database folder
using System;
using System.Collections.Generic;
using System.Text;
using SQLite;
using Calculette.Models;
using System.Threading.Tasks;
using Calculette.ViewModel;
namespace Calculette.Database
{
public class AgendaDatabase
{
readonly SQLiteAsyncConnection database;
public AgendaDatabase(string dbPath)
{
database = new SQLiteAsyncConnection(dbPath);
database.CreateTableAsync<Agenda>().Wait();
}
// Get all agenda
public Task<List<Agenda>> GetAgendasAsync()
{
return database.Table<Agenda>().ToListAsync();
}
// Get specific agenda
public Task<Agenda> GetAgendaAsync(int id)
{
return database.Table<Agenda>()
.Where(i => i.ID == id)
.FirstOrDefaultAsync();
}
// Insert new agenda (save)
public Task<int> SaveAgendaAsync(Agenda agenda)
{
if (agenda.ID != 0)
{
return database.UpdateAsync(agenda);
}
else
{
return database.InsertAsync(agenda);
}
}
//Delete specific agenda
public Task<int> DeleteAgendaAsync(Agenda agenda)
{
return database.DeleteAsync(agenda);
}
public Task<int> AddAgendaAsync(Agenda agenda)
{
return database.InsertAsync(agenda);
}
}
}
Agenda.cs in the Models folder
[Table("Agenda")]
public class Agenda
{
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public string Topic { get; set; }
public string Duration { get; set; }
public DateTime Date { get; set; }
This is the NewFormViewModel.cs which is used to create new agenda items to the collectionview, it feels like i would need to access all the items added there, but im unsure on how to do that for the DeleteCommand
using Calculette.Database;
using Calculette.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using Xamarin.Forms;
namespace Calculette.ViewModel
{
class NewFormViewModel : BaseViewModel
{
public Command AgendaSaveFormCommand { get; set; }
public NewFormViewModel()
{
// Initialise la valeur du DatePicker a celle d'aujourd'hui
this.Date = DateTime.Now;
// Commande pour la sauvegarde sur la page NewFormPage ( voir SaveForm() plus bas)
AgendaSaveFormCommand = new Command(async () => await SaveForm(), () => !IsBusy);
}
// Création des propriétés d'un agenda
private string topic;
public string Topic
{
get => topic;
set
{
topic = value;
NotifyPropertyChanged();
}
}
private string duration;
public string Duration
{
get => duration;
set
{
duration = value;
NotifyPropertyChanged();
}
}
private DateTime date;
public DateTime Date
{
get => date;
set
{
date = value;
NotifyPropertyChanged();
}
}
bool isBusy = false;
public bool IsBusy
{
get { return isBusy; }
set
{
isBusy = value;
NotifyPropertyChanged();
AgendaSaveFormCommand.ChangeCanExecute();
}
}
public int ID { get; }
// Methode qui enregistre un agenda et l'ajoute a la collection d'agenda de AgendaPage
async Task SaveForm()
{
IsBusy = true;
await Task.Delay(4000);
IsBusy = false;
// Agenda agenda = new Agenda();
//ObservableCollection<Agenda> agenda = new ObservableCollection<Agenda>();
Agenda agenda = new Agenda();
agenda.Topic = Topic;
agenda.Date = Date;
agenda.Duration = Duration;
await App.Database.SaveAgendaAsync(agenda);
await Application.Current.MainPage.DisplayAlert("Save", "La tâche a été enregistrée", "OK");
await Application.Current.MainPage.Navigation.PopAsync();
}
}
AgendaPage.xaml
<CollectionView Grid.Row="2" Margin="25" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
SelectionMode="Single" x:Name="AgendaCollection" ItemsSource="{Binding Agendas}"> <!--ItemsSource="{Binding AngedaCollection}" -->
<CollectionView.Header>
<StackLayout Orientation="Horizontal" Spacing="220">
<Label Text="Agenda" TextColor="Black" FontSize="18"/>
<StackLayout Orientation="Horizontal">
<ImageButton Source="iconplus.png" HeightRequest="30" WidthRequest="30" Clicked="GoToNewFormPage"></ImageButton>
<ImageButton Source="iconmoins.png" HeightRequest="30" WidthRequest="30" Clicked="DeleteButtonClicked"></ImageButton>
</StackLayout>
</StackLayout>
</CollectionView.Header>
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical" ItemSpacing="20"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate >
<DataTemplate>
<pv:PancakeView HasShadow="True" BackgroundColor="White" VerticalOptions="StartAndExpand "
HorizontalOptions="FillAndExpand">
<Grid VerticalOptions="StartAndExpand" HorizontalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<BoxView BackgroundColor="{Binding Color}" WidthRequest="3" HorizontalOptions="Start"
VerticalOptions="FillAndExpand"/>
<Expander Grid.Column="1" >
<Expander.Header>
<Grid HorizontalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="3.5*"/>
</Grid.ColumnDefinitions>
<StackLayout HorizontalOptions="Center" VerticalOptions="Center">
<Label Text="{Binding Date, StringFormat='{0:dd}'}" TextColor="#008A00" FontSize="27"
HorizontalOptions="Center"/>
<Label Text="{Binding Date, StringFormat='{0:MMMM}'}" TextColor="Black" FontSize="10"
HorizontalOptions="Center" Margin="0,-10,0,0" FontAttributes="Bold"/>
<ImageButton Source="iconplus.png" HorizontalOptions="Center" HeightRequest="30" WidthRequest="30" Clicked="GoToFormPage"></ImageButton>
</StackLayout>
<BoxView Grid.Column="1" BackgroundColor="#F2F4F8" WidthRequest="1" HorizontalOptions="Start"
VerticalOptions="FillAndExpand"/>
<StackLayout x:Name="topicLayout" Grid.Column="2" HorizontalOptions="Start" VerticalOptions="Center" Margin="20">
<Label Text="{Binding Topic}" TextColor="#008A00" FontSize="15" FontAttributes="Bold"/>
<Label Text="{Binding Duration}" TextColor="#2F3246" FontSize="12" Margin="0,-10,0,0"/>
<ImageButton Source="iconmoins.png" HeightRequest="30" WidthRequest="30" Command="{Binding Source={x:Reference AgendaCollection}, Path=AgendaPageViewModel.DeleteAgendaCommand}"
CommandParameter="{Binding .}"></ImageButton>
</StackLayout>
</Grid>
</Expander.Header>
<Grid HorizontalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="3.5*"/>
</Grid.ColumnDefinitions>
<BoxView Grid.Column="1" BackgroundColor="#F2F4F8" WidthRequest="1" HorizontalOptions="Start"
VerticalOptions="FillAndExpand"/>
<StackLayout Grid.Column="2" Spacing="10">
<Label Text="Tâches" TextColor="Black" FontSize="15" Margin="20,0"/>
<StackLayout BindableLayout.ItemsSource="{Binding Speakers}" HorizontalOptions="Start" VerticalOptions="Center" Margin="20,0,0,20">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Label TextColor="#2F3246" FontSize="12">
<Label.FormattedText>
<FormattedString>
<FormattedString.Spans>
<Span Text="{Binding Time}"/>
<Span Text=" - "/>
<Span Text="{Binding Name}" FontAttributes="Bold"/>
</FormattedString.Spans>
</FormattedString>
</Label.FormattedText>
</Label>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</StackLayout>
</Grid>
</Expander>
</Grid>
</pv:PancakeView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Im not sure where you have your delete button, so here are two possible ways of doing this:
1) You select an Agenda from the CollectionView and then use one Button to delete this agenda:
Add the SelectedItem as a Property to the ViewModel like that:
ObservableCollection<Agenda> agendas = new ObservableCollection<Agenda>();
public ObservableCollection<Agenda> Agendas { get => agendas; set => agendas = value; }
public Agenda SelectedItem { get; set; }
public Command Delete { get; set; }
public ViewModel()
{
Agendas.Add(new Agenda() { Name = "test" });
Agendas.Add(new Agenda() { Name = "test2" });
Agendas.Add(new Agenda() { Name = "test3" });
Delete = new Command(new Action<object>((obj) =>
{
DeleteAsync(SelectedItem);
}));
}
private void DeleteAsync(Agenda selectedItem)
{
//delete Agenda here
}
To use this simply bind the SelectedItem property of the CollectionView to your ViewModel:
<StackLayout>
<StackLayout.BindingContext>
<local:ViewModel/>
</StackLayout.BindingContext>
<CollectionView ItemsSource="{Binding Agendas}"
SelectionMode="Single"
SelectedItem="{Binding SelectedItem}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame>
<StackLayout>
<Label Text="{Binding Name}" FontSize="120" Margin="12"/>
</StackLayout>
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<Button Text="Delete" Command="{Binding Delete}" />
</StackLayout>
2) You have a button to delete an Agenda for each Agenda:
You can setup your ViewModel similar to this:
ObservableCollection<Agenda> agendas = new ObservableCollection<Agenda>();
public ObservableCollection<Agenda> Agendas { get => agendas; set => agendas = value; }
public Command Delete { get; set; }
public ViewModel()
{
Delete = new Command(new Action<object>((obj) =>
{
DeleteAsync((Agenda) obj);
}));
}
private void DeleteAsync(Agenda selectedItem)
{
//delete Agenda here
}
Then you can set this up in xaml like this:
<StackLayout>
<StackLayout.BindingContext>
<local:ViewModel/>
</StackLayout.BindingContext>
<CollectionView x:Name="AgendaCollection"
ItemsSource="{Binding Agendas}" >
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame>
<StackLayout>
<Label Text="{Binding Topic}" FontSize="120" Margin="12"/>
<Button Text="Delete"
Command="{Binding Source={x:Reference AgendaCollection}, Path=BindingContext.Delete}"
CommandParameter="{Binding .}"/>
</StackLayout>
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
Note you can directly Bind to a Command, you dont have to use ButtonClicked and than pass it on.
In this case you have to change the Binding Source to the CollectionView (give that a x:Name and Bind to that) because the Binding Context inside the DataTemplate will be set to the Children of the ItemSource (the individual Agendas). That way the command is automatically executed without the Clicked event.
To pass on the Agenda on which delete was clicked to the Command you can set the CommandParameter. Here it Binds to itself (As i said CollectionView will set the DataContext of its elements to the corresponding individual element.)

How can I bind a row so I can set isVisible to false in .cs file?

It seems that I can't access it even if I put a name on it because it's inside a listview. If i wan row1 to not be visible on code behind how do i go around this?
I tried to a name for Label but I can't access it. or added I cant access on code behind.
<ListView x:Name="postListView" SeparatorVisibility="Default" HasUnevenRows="True" ItemsSource="{Binding Items}" SeparatorColor="White">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid BackgroundColor="Black" HorizontalOptions="CenterAndExpand"
VerticalOptions="FillAndExpand" Padding="1,2,1,0">
<Grid HorizontalOptions="CenterAndExpand"
VerticalOptions="FillAndExpand" ColumnSpacing="1" RowSpacing="1">
<Grid.RowDefinitions >
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" FontSize="Medium" Grid.Column="0" Text="right tst:" HorizontalTextAlignment="Start" BackgroundColor="cornflowerblue" />
<Label Grid.Column="1" Grid.Row="0" Text="{Binding drain1vol}" HorizontalTextAlignment="Center" BackgroundColor="cornflowerblue"/>
<Label Grid.Row="1" Grid.Column="0" Text="nothing" BackgroundColor="Yellow"/>
<Label Grid.Row="1" Grid.Column="1" Text="{Binding drain2vol}" HorizontalTextAlignment="Center" BackgroundColor="Yellow" />
</Grid>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
//Model and Source of data
using System;
using SQLite;
using Demo.Helpers;
namespace Demo.Model
{
//this is the source of Binding
public class Post
{
//ID primary key that we will autoincrement
//These are binding source for Historypage
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public bool showLabel { get; set; } //public class model
}
}
The source is the Post class.
Binding the label's IsVisible property to the property in your model to control the visiable ability of the label.
For example,
In xaml:
<Label Grid.Column="1" Grid.Row="0" IsVisible="{Binding showLabel}" Text="{Binding drain1vol}" HorizontalTextAlignment="Center" BackgroundColor="cornflowerblue"/>
And then in your model:
public class model
{
public string drain1vol { get; set; }
public bool showLabel { get; set; }
}
When you create the dataSource, you can set the label's isVisable:
Items.Add(new model { drain1vol = "Rob Finnerty" ,showLabel= false });
Items.Add(new model { drain1vol = "Bill Wrestler", showLabel = true });
Items.Add(new model { drain1vol = "Dr. Geri-Beth Hooper", showLabel = false });
Items.Add(new model { drain1vol = "Dr. Keith Joyce-Purdy", showLabel = true });
Items.Add(new model { drain1vol = "Sheri Spruce", showLabel = false });
postListView.ItemsSource = Items;
To change the visiable ability in code behind:
void test() {
//Get the model you want to change
model.showLabel = false / true;
}
Update:
Implement the INotifyPropertyChanged interface in your model:
class model : INotifyPropertyChanged
{
private bool showLabel { get; set; }
private string drain1vol { get; set; }
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
public bool ShowLabel
{
set
{
if (showLabel != value)
{
showLabel = value;
OnPropertyChanged("ShowLabel");
}
}
get
{
return showLabel;
}
}
public string Drain1vol
{
set
{
if (drain1vol != value)
{
drain1vol = value;
OnPropertyChanged("Drain1vol");
}
}
get
{
return drain1vol;
}
}
}
And in your xaml, binding to ShowLabel and Drain1vol(upper-case):
<Label Grid.Column="1" Grid.Row="0" IsVisible="{Binding ShowLabel}" Text="{Binding Drain1vol}" HorizontalTextAlignment="Center" BackgroundColor="cornflowerblue"/>

Xamarin form activityindicator not working in constructor

I am working on Xamarin forms,I need to display data from database on form load.
So I want to display Activity indicator when database operation is taking time.
I am setting ActivityIndicator to true on constructor load andat the end setting it to false.
But its not showing
Here is code
Xaml is as below
<ContentPage.Content>
<StackLayout VerticalOptions="StartAndExpand" Padding="5,5,5,5">
<ListView HasUnevenRows="True" RowHeight="100" HeightRequest="-1" x:Name="ListViewAppointments" VerticalOptions="StartAndExpand">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="20,0,20,0" ColumnSpacing="20">
<Grid.RowDefinitions>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition Height="40"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="40"></ColumnDefinition>
</Grid.ColumnDefinitions>
<!--<BoxView Color="#f7f7f7" Grid.Column="0" Grid.RowSpan="2"/>
<BoxView Color="#ffffff" Grid.Column="1" Grid.RowSpan="2"/>-->
<Image Grid.RowSpan="2" Grid.Column="0" Source="documenticon.png" Aspect="AspectFit"></Image>
<Label TextColor="#00344e" FontAttributes="Bold" Text="{Binding ReportName}" Grid.Row="0" Grid.Column="1" VerticalTextAlignment="End"></Label>
<Label TextColor="#0073ae" Text="{Binding PrintDate}" Grid.Row="1" Grid.Column="1" VerticalTextAlignment="Start"></Label>
<Image Grid.RowSpan="2" Grid.Column="2" Source="downloadicon.png" Aspect="AspectFit"></Image>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ActivityIndicator x:Name="ActLoder" HorizontalOptions="CenterAndExpand" Color="#ffffff" VerticalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage.Content>
and code behind
public partial class CurrentDocuments : ContentPage
{
public CurrentDocuments()
{
InitializeComponent();
new Task(Initializer).Start();
}
public async void Initializer()
{
ShowLoader(true);
NavigationPage.SetBackButtonTitle(this, "");
Title = "CURRENT DOCUMENTS";
DocumentsResponse appointsData = null;
await Task.Run(async () =>
{
appointsData = await GetCurrentDocuments();
}).ContinueWith(_ =>
{
Device.BeginInvokeOnMainThread(() => {
ListViewAppointments.ItemsSource = appointsData.ListOfDocuments;
ShowLoader(false);
});
});
}
private async Task<DocumentsResponse> GetCurrentDocuments()
{
DocumentManager manager = new DocumentManager();
var result = await manager.GetCurrentDocuments(Application.Current.Properties["SessionId"].ToString());
return result;
}
public async void ShowLoader(bool isVisible)
{
ActLoder.IsRunning = isVisible;
ActLoder.IsVisible = isVisible;
ActLoder.IsEnabled = true;
}
}
GetCurrentDocuments is returning a Task<DocumentsResponse> and you are not awaiting it to activate that Task.
var appointsData = await GetCurrentDocuments();
But, you should not await in an .ctor.
Something like this will get you started (I did this on the fly, so correct any typos/syntax errors/etc:
public partial class CurrentDocuments : ContentPage
{
public CurrentDocuments()
{
InitializeComponent();
new Task(Initializer).Start();
}
public async void Initializer()
{
ShowLoader(true);
NavigationPage.SetBackButtonTitle(this, "");
Title = "CURRENT DOCUMENTS";
Task<DocumentsResponse> appointsData;
await Task.Run(async () =>
{
appointsData = await GetCurrentDocuments();
}).ContinueWith(_ =>
{
Device.BeginInvokeOnMainThread(() => {
ListViewAppointments.ItemsSource = appointsData;
ShowLoader(false);
});
});
}
public void ShowLoader(bool isVisible)
{
ActLoder.IsRunning = isVisible;
ActLoder.IsVisible = isVisible;
ActLoder.IsEnabled = true;
}
public async Task<DocumentsResponse> GetCurrentDocuments()
{
DocumentManager manager = new DocumentManager();
var result = await manager.GetCurrentDocuments(Application.Current.Properties["SessionId"].ToString());
return result;
}
}

Binding list view item template image to ObservableCollection not working

I know that there were many questions about this and I searched a lot and tried everything which I found about this but I can't get it working.
Simply put, for some reason I'm unable to show an image inside a ListView item template.
So I have this ItemViewModel class:
public class ItemViewModel : BaseViewModel, IItemViewModel
{
public ItemViewModel()
{
if (dalInterface == null)
{
dalInterface = ApplicationContext.Container.Resolve<IDalInterface>();
}
if (eventCenter == null)
{
eventCenter = ApplicationContext.Container.Resolve<IEventCenter>();
}
SaveCommand = new Command(SaveChanges, true);
DeleteCommand = new Command(RemoveItem, true);
AddNewItemCommand = new Command(AddNewItem, true);
}
public ICommand SaveCommand { get; set; }
public ICommand DeleteCommand { get; set; }
public ICommand AddNewItemCommand { get; set; }
private Item data;
public int ID { get; private set; }
private string title;
public string Title
{
get { return title; }
set
{
title = value;
NotifyPropertyChanged("Title");
}
}
private string author;
public string Author
{
get { return author; }
set
{
author = value;
NotifyPropertyChanged("Author");
}
}
private string shortDescription;
public string ShortDescription
{
get { return shortDescription; }
set
{
shortDescription = value;
NotifyPropertyChanged("ShortDescription");
}
}
private string buyPrice;
public string BuyPrice
{
get { return buyPrice; }
set
{
buyPrice = value;
NotifyPropertyChanged("BuyPrice");
}
}
private string borrowPrice;
public string BorrowPrice
{
get { return borrowPrice; }
set
{
borrowPrice = value;
NotifyPropertyChanged("BorrowPrice");
}
}
private int quantity;
public int Quantity
{
get { return quantity; }
set
{
quantity = value;
NotifyPropertyChanged("Quantity");
}
}
private string detailedDescription;
public string DetailedDescription
{
get { return detailedDescription; }
set
{
detailedDescription = value;
NotifyPropertyChanged("DetailedDescription");
}
}
private string imagePath;
public string ImagePath
{
get { return imagePath; }
set
{
imagePath = value;
NotifyPropertyChanged("ImagePath");
}
}
private Image image;
public Image Image
{
get { return image; }
set
{
image = value;
NotifyPropertyChanged("Image");
}
}
public void SetData(Item item)
{
data = item;
ID = data.ID;
Author = data.Author;
Title = data.Title;
Quantity = data.Quantity;
ShortDescription = data.ShortDescription;
DetailedDescription = data.DetailedDescription;
BuyPrice = data.BuyPrice;
BorrowPrice = data.BorrowPrice;
Image = GetImage(data.ImagePath);
}
private Image GetImage(string imagePath)
{
var imageUri = new Uri(imagePath, UriKind.RelativeOrAbsolute);
var bitmapImage = new BitmapImage(imageUri);
var image = new Image
{
Source = bitmapImage
};
return Image;
}
private void SaveChanges()
{
UpdateChanges(data);
dalInterface.UpdateItem(data);
}
private void RemoveItem()
{
dalInterface.RemoveItem(data);
}
private void AddNewItem()
{
var newItem = new Item();
if (AllDataCorrect())
{
UpdateChanges(newItem);
dalInterface.AddNewItem(newItem);
eventCenter.Publish(new AddItemEventArgs { OperationSuccess = true });
}
else
{
eventCenter.Publish(new AddItemEventArgs { OperationSuccess = false });
}
}
private void UpdateChanges(Item itemToUpdate)
{
itemToUpdate.Author = Author;
itemToUpdate.Title = Title;
itemToUpdate.BorrowPrice = BorrowPrice;
itemToUpdate.BuyPrice = BuyPrice;
itemToUpdate.DetailedDescription = DetailedDescription;
itemToUpdate.ShortDescription = ShortDescription;
itemToUpdate.Quantity = Quantity;
itemToUpdate.ImagePath = ImagePath;
}
private bool AllDataCorrect()
{
float val = -1.0F;
float.TryParse(BuyPrice, out val);
if (val <= 0.0F)
{
return false;
}
float.TryParse(BorrowPrice, out val);
if (val <= 0.0F)
{
return false;
}
if ((ShortDescription == string.Empty) ||
(DetailedDescription == string.Empty) ||
(Author == string.Empty) ||
(Title == string.Empty)
)
{
return false;
}
if (Quantity <= 0)
{
return false;
}
return true;
}
public void Clear()
{
Author = string.Empty;
Title = string.Empty;
ImagePath = string.Empty;
ShortDescription = string.Empty;
DetailedDescription = string.Empty;
BuyPrice = string.Empty;
BorrowPrice = string.Empty;
Quantity = 0;
}
}
And for this class I have the following user control:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Grid.Row="0"
Grid.Column="0"
Grid.RowSpan="4"
Style="{StaticResource ImageBorderStyle}">
<Image Source="{Binding Image, Mode=TwoWay}"
MinWidth="80"
MinHeight="80"
Stretch="UniformToFill"/>
</Border>
<Border Grid.Row="0"
Grid.Column="1"
Style="{StaticResource BaseBorderStyle}">
<TextBlock Style="{StaticResource BaseTextBlockStyle}"
Text="Wiki"
TextAlignment="Center"/>
</Border>
<Border Grid.Row="1"
Grid.Column="1"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TextBox Style="{StaticResource DataTextBoxStyle}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Width="Auto"
Text="{Binding ShortDescription}"/>
</Border>
</Grid>
<Grid Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Border Grid.Row="0"
Grid.Column="1"
Style="{StaticResource BaseBorderStyle}"
HorizontalAlignment="Left"
Width="100">
<TextBlock Style="{StaticResource BaseTextBlockStyle}"
Text="About"
TextAlignment="Center"/>
</Border>
<Border Grid.Row="1"
Grid.Column="1"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TextBox Style="{StaticResource DataTextBoxStyle}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Width="Auto"
Text="{Binding DetailedDescription}"/>
</Border>
</Grid>
</Grid>
<Grid Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Border Grid.Row="0"
Grid.Column="0"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch">
<TextBlock Style="{StaticResource BaseTextBlockStyle}"
Text="Title"
TextAlignment="Center"
HorizontalAlignment="Stretch"/>
</Border>
<Border Grid.Row="1"
Grid.Column="0"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TextBox Style="{StaticResource DataTextBoxStyle}"
HorizontalAlignment="Stretch"
Text="{Binding Title}"/>
</Border>
<Border Grid.Row="2"
Grid.Column="0"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch">
<TextBlock Style="{StaticResource BaseTextBlockStyle}"
Text="Author"
TextAlignment="Center"/>
</Border>
<Border Grid.Row="3"
Grid.Column="0"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TextBox Style="{StaticResource DataTextBoxStyle}"
HorizontalAlignment="Stretch"
Text="{Binding Author}"/>
</Border>
<Border Grid.Row="4"
Grid.Column="0"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch">
<TextBlock Style="{StaticResource BaseTextBlockStyle}"
Text="Quantity"
TextAlignment="Center"/>
</Border>
<Border Grid.Row="5"
Grid.Column="0"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TextBox Style="{StaticResource DataTextBoxStyle}"
HorizontalAlignment="Stretch"
Text="{Binding Quantity}"/>
</Border>
<Border Grid.Row="0"
Grid.Column="1"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch">
<TextBlock Style="{StaticResource BaseTextBlockStyle}"
Text="Buy Price"
TextAlignment="Center"/>
</Border>
<Border Grid.Row="1"
Grid.Column="1"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TextBox Style="{StaticResource DataTextBoxStyle}"
HorizontalAlignment="Stretch"
Text="{Binding BuyPrice}"/>
</Border>
<Border Grid.Row="2"
Grid.Column="1"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch">
<TextBlock Style="{StaticResource BaseTextBlockStyle}"
Text="Borrow Price"
TextAlignment="Center"/>
</Border>
<Border Grid.Row="3"
Grid.Column="1"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TextBox Style="{StaticResource DataTextBoxStyle}"
HorizontalAlignment="Stretch"
Text="{Binding BorrowPrice}"/>
</Border>
<Button Grid.Row="5"
Grid.Column="1"
Style="{StaticResource SaveButtonStyle}"/>
<Button Grid.Row="5"
Grid.Column="1"
Style="{StaticResource RemoveButtonStyle}"
HorizontalAlignment="Right"/>
</Grid>
</Grid>
Using this I want to show into a page, inside a list view, based on an observable collection, loaded from a database some items.
The page view model is the follow:
public class ManageItemsViewModel : BaseViewModel, IManageItemsViewModel
{
public ManageItemsViewModel()
{
if(dalInterface == null)
{
dalInterface = ApplicationContext.Container.Resolve<IDalInterface>();
}
if(eventCenter == null)
{
eventCenter = ApplicationContext.Container.Resolve<IEventCenter>();
}
Items = new ObservableCollection<ItemViewModel>();
}
public ObservableCollection<ItemViewModel> Items { get; set; }
public void Refresh()
{
var dalItems = dalInterface.GetAllItems();
foreach(Item item in dalItems)
{
var vm = Items.Where(v => v.ID.Equals(item.ID));
if(vm.Equals(null))
{
var newItemVm = (ItemViewModel)ApplicationContext.Container.Resolve<IItemViewModel>();
newItemVm.SetData(item);
Items.Add(newItemVm);
}
}
NotifyPropertyChanged("Items");
}
public void LoadData()
{
if(Items.Count == 0)
{
var dalItems = dalInterface.GetAllItems();
foreach(Item item in dalItems)
{
var newItemVm = (ItemViewModel)ApplicationContext.Container.Resolve<IItemViewModel>();
newItemVm.SetData(item);
Items.Add(newItemVm);
}
NotifyPropertyChanged("Items");
}
else
{
Refresh();
}
}
}
And the page view is the follow:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Grid.Row="0"
Margin="2"
Style="{StaticResource AddButtonStyle}"
Click="GoToAddNewItem"/>
<Button Grid.Row="0"
Margin="2"
HorizontalAlignment="Right"
Style="{StaticResource CloseButtonStyle}"
Click="GoToItems"/>
<ListView Grid.Row="1"
ItemsSource="{Binding Items, Mode=TwoWay}"
Margin="5">
<ListView.ItemTemplate>
<DataTemplate>
<templates:EditableItem/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
That page should be like (at the moment I only have one item in DB for tests):
The problem which I facing now is that I cannot see the Image inside this item template, although all the other properties are correctly shown inside the item template.
I've tried to use the image path as I get it from DB and bind into item xaml the source path property to this, also I tried to have an ImageSource or BitmapImage property inside item VM to have the xaml image source bound to this property but without any success so far.
After reading many questions about and trying again and again without success I'm here...
Any hints about what I'm doing wrong?
Many thanks!
(P.S. The app which I'm working to is an UWP app, maybe it matters...)
You can't use an Image control as the value of the Source property of another Image control.
Use an ImageSource instead:
private ImageSource image;
public ImageSource Image
{
get { return image; }
set
{
image = value;
NotifyPropertyChanged("Image");
}
}
And change the GetImage method to:
private ImageSource GetImage(string imagePath)
{
return new BitmapImage(new Uri(imagePath, UriKind.RelativeOrAbsolute));
}
That said, you could simply drop the Image property and bind directly to ImagePath. Built-in type conversion will automatically convert from a path string to ImageSource.
<Image Source="{Binding ImagePath}"/>
Note also that Mode=TwoWay makes no sense for this Binding.
As I searched again and again about this problem and tried lots of suggested solutions I finally found a solution which is suitable for me and solves this problem.
I implemented a little demo to show how exactly is working. The UWP app is not constraint to interact with the pictures folder only. The images can be loaded from an arbitrary folder on disk.
Hope that this will help others to.
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
PickFileCommand = new ActionCommand(PickFile, true);
}
public event PropertyChangedEventHandler PropertyChanged;
public ICommand PickFileCommand { get; set; }
private BitmapImage imageSrc;
public BitmapImage ImageSrc
{
get { return imageSrc; }
set
{
imageSrc = value;
NotifyPropertyChanged("ImgSource");
}
}
private async void PickFile()
{
var filePicker = new FileOpenPicker
{
SuggestedStartLocation = PickerLocationId.PicturesLibrary
};
filePicker.FileTypeFilter.Add(".jpg");
filePicker.FileTypeFilter.Add(".jpeg");
filePicker.FileTypeFilter.Add(".png");
StorageFile file = await filePicker.PickSingleFileAsync();
if (file != null)
{
var stream = await file.OpenAsync(FileAccessMode.Read);
var bitmap = new BitmapImage
{
UriSource = new Uri(file.Path, UriKind.Absolute)
};
await bitmap.SetSourceAsync(stream);
ImageSrc = bitmap;
}
}
protected void NotifyPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(name));
}
}
}
Now, the trick which solved the image problem is in View code behind class.
public sealed partial class MainPage : Page
{
private ViewModel dataContext;
public MainPage()
{
this.InitializeComponent();
dataContext = new ViewModel();
DataContext = dataContext;
}
**private void PageLoaded(object sender, RoutedEventArgs e)
{
if (DataContext is ViewModel dc)
{
dc.PropertyChanged += Dc_PropertyChanged;
}
}
private void Dc_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if(e.PropertyName.Equals("ImgSource"))
{
if (DataContext is ViewModel dc)
{
ShowImage.Source = dc.ImageSrc;
}
}
}**
}
What was needed it was that the source of the Image UI element to be set explicitly. I made this by subscribing to the PropertyChanged event from ViewModel and set the image source.

Resources