Xamarin Forms: Display error occurring in view model - xamarin.forms

I'm following tutorials/examples for a Xamarin Forms project, where there is a view with a C# code-behind, binding to a view model. However, I want to catch an exception occurring in the view model and display it in an alert or use any other common technique for displaying errors.
Here is the view, which reloads data using a refresh:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyCompany.App.Views.DashboardPage"
xmlns:vm="clr-namespace:MyCompany.App.ViewModels"
xmlns:dashboard="clr-namespace:MyCompany.App.ViewModels.Dashboard;assembly=MyCompany.App"
Title="{Binding Title}">
...
<RefreshView x:DataType="dashboard:DashboardViewModel" Command="{Binding LoadItemsCommand}" IsRefreshing="{Binding IsBusy, Mode=TwoWay}">
... content
</RefreshView>
</ContentPage>
Then I have the C# code behind for the XAML:
public partial class DashboardPage : ContentPage
{
DashboardViewModel _viewModel;
public DashboardPage()
{
InitializeComponent();
BindingContext = _viewModel = new DashboardViewModel();
}
protected override void OnAppearing()
{
base.OnAppearing();
_viewModel.OnAppearing();
}
}
And finally the view model where the loading happens. It inherits from the BaseViewModel which is provided in the tutorials.
public class DashboardViewModel : BaseViewModel
{
private DashboardItemViewModel _selectedItem;
public ObservableCollection<DashboardItemViewModel> Items { get; }
public Command LoadItemsCommand { get; }
public Command<DashboardItemViewModel> ItemTapped { get; }
public DashboardViewModel()
{
Title = "Dashboard";
Items = new ObservableCollection<DashboardItemViewModel>();
LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand());
ItemTapped = new Command<DashboardItemViewModel>(OnItemSelected);
}
async Task ExecuteLoadItemsCommand()
{
IsBusy = true;
try
{
Items.Clear();
var items = await GetItems();
foreach (var item in items)
{
Items.Add(item);
}
}
finally
{
IsBusy = false;
}
}
private static async Task<List<DashboardItemViewModel>> GetItems()
{
// Where errors happen
return items;
}
public void OnAppearing()
{
IsBusy = true;
SelectedItem = null;
}
public DashboardItemViewModel SelectedItem
{
get => _selectedItem;
set
{
SetProperty(ref _selectedItem, value);
OnItemSelected(value);
}
}
async void OnItemSelected(DashboardItemViewModel item)
{
if (item == null || item.Uri.IsNotSet())
return;
await Shell.Current.GoToAsync(item.Uri);
}
}
I can't see any overridable methods in ContentPage for catching exceptions. What's the best way to catch an exception and display it in an alert?

I'm not sure what exactly you want, but for catching errors I use try/catch method.
try
{
//your code here
}
catch(Exception exc)
{
await App.Current.MainPage.DisplayAlert("Warning", "Error: " + exc, "Ok");
}

Related

Xamarin ios share extension get image

