Xamarin forms: How to change the label text value from viewmodel? - xamarin.forms

I want to change my label text value of the previous page when back button pressed from the new page. I use messagecenter feature from new page and redirect the code flow to the RefreshCustomerDetails() in viewmodel.
I tried like below.
string _fullname = "";
public string FullName
{
protected set
{
if (_fullname != value)
{
_fullname = value;
OnPropertyChanged("FullName");
}
}
get { return _fullname; }
}
public void RefreshCustomerDetails()
{
//FullName = null;
FullName = Application.Current.Properties["customerFullName"].ToString();
}
<Label
x:Name="title_label"
Text="{Binding FullName}"
Font="Bold,18"
TextColor="Black"
Margin="-20,0,0,0"
HorizontalOptions="CenterAndExpand"
VerticalOptions="Center"/>
Taking the Fullname value from Local db and Bind it like above code, but no change in the name when back buttoon press. Try assign null value, that is also not working.
Any correction on my code?

Your code looks good, it should work. Does your ViewModel inherate from INotifyPropertyChanged? This is required for OnPropertyChanged():
public class MyViewModel : INotifyPropertyChanged
{
// stuff ...
}

Updated the code like below, remove RefreshCustomerDetails()
and add Device.BeginInvokeOnMainThread inside of MessagingCenter.
Text="{Binding FullName,Mode=TwoWay}"
MessagingCenter.Subscribe<AddCustomerBySOPage>(this, "Refresh", (sender) =>
{
Device.BeginInvokeOnMainThread(() =>
{
FullName = Application.Current.Properties["customerFullName"].ToString();
});
});

Related

Xamarin Forms 5.0.0.2515: Relative routing to shell elements is currently not supported

I am receiving the below error :
Relative routing to shell elements is currently not supported. I am running Xamarin forms 5.0.0.2515.
The project navigates to the Items Journal Template View Model where I select an item. The item's name is then passed to the Finished Goods View Model where I will do a search based on the value passed. However I am receiving the above error even though I have already done this on other pages without an error.
I have my routing registered:
public AppShell()
{
InitializeComponent();
Routing.RegisterRoute(nameof(LocatePage), typeof(LocatePage));
Routing.RegisterRoute(nameof(FinishedGoodsPage), typeof(FinishedGoodsPage));
Routing.RegisterRoute(nameof(ItemJournalTemplatePage), typeof(ItemJournalTemplatePage));
}
ItemJournalTemplateViewModel: I call the Finished Goods page and pass it the item's name...
async void SelectedItemJournalTemplate(ItemJournalTemplate item)
{
if (item == null)
return;
await Shell.Current.GoToAsync($"{nameof(FinishedGoodsPage)}?{nameof(FinishedGoodsViewModel.PassedJournalBatchName)}={item.Name}");
}
and the page receiving the call FinishedGoodsViewModel:
[QueryProperty(nameof(PassedJournalBatchName), nameof(PassedJournalBatchName))]
public class FinishedGoodsViewModel:BaseViewModel
{
private string passedJournalBatchName;
public string PassedJournalBatchName
{
get => passedJournalBatchName;
set
{
passedJournalBatchName = value;
OnPropertyChanged(nameof(PassedJournalBatchName));
}
} .....
}
What makes this odd is that I use this on the FinishedGoodsViewModel to call the LocateViewModel and it works fine:
public async void UpdateLocation(object value)
{
SelectedValue = (FinishedGood)value;
await Shell.Current.GoToAsync($"{nameof(LocatePage)}" +
$"?{nameof(LocateViewModel.ItemNo)}={SelectedValue.Item_No}" +
$"&{nameof(LocateViewModel.BatchName)}={SelectedValue.Journal_Batch_Name}" +
$"&{nameof(LocateViewModel.TemplateName)}={SelectedValue.Journal_Template_Name}" +
$"&{nameof(LocateViewModel.LineNo)}={SelectedValue.Line_No}");
}
LocateViewModel:
[QueryProperty(nameof(ItemNo), nameof(ItemNo))]
[QueryProperty(nameof(BatchName), nameof(BatchName))]
[QueryProperty(nameof(TemplateName), nameof(TemplateName))]
[QueryProperty(nameof(LineNo), nameof(LineNo))]
public class LocateViewModel : BaseViewModel
{
private string batchName;
public string BatchName
{ get => batchName;
set
{
batchName = value;
OnPropertyChanged(nameof(BatchName));
}
}
private string templateName;
public string TemplateName
{
get => templateName;
set
{
templateName = value;
OnPropertyChanged(nameof(TemplateName));
}
}
private string lineNo;
public string LineNo
{
get => lineNo;
set
{
lineNo = value;
OnPropertyChanged(nameof(LineNo));
}
}
private string itemNo;
public string ItemNo
{
get => itemNo;
set
{
itemNo = value;
OnPropertyChanged(nameof(ItemNo));
}
}...
}
Can someone please tell me where I am going wrong here?
It looks like the answer was in the AppShell.xaml file. The original Flyout pointed to the FinsishedGoodsPage but I changed it to ItemJournalTemplatePage, allowing the user to first make a selection first.
<FlyoutItem Title="Finished Goods" Icon="placeholder.png">
<ShellContent Route="FinishedGoodsPage" ContentTemplate="{DataTemplate local:ItemJournalTemplatePage}" />
</FlyoutItem>
I forgot I also needed to change the Route. It probably would not go to the FinishedGoodsPage because it thought it was already on that page.
<FlyoutItem Title="Finished Goods" Icon="placeholder.png">
<ShellContent Route="ItemJournalTemplatePage" ContentTemplate="{DataTemplate local:ItemJournalTemplatePage}" />
</FlyoutItem>

