I'm using mvvm thoughout my app and data binding and property changes are working everywhere else in the application but for some reason, on my view using XZing Barcode Scanner, my view is not updating after I scan an item.
I'm used to working with XAML ViewModel and Codebehind but they way I set up the barcode scanner is only ViewModel and Codebehind/alone class. So maybe I'm missing something simple that I am just not used to doing.
I've posted some code below that is an example of just one of the properties not updating, the PartName for productsLable.Text. It is bound when the page first loads and the dummy PartName shows correctly but after scanning a new item, I update the ScannedPart in my ViewModel but the "get" is never called after I set and call OnPropertyChanged();
This is working well in other parts of my application but maybe I am missing something silly here, like I said I am more comfortable working with bindings in XAML. Do you see anything I might be doing wrong or missing?
Thanks in advance.
public class TimsCustomScanPage : ContentPage
{
ZXingScannerView zxing;
TimsCustomOverlay overlay;
RequestPartsViewModel viewModel;
public TimsCustomScanPage(RequestPartsViewModel viewModel) : base()
{
//Attaching ViewModel
this.viewModel = viewModel;
this.BindingContext = viewModel;
//Scanner found barcode
zxing.OnScanResult += (result) =>
{
// Stop analysis until we navigate away so we don't keep reading barcodes
zxing.IsAnalyzing = false;
//Setting new scanned part
viewModel.ScannedPart = viewModel.Parts.FirstOrDefault();
};
--Some more code--
var productsLabel = new Label()
{
//Text = "Find New Part",
TextColor = Color.Black,
WidthRequest = 150,
HorizontalOptions = LayoutOptions.Start,
VerticalTextAlignment = TextAlignment.Center,
Padding = new Thickness(0, 0, 0, 0),
};
//Binding text property
productsLabel.SetBinding(Label.TextProperty, "PartName");
productsLabel.BindingContext = viewModel.ScannedPart;
}
ViewModel
public class RequestPartsViewModel : BaseViewModel
{
public ObservableCollection<TimsCategory> Categories { get; set; }
public ObservableCollection<TimsCategory> FavoriteCategories { get; set; }
public ObservableCollection<TimsPart> Parts { get; set; }
public TimsCategory CurrentSelection { get; set; }
public Command LoadItemsCommand { get; set; }
TimsPart scannedPart;
string partName;
int totalRequestedParts;
public RequestPartsViewModel()
{
Title = "Request Parts";
Categories = new ObservableCollection<TimsCategory>(db.Table<TimsCategory>().ToList());
Parts = new ObservableCollection<TimsPart>(db.Table<TimsPart>().ToList());
TotalRequestedParts = 0;
ScannedPart = new TimsPart();
ScannedPart.PartName = "Scan New Part";
ScannedPart.AvailableQuantity = 0;
//Attach parts to categories
foreach (var item in Categories)
{
int newId = item.CategoryId;
var partsForCategories = Parts.Where(p => p.CategoryId == newId);
var partsForCategoriesCollection = new ObservableCollection<TimsPart>(partsForCategories);
item.Parts = partsForCategoriesCollection;
}
FavoriteCategories = new ObservableCollection<TimsCategory>(Categories.Take(6).ToList());
LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand());
}
public TimsPart ScannedPart
{
get
{
return scannedPart;
}
set
{
if (scannedPart != value)
{
scannedPart = value;
OnPropertyChanged("PartName");
OnPropertyChanged("RequestedQuantity");
OnPropertyChanged("AvailableQuantity");
OnPropertyChanged("ScannedPart");
}
}
}
Base ViewModel
public class BaseViewModel : INotifyPropertyChanged
{
public SQLiteConnection db = TimsData.database;
bool isBusy = false;
public bool IsBusy
{
get { return isBusy; }
set { SetProperty(ref isBusy, value); }
}
string title = string.Empty;
public string Title
{
get { return title; }
set { SetProperty(ref title, value); }
}
protected bool SetProperty<T>(ref T backingStore, T value,
[CallerMemberName]string propertyName = "",
Action onChanged = null)
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
I think the problem is that you are binding directly to a property, but then changing the base object, not just the property. Try this instead
productsLabel.SetBinding(Label.TextProperty, "ScannedPart.PartName");
// you already have a BindingContext set for your page that the Label will inherit
// productsLabel.BindingContext = viewModel.ScannedPart;