I created demo application with Xamarin.Forms (5.X.X)
And i created share extension. Everything is good until here.
I want to get image from share extension but i cant get.
I tried almost everything.
public class CustomShareViewController: SLComposeServiceViewController
{
public CustomShareViewController(IntPtr handle) : base(handle)
{
}
public override void DidReceiveMemoryWarning()
{
// Releases the view if it doesn't have a superview.
base.DidReceiveMemoryWarning();
// Release any cached data, images, etc that aren't in use.
}
public override async void ViewDidLoad()
{
base.ViewDidLoad();
string urlstr = string.Empty;
NSExtensionItem item = ExtensionContext.InputItems[0];
var itemProvider = item.Attachments[0];
var type = itemProvider.Description.Split('"');
if (itemProvider.HasItemConformingTo(type[1]))
{
var load = await itemProvider.LoadItemAsync(type[1], null);
NSUrl newUrl = (NSUrl)load;
urlstr = newUrl.FilePathUrl.ToString();
}
if (!Xamarin.Forms.Forms.IsInitialized)
Xamarin.Forms.Forms.Init();
// Create an instance of XF page with associated View Model
var xfPage = new ShareMainPage();
var viewModel = (ShareMainPageViewModel)xfPage.BindingContext;
viewModel.Message = urlstr;
viewModel.Image = urlstr;
//viewModel.BindableImage = ImageSource.FromStream(() => new MemoryStream(byteArray));
// Override the behavior to complete the execution of the Extension when a user press the button
viewModel.DoCommand = new Command(() => DoneClicked());
// Convert XF page to a native UIViewController which can be consumed by the iOS Extension
var newController = xfPage.CreateViewController();
// Make sure the presentation style is set to full screen to avoid rendering the original entry point
newController.ModalPresentationStyle = UIModalPresentationStyle.FormSheet;
// Present new view controller as a regular view controller
this.PresentModalViewController(newController, false);
//var someController = this.Storyboard.InstantiateViewController("SomeController") as SomeViewController;
//NavigationController.PushViewController(someController, true);
//var itemProvider = inputItem?.Attachments?[0] as NSItemProvider;
//if (itemProvider.HasItemConformingTo("public.url"))
//{
// itemProvider.LoadPreviewImage(null, (item, error) => {
// if (item is UIImage)
// {
// var image = item as UIImage;
// var data = image.AsPNG();
// ImageView.Image = UIImage.LoadFromData(data);
// }
// });
//}
}
public override bool ShouldInteractWithUrl(UITextView textView, NSUrl url, NSRange characterRange, UITextItemInteraction interaction)
{
return base.ShouldInteractWithUrl(textView, url, characterRange, interaction);
}
public override bool IsContentValid()
{
// Do validation of contentText and/or NSExtensionContext attachments here
return true;
}
public override void DidSelectPost()
{
// This is called after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments.
// Inform the host that we're done, so it un-blocks its UI. Note: Alternatively you could call super's -didSelectPost, which will similarly complete the extension context.
}
public override void PresentationAnimationDidFinish()
{
base.PresentationAnimationDidFinish();
}
private void DoneClicked()
{
// Return any edited content to the host app.
// This template doesn't do anything, so we just echo the passed-in items.
ExtensionContext.CompleteRequest(ExtensionContext.InputItems, null);
}
private void WriteToDebugFile(string dbgText)
{
var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var filename = Path.Combine(documents, "Share-Extension-Debug.txt");
if (!File.Exists(filename))
{
File.WriteAllText(filename, $"\n{DateTime.Now} - {dbgText}");
}
else
{
File.AppendAllText(filename, $"\n{DateTime.Now} - {dbgText}");
}
}
}
I tried two ways.
Bind file path and get from there. ImageResource.FromFile(...) but it doesnt work.
Bind stream and it doesnt work too.
My sharepage.xml is
<?xml version="1.0" encoding="UTF-8" ?>
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Demo.ViewModels"
x:DataType="local:ShareMainPageViewModel"
xmlns:converters="clr-namespace:LifeApp.Mobile.Converters"
BackgroundColor="Green"
x:Class="LifeApp.Mobile.Views.ShareMainPage">
<ContentPage.BindingContext>
<local:ShareMainPageViewModel Message="Hello from Xamarin.Forms!" />
</ContentPage.BindingContext>
<ContentPage.Content>
<StackLayout HorizontalOptions="Center" VerticalOptions="Center">
<Image Source="{Binding BindableImage}"
VerticalOptions="Start"
HorizontalOptions="CenterAndExpand" WidthRequest="200" HeightRequest="150"/>
<Label Text="{Binding Message}" TextColor="Black" FontSize="Medium" VerticalTextAlignment="Center"/>
<Button Command="{Binding DoCommand}" Text="Do the jungle!" TextColor="Black" />
</StackLayout>
</ContentPage.Content>
And SharePageViewModel
using System;
using System.ComponentModel;
using System.IO;
using System.Windows.Input;
using Xamarin.Essentials;
using Xamarin.Forms;
using Xamarin.Forms.Shapes;
namespace Demo.ViewModels
{
public class ShareMainPageViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _message;
public string Message
{
get { return _message; }
set
{
if (_message != value)
{
_message = value;
PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs(nameof(Message)));
}
}
}
private string _image;
public string Image
{
get { return _image; }
set
{
if (_image != value)
{
_image = value;
PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs(nameof(Image)));
}
}
}
public ImageSource _bindableImage { get; set; }
public ImageSource BindableImage
{
get
{
return _bindableImage;
}
set
{
if (_bindableImage !=null)
{
_bindableImage = value;
PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs(nameof(BindableImage)));
}
}
}
private ICommand _doCommand;
public ICommand DoCommand
{
get { return _doCommand; }
set
{
if (_doCommand != value)
{
_doCommand = value;
PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs(nameof(DoCommand)));
}
}
}
public ShareMainPageViewModel()
{
DoCommand = new Command(OnDoCommandExecuted);
}
private void OnDoCommandExecuted(object state)
{
// Acr.UserDialogs.UserDialogs.Instance.Alert("Completed");
Message = $"Job {Environment.TickCount} has been completed!";
}
}
}
I can get image file path but i cant bind and show image
file:///Users/xxxx/Library/Developer/CoreSimulator/Devices/58453DF3-58CF-4591-AC91-6A096FD7B814/data/Containers/Data/Application/1466F475-0F58-4F94-8511-B0BD9FE86DCA/tmp/WKFileShare-FbFp8eSD/adcf0796-2088-4d85-9420-c1d58c15cca4/....JPG
How can i solve this problem.