Create table in SQLite with generic class 'Quantity' as a picker - ERROR not a valid type for SQLite DB

Morning All,
xamarin forms - populate picker in MVVM from SQLite DB issue
So...Ive used the following post
https://www.c-sharpcorner.com/article/populate-picker-using-mvvm/
to help me include a populated picker using MVVM in my project, using a picker to allowing the user to select a 'Quantity' to order for each product (which all works fine as its populated from code, but now refactoring to load from SQLite..please see current code below)..
//ProductModel
public class ProductModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _ProductId;
private string _BrandName;
public ObservableCollection<Quantity> _ListQuantites;
private Quantity _selectedQuantity;
//ETC have removed the other properties for sake of this Q
//Constructor
public ProductModel()
{
//Subscription
this.PropertyChanged += OnPropertyChanged;
}
[PrimaryKey, AutoIncrement]
public int ProductId
{
get { return _ProductId; }
set
{
if (_ProductId == value) return;
_ProductId = value;
OnPropertyChanged();
}
}
public ObservableCollection<Quantity> ListQuantites
{
get
{
return _ListQuantites;
}
set
{
_ListQuantites = value;
OnPropertyChanged();
}
}
public Quantity SelectedQuantity
{
get
{
return _selectedQuantity;
}
set
{
if (value == null)
{
_selectedQuantity = _selectedQuantity;
}
else
{
_selectedQuantity = value;
OnPropertyChanged();
}
}
}
//OnPropertyChanged
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(SelectedQuantity))
{
//test quantity amount
}
}
// [NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Up Until now I was populating the ProductModel with its picker, from the constructor of the ProductViewModel page with:
WineList = new ObservableCollection<ProductModel>();
WineList.Add(new ProductModel { ProductId = 1, BrandName = "Mc guigans", Grape = "Red", ListQuantites = List_Quantites, Image = "W.png", Description = "Fruity Flav", Size="700ml", Price = 10.00M, SubTotalForItem = 0.00M, Genre = "Wine" });
//etc...and so on
//then
List_Quantites = PickerService.GetQuantitiesForProductPage();
//Picker service
public static ObservableCollection<QuantityModel> GetQuantitiesForProductPage()
{
var quantities = new ObservableCollection<QuantityModel>()
{
new QuantityModel() {Key=1, Value="0"},
new QuantityModel() {Key=2, Value="1"},
new QuantityModel() {Key=3, Value="2"},
new QuantityModel() {Key=4, Value="3"}
//etc
};
return quantities;
}
//XAML
<Picker Grid.Column="3" Grid.Row="0" Title=" " VerticalOptions="Center" x:Name="productPicker" VerticalTextAlignment="Center" HorizontalOptions="EndAndExpand" ItemsSource="{Binding ListQuantites}" ItemDisplayBinding="{Binding Value}" SelectedIndexChanged="QuantityChanged" SelectedItem ="{Binding SelectedQuantity}"/>
So...yeah as I said all works fine and dandy...But now I would like to load the ProductModel list from a table in SQLite....I have already used SQLite basic CRUD operations to create, load, view, update, edit...etc for orders made...so this is also working...the problem I seem to be having is creating the table, it is failing when I try to create an entry in the table with 'Quantity'....obviously...so I have changed the code...change 'Quantity' to object, then the plan being to populate this on the VM...but this also didnt work...
found another post relating to this:
https://forums.xamarin.com/discussion/2546/create-table-in-xamarin-throws-exception-because-of-generic-list
So 'Quantity' class is not a valid type for SQLite DB value...but turns out neither is object...has anyone idea for a work around for this...or some advice on how to refactor to resolve this?
any help or point in the right direction is appreciated thank Y