How to update label in Xamarin Forms

Beginner here.
I'm trying to get the exact time, when this operation was executed successfully and print it on label. The problem is that when I click the button, the label doesn't update the text.
namespace HGB.Droid.Helpers
{
public class CallServiceHelper : ICallServiceHelper
{
IContactsHelper contactsHelper = DependencyService.Get<IContactsHelper>();
List<Repository> ObjContactList = new List<Repository>();
LabelModel labelModel = new LabelModel();
Context context = Android.App.Application.Context;
HttpClient client = new HttpClient();
public async Task UpdatePhonebook()
{
if (NetworkCheck.IsInternet())
{
var response = await client.GetAsync("http://mmmmmmmmmmm.aspx");
if (response.IsSuccessStatusCode)
{
string contactsJson = await response.Content.ReadAsStringAsync();
var list = JsonConvert.DeserializeObject<List<Repository>>(contactsJson);
contactsHelper.DeleteContact();
ObjContactList = list;
foreach (Repository obj in ObjContactList)
{
contactsHelper.CreateContacts(obj.name, obj.phone);
}
Device.BeginInvokeOnMainThread(() =>
{
labelModel.UpdateLabelValue.Execute(DateTime.Now.ToString());
});
}
}
else
{
Device.BeginInvokeOnMainThread(() =>
{
Toast.MakeText(context, "Error", ToastLength.Long).Show();
});
}
}
I'm calling this function on UI button
public partial class MainPage : ContentPage
{
ICallServiceHelper callServiceHelper = DependencyService.Get<ICallServiceHelper>();
public MainPage()
{
InitializeComponent();
}
private async void updateContactsBtn_Clicked(object sender, EventArgs e)
{
await callServiceHelper.UpdatePhonebook();
}
}
This is my ViewModel
public class LabelModel : BindableObject
{
string dateValue = "Date Value";
public LabelModel()
{
UpdateLabelValue = new Command<string>(UpdateLabel);
}
public ICommand UpdateLabelValue { get; }
public string DateDisplay
{
get => dateValue;
set
{
dateValue = value;
OnPropertyChanged(nameof(DateDisplay));
}
}
void UpdateLabel(string newLabel)
{
DateDisplay = newLabel;
}
}
And this is my Xaml file
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:HGB.ViewModel"
x:Class="HGB.MainPage">
<ContentPage.BindingContext>
<local:LabelModel/>
</ContentPage.BindingContext>
<StackLayout HorizontalOptions="Center" VerticalOptions="Center" Spacing="10">
<Button
Text="Update Phonebook"
x:Name="updateContactsBtn"
Clicked="updateContactsBtn_Clicked"
/>
<Label
VerticalOptions="Center"
HorizontalOptions="Center"
Text="{Binding DateDisplay}"
/>
</StackLayout>
I'm using the Helper method in my Foreground Service class, where it
gets called every 24 hours. What I'm trying to achieve is print the
exact time, when the phonebook was successfully updated and print that
date to my label.
For your problem, a simple method is to use MessagingCenter just as Jason mentioned.
You can send message in your CallServiceHelper and subscribe to this message in your ViewModel(LabelModel.cs).
Please refer to the following code:
1.In the constructor of your ViewMode(LabelModel.cs),subscribe to this message:
public LabelModel()
{
MessagingCenter.Subscribe<object, string>(this, "time", (sender, args) =>
{
System.Diagnostics.Debug.WriteLine("received time is: "+ args);
DateDisplay = args;
});
}
2.In your CallServiceHelper , public your message:
MessagingCenter.Send<object, string>(this, "time", "2022-4-8");

Xamarin application not navigating back

Problem
This is the process:
Select a category from list.
Load Tasks page.
Tasks are loaded depending on the categoryId selected from the previous page. (Navigate back to Category page is possible ✔️)
Select a Task from from list.
Load Task Page.
Task details are loaded on the page. (Navigate back to Tasks page is not possible ❌)
Video
Question
I do not understand why I cannot navigate back a page. How can I fix this?
Code
CategoriesViewModel
public class CategoriesViewModel : BaseViewModel
{
public ObservableCollection<CategoryModel> Categories { get; } = new ObservableCollection<CategoryModel>();
public Command LoadCategoriesCommand { get; }
public Command<CategoryModel> SelectedCategory { get; }
public CategoriesViewModel()
{
Title = "Categories";
LoadCategoriesCommand = new Command(async () => await LoadCategories());
SelectedCategory = new Command<CategoryModel>(OnSelectedCategory);
}
private async Task LoadCategories()
{
IsBusy = true;
try
{
Categories.Clear();
var categories = await DatabaseService.GetCategoriesAsync();
foreach (var category in categories)
{
this.Categories.Add(category);
}
}
catch (Exception)
{
throw;
}
finally
{
IsBusy = false;
}
}
private async void OnSelectedCategory(CategoryModel category)
{
if (category == null)
return;
await Shell.Current.GoToAsync($"{nameof(TasksPage)}?{nameof(TasksViewModel.CategoryId)}={category.CategoryId}");
}
public void OnAppearing()
{
IsBusy = true;
}
}
TasksViewModel
[QueryProperty(nameof(CategoryId), nameof(CategoryId))]
public class TasksViewModel : BaseViewModel
{
public ObservableCollection<TaskModel> Tasks { get; } = new ObservableCollection<TaskModel>();
private int categoryId;
public int CategoryId
{
get { return categoryId; }
set
{
categoryId = value;
}
}
public Command LoadTasksCommand { get; set; }
public Command<TaskModel> SelectedTask { get; set; }
public TasksViewModel()
{
Title = "Tasks";
LoadTasksCommand = new Command(async () => await LoadTasks());
SelectedTask = new Command<TaskModel>(OnSelectedTask);
}
private async Task LoadTasks()
{
IsBusy = true;
try
{
this.Tasks.Clear();
var tasks = await DatabaseService.GetTasksAsync(CategoryId);
foreach (var task in tasks)
{
this.Tasks.Add(task);
}
}
catch (Exception)
{
throw;
}
finally
{
IsBusy = false;
}
}
private async void OnSelectedTask(TaskModel task)
{
if (task == null)
return;
await Shell.Current.GoToAsync($"{nameof(TaskPage)}?{nameof(TaskViewModel.TaskId)}={task.TaskId}");
}
public void OnAppearing()
{
IsBusy = true;
}
}
TaskViewModel
[QueryProperty(nameof(TaskId), nameof(TaskId))]
public class TaskViewModel : BaseViewModel
{
private int taskId;
public int TaskId
{
get { return taskId; }
set
{
taskId = value;
}
}
private string taskTitle;
public string TaskTitle
{
get { return taskTitle; }
set
{
taskTitle = value;
OnPropertyChanged(nameof(TaskTitle));
}
}
private string description;
public string Description
{
get { return description; }
set
{
description = value;
OnPropertyChanged(nameof(Description));
}
}
public Command LoadTaskCommand { get; }
public TaskViewModel()
{
LoadTaskCommand = new Command(async () => await LoadTask());
}
private async Task LoadTask()
{
IsBusy = true;
try
{
var task = await DatabaseService.GetTaskAsync(TaskId);
this.TaskTitle = task.Title;
this.Description = task.Description;
}
catch (Exception)
{
throw;
}
finally
{
IsBusy = false;
}
}
public void OnAppearing()
{
IsBusy = true;
}
}
Update 1
I tried replacing this line of code in TasksViewModel:
await Shell.Current.GoToAsync($"{nameof(TaskPage)}?{nameof(TaskViewModel.TaskId)}={task.TaskId}");
to this:
await Shell.Current.Navigation.PushAsync(new AboutPage());
Also, the same outcome.
Update 2
As per requested comment, here is the TaskPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm="clr-namespace:SomeProject.ViewModels"
x:Class="SomeProject.Views.Task.TaskPage"
Title="{Binding TaskTitle}">
<ContentPage.Content>
<RefreshView x:DataType="vm:TaskViewModel"
Command="{Binding LoadTaskCommand}"
IsRefreshing="{Binding IsBusy, Mode=TwoWay}">
<StackLayout>
<Label Text="{Binding Description}" />
</StackLayout>
</RefreshView>
</ContentPage.Content>
</ContentPage>
and TaskPage.xaml.cs:
public partial class TaskPage : ContentPage
{
TaskViewModel _viewModel;
public TaskPage()
{
InitializeComponent();
BindingContext = _viewModel = new TaskViewModel();
}
protected override void OnAppearing()
{
base.OnAppearing();
_viewModel.OnAppearing();
}
}
Update 3
As per requested comment, here is the routes:
public AppShell()
{
InitializeComponent();
Routing.RegisterRoute(nameof(CategoriesView), typeof(CategoriesView));
Routing.RegisterRoute(nameof(TasksPage), typeof(TasksPage));
Routing.RegisterRoute(nameof(TaskPage), typeof(TaskPage));
}
Check your registers route.
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/shell/navigation#register-page-routes
In the Shell subclass constructor, or any other location that runs
before a route is invoked, additional routes can be explicitly
registered for any pages that aren't represented in the Shell visual
hierarchy
I had CategoryPage registered in AppShell.xaml.cs and also AppShell.xaml like so:
<ShellContent Route="CategoryPage" ContentTemplate="{DataTemplate local:CategoryPage}" />
Only can register one route in one or the other.

Media Plugin not displaying an image after capture MVVM

I am using Media Plugin and everything worked fine until i have decided to move my logic to ViewModel.
This is my Xaml
<Frame BackgroundColor="LightGray" HasShadow="True">
<Image
x:Name="Photo"
Grid.Row="2"
HeightRequest="100"
Source="{Binding postViewModel.SelectedPhoto}"
VerticalOptions="Start"/>
</Frame>
My Binding to MasterViewModel
MasterPostsViewModel ViewModel;
protected override void OnAppearing()
{
base.OnAppearing();
BindingContext = ViewModel = new MasterPostsViewModel(Navigation);
}
My Master
class MasterPostsViewModel : BaseViewModel
{
public PostViewModel postViewModel { get; set; }
public CategoriesViewModel categoriesViewModel { get; set; }
public MasterPostsViewModel(INavigation navigation)
{
postViewModel = new PostViewModel();
categoriesViewModel = new CategoriesViewModel();
postViewModel = new PostViewModel(navigation);
}
}
Taking Picture in View Model
private MediaFile _selectedPhoto;
public MediaFile SelectedPhoto { get => _selectedPhoto; set => SetValue(ref
_selectedPhoto, value); }
private async Task TakePicture()
{
await Permission();
var imageSource = await DependencyService.Get<IMessage>().ShowActionSheet(AppResources.AlertPhoto, AppResources.AlertNewPhoto, AppResources.AlertGallery);
if (imageSource == AppResources.AlertNewPhoto)
{
var imageFileName = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions()
{
Name = $"{DateTime.UtcNow}.jpg",
DefaultCamera = Plugin.Media.Abstractions.CameraDevice.Rear,
PhotoSize = PhotoSize.Medium,
SaveToAlbum = true
});
if (imageFileName == null) return;
else
{
SelectedPhoto = imageFileName;
}
}
}
I can see tthe adress of the picture however the picture doesnt display on my xaml. I have tried to follow this
Bind Plugin.Media fromViewModel
But still didnt work. Please some suggestion on what am i doing wrong
I use you code and write a demo with binding a string, it works well. You can have a look at the code and may get some idea from it.
Code in xaml:
<StackLayout>
<!-- Place new controls here -->
<Label Text="{Binding postViewModel.SelectedPhoto}"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<Button Text="click me" Command ="{Binding postViewModel.NewCommand}"/>
</StackLayout>
Code behind:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
MasterPostsViewModel ViewModel;
protected override void OnAppearing()
{
base.OnAppearing();
BindingContext = ViewModel = new MasterPostsViewModel(Navigation);
}
}
class MasterPostsViewModel
{
public PostViewModel postViewModel { get; set; }
public MasterPostsViewModel(INavigation navigation)
{
postViewModel = new PostViewModel();
}
}
class PostViewModel : INotifyPropertyChanged
{
string _selectedPhoto;
public ICommand NewCommand { private set; get; }
public event PropertyChangedEventHandler PropertyChanged;
public PostViewModel()
{
SelectedPhoto = "default text";
NewCommand = new Command(TakePicture);
}
private void TakePicture()
{
SelectedPhoto = "test text After click button";
}
public string SelectedPhoto
{
set
{
if (_selectedPhoto != value)
{
_selectedPhoto = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("SelectedPhoto"));
}
}
}
get
{
return _selectedPhoto;
}
}
}
Sample project has been uploaded here.