Invoking command on TextChange is not updating Text Source Immediately

I am using MVVM light in my windows phone 8.1 here is code
xaml
<TextBox Text="{Binding SearchText, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" >
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="TextChanged">
<Core:InvokeCommandAction Command="{Binding SearchTextChanged}"></Core:InvokeCommandAction>
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</TextBox>
here is my VM
private RelayCommand _searchTextChanged;
/// <summary>
/// Gets the SearchTextChanged.
/// </summary>
public RelayCommand SearchTextChanged
{
get
{
return _searchTextChanged
?? (_searchTextChanged = new RelayCommand(
() =>
{
LoadContents(this.SearchText);
}));
}
}
each time text is changed SearchTextChanged command is firing properly but the text in SearchText property is not updated it is one character less. e.g. if text in textbox is A than SearchText contains null. If text in textbox is 'aaa' than text in SearchText is only 'aa' the last character is always missing.
Any Ideas?
OK, so based on the comments, here's the answer - don't use the InvokeCommandAction but the two-way binding.
<TextBox Text="{Binding SearchText, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
In the viewmodel behind this, there's a property called SearchText which has a setter that can call the LoadContents method, something like this...
public string SearchText
{
get { return this.searchText; }
set
{
this.searchText = value;
NotifyPropertyChanged("SearchText");
LoadContents(this.searchText);
}
}
Every time the string in the TextBox changes, the setter is invoked and so is the LoadContents method.
Bumped into a very similar situation but the given workaround wasn't a solution in my case! So discovered another way to update the value.
The solution:
Pass the element as parameter to the command and update the source and then call the method.
public string SearchText
{
get { return this.searchText; }
set
{
this.searchText = value;
NotifyPropertyChanged("SearchText");
}
}
public RelayCommand SearchTextChanged
{
get
{
return _searchTextChanged
?? (_searchTextChanged = new RelayCommand(
() =>
{
someCommandAction();
}));
}
}
private void someCommandAction(object obj)
{
TextBox textBox = (obj as TextBox);
if (textBox != null)
{
var be = textBox.GetBindingExpression(TextBox.TextProperty);
if (be != null)
be.UpdateSource();
}
LoadContents(textBox.Text); //textBox.text or the SearchTextproperty itself
}
XAML part:
<TextBox x:Name="textBoxName" Text="{Binding SearchText,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" >
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="TextChanged">
<Core:InvokeCommandAction Command="{Binding SearchTextChanged}"
CommandParameter="{Binding ElementName=textBoxName}" />
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</TextBox>

Windows Metro Style app data binding

I have a requirement to load images from a folder in the project to a stackpanel. Under each image a name should also be shown. The image folder can change at any time and the number of images can also change.(with a maximum of 50 images) I want to know if I can use data binding to handle this. I thought of having image ID's, their sources and the name for each image in an XML so that I can change that XML file whenever the image folder changes, without changing the rest of the code. Is that feasible? If so how? Can someone please guide me? Thank you in advance.
One solution would be to use a Filepicker to let the user select the images inside the folder, and then bind the selected images to an Itemscontrol. That itemscontrol can then be put inside the Stackpanel. Here's a quick sample using that solution.
Here's the codebehind for picking the image files:
private List<EditableImage> availableImagesList = new List<EditableImage>();
private async void FilePicker_Clicked(object sender, RoutedEventArgs e)
{
FileOpenPicker openPicker = new FileOpenPicker();
openPicker.ViewMode = PickerViewMode.List;
openPicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
//TODO: add supported image file types
openPicker.FileTypeFilter.Add("jpg,png,gif");
// We prompt the user to pick one or more files
IReadOnlyList<StorageFile> files = await openPicker.PickMultipleFilesAsync();
if (files.Count > 0)
{
availableImages.DataContext = null;
String fp = ""; // The path of the picked image
int index = availableImagesList.Count;
foreach (StorageFile file in files)
{
// Copying the selected image to local app data folder
//TODO: check if the selected file is actually and image
if (file != null )
{
StorageFile fileCopy = await file.CopyAsync(ApplicationData.Current.LocalFolder, file.DisplayName + file.FileType, NameCollisionOption.ReplaceExisting);
fp = fileCopy.Path;
}
//Creating the image
CustomImage picToAdd = new CustomImage(index+1, file.DisplayName, fp);
//Adding the image as an UI element to the app bar
availableImagesList.Add(picToAdd);
}
availableImages.DataContext = availableImagesList;
}
}
The CustomImage model:
public class CustomImage
{
private static Uri _baseUri = new Uri("ms-appx:///");
private int _id;
public int Id
{
get { return _id; }
set
{
this.SetProperty(ref this._id, value);
}
}
private string _name;
public string Name
{
get { return _name; }
set
{
this.SetProperty(ref this._name, value);
}
}
private string _imgPath;
public string ImgPath
{
get { return _imgPath; }
set
{
this.SetProperty(ref this._imgPath, value);
}
}
private String _imagePath = null;
private ImageSource _image = null;
public ImageSource Image
{
get
{
if (this._image == null && this._imagePath != null)
{
this._image = new BitmapImage(new Uri(CustomImage._baseUri, this._imagePath));
}
return this._image;
}
set
{
this._imagePath = null;
this.SetProperty(ref this._image, value);
}
}
public void SetImage(String path)
{
this._image = null;
this._imagePath = path;
this.OnPropertyChanged("Image");
}
public CustomImage(int id, string name, string imagepath)
{
SetImage(imagepath);
_id = id;
_name = name;
}
}
Here's the XAML for the ItemsControl inside the Stackpanel:
<StackPanel x:Name="loadedImages" HorizontalAlignment="Left" Orientation="Horizontal">
<!--Displaying the selected images in stackpanel-->
<ItemsControl ItemsSource="{Binding}" ItemsPanel="{StaticResource LoadedItemsPanel}">
<ItemsControl.ItemTemplate>
<!--The template for each object that is displayed as an UI element-->
<DataTemplate>
<Grid Height="88" Margin="2,0" Width="88" >
<Image Source="{Binding Image}"/>
<TextBlock Text="{Binding Name}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
In your page resources, you must also define:
<ItemsPanelTemplate x:Key="LoadedItemsPanel">
<WrapGrid Orientation="Horizontal"/>
</ItemsPanelTemplate>

Binding without DependencyProperty

How to bind property in Two Way?
Here is my Code:
public interface IDataOperations
{
string GetData();
string SaveData();
}//close interface
public class Vendor : IDataOperations
{
private string _vendorName = "My Vendor Name";
public string VendorName
{
get { return _vendorName; }
set { _vendorName = value; }
}
}
How can I bind VendorName in my Xaml? I need to bind to Text Box in such a way that if user changes text box value, it should also change the value of that VendorObject too.
Where should I declare VendorObject? Either in xaml.cs file or xaml by using ?
your XAML code should look like
<TextBox Binding="{VendorName}" Name="textBox1" />
and on your Window_Loaded function add this code:
textBox1.DataContext=v;
where v is an instance of the Vendor class.

Resources