Xamarin datagrid not show up after OnScanResult Zxing

I can get datatable after scan barcode but it not show when i bind it to sfDatagrid. what am i doing wrong. I think i call vm.TimSPTonKho.Execute(null); in .cs incorrectly
code xaml and .cs
<ContentPage.BindingContext>
<vm:vmBanHang_get_TTSanPham_ScanCode />
</ContentPage.BindingContext>
<StackLayout>
<Grid VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand">
<zxing:ZXingScannerView x:Name="scanView"
OnScanResult="scanView_OnScanResult"
IsScanning="True"
WidthRequest="200"
HeightRequest="300"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand"/>
<zxing:ZXingDefaultOverlay TopText="Align the barcode within the frame"/>
</Grid>
<datagrid:SfDataGrid HorizontalOptions="Center" x:Name="datagrid"
AllowTriStateSorting="True"
ColumnSizer="Star"
ItemsSource="{Binding DataTableCollection}">
</datagrid:SfDataGrid>
</StackLayout>
this my xaml file
private void scanView_OnScanResult(Result result)
{
Device.BeginInvokeOnMainThread(async () =>
{
await DisplayAlert("Scanned result", "The barcode's text is " + result.Text + ". The barcode's format is " + result.BarcodeFormat, "OK");
var vm = new vmBanHang_get_TTSanPham_ScanCode();
vm.MaSanPham = result.Text;
vm.IDCuaHang = 1;
vm.TimSPTonKho.Execute(null);
});
}
my ViewModel.cs
class vmBanHang_get_TTSanPham_ScanCode : INotifyPropertyChanged
{
private ApiServices _apiServices = new ApiServices();
public int IDCuaHang { get; set; }
public string MaSanPham { get; set; }
public vmBanHang_get_TTSanPham_ScanCode()
{
DataTableCollection = _DataTableCollection;
}
public DataTable DataTableCollection
{
get { return _DataTableCollection; }
set
{
_DataTableCollection = value;
OnPropertyChanged();
}
}
public DataTable _DataTableCollection;
public ICommand TimSPTonKho
{
get
{
return new Command(async () =>
{
if (!string.IsNullOrEmpty(MaSanPham))
{
DataTableCollection = await _apiServices.get_TTSanPham_ScanCode(IDCuaHang, MaSanPham, Settings.Accesstoken);
}
});
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
2. My second question, how can i get all values Datagrid send to Datatable ? do i have to implement it in xaml.cs or in viewmodel.
Thanks for helps
I found the solution. bind directly from xaml.cs
datagrid.ItemsSource = await _apiServices.get_TTSanPham_ScanCode(1, result.Text, Settings.Accesstoken);
instead of call Icommand in Viewmodel
public ICommand TimSPTonKho
{
get
{
return new Command(async () =>
{
if (!string.IsNullOrEmpty(MaSanPham))
{
DataTableCollection = await _apiServices.get_TTSanPham_ScanCode(IDCuaHang, MaSanPham, Settings.Accesstoken);
}
});
}
}

Resources