Event when Xamarin ListView BindingContext item changes - xamarin

Is is possible to get an event when an item in the collection bound to a Xamarin Forms listview changes?
For example my object has a Date field which is bound to a label in a ViewCell. I would like an event fired when the Date is changed. Our object implements INotifyPropertyChanged so the listview updates properly.
I can manually subscribe to the OnPropertyChanged event of each item but I'm hoping their is an easier way.
Thanks.

There are triggers in Xamarin.Forms. It seems like an event trigger will do what you need. For example:
<EventTrigger Event="TextChanged">
<local:NumericValidationTriggerAction />
</EventTrigger>
public class NumericValidationTriggerAction : TriggerAction<Entry>
{
protected override void Invoke (Entry entry)
{
double result;
bool isValid = Double.TryParse (entry.Text, out result);
entry.TextColor = isValid ? Color.Default : Color.Red;
}
}
You can find more information about triggers here
See this example for deleting an object when it is selected from a list view.
private MyItemsViewModel _myItemsViewModel;
private void MyItemsListView_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
MyItem item = (MyItem)e.SelectedItem;
if (item == null)
return;
// remove the item from the ObservableCollection
_myItemsViewModel.Items.Remove(item);
}

Related

How to Refresh the Bindings on HomePageViewModel when some data has been changed in Navigated Page2

I have some property which I am using from Page2 in HomePageViewModel, When I navigate to Page2 I have changed that property and on coming back by doing NavigiationPop.
HomePage is not re-loaded/ refreshed at all as I have set the BindingContext in the constructor of HomePage, which is loaded only once.
The solution which is working for me is
Setting the BindingContext to the ViewModel on "onAppearing()", usually its not consider the best practice. But I also need to refresh the values which I have changed on Page2.
Looking forward for your valuable inputs and suggestions.
Also Pros/Cons of Setting the BindingContext in OnAppearing.
Thanks,
Hemant
It depends on you, they all can achieve the effect.
However if you want to reduce the CPU consumption of the program, then don't use the onAppearing method too much to refresh the page data.
Another method is that you can change the data of the model through Delegate/Event/MessageCenter.
You can refer to this discussion to know how to use Delegate/Event/MessageCenter.
Here I will give the sample by using MessageCenter to achieve that.
For example, the TestViewModel as follows:
public class TestViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private string pagetitle;
public string PageTitle
{
get
{
return pagetitle;
}
set
{
if (pagetitle != value)
{
pagetitle = value;
NotifyPropertyChanged();
}
}
}
}
Then bind it in FirstPage as follows, and Subscribe MessageCenter inside it:
TestViewModel testViewModel = new TestViewModel();
BindingContext = testViewModel;
MessagingCenter.Subscribe<object>(this, "Hi", (sender) =>
{
// Do something whenever the "Hi" message is received
testViewModel.PageTitle = "Modified Title";
});
Then in the SecondPage, when changing the data by sending message. When back to FirstPage, the View Will be updated.
private void Button_Clicked(object sender, EventArgs e)
{
MessagingCenter.Send<object>(this, "Hi");
}

Create Behavior from Map Property in Control - Xamarin Forms MVVM Prism ArcGIS

I'm trying to show a callout when navigating from View A (form view) to View B (map view). I pass the record detail for the callout from View A to View B and it's bound to viewmodel of View B.
I would like to access the LoadStatusChanged event on the Map property of the Esri MapView control while adhering to MVVM architecture. Here is what my control looks like:
<esri:MapView x:Name="mapViewMain"
Map="{Binding MainMap}"
Grid.Row="0"
InteractionOptions="{Binding MapViewOptions}"
GraphicsOverlays="{Binding GraphicsOverlays}">
<esri:MapView.Behaviors>
<bh:ShowCalloutOnTapBehavior CalloutClickCommand="{Binding GoToDetailCommand}" />
<bh:ShowCalloutOnDataReceivedBehavior MeterMasterRequest="{Binding RequestParameters}" Map="{Binding MainMap}" />
</esri:MapView.Behaviors>
</esri:MapView>
I think I need to create a behavior that will take in the Map, wait for it to finish load, then show callout (results of MeterMasterRequest) on MapView (the ShowCallout method is on MapView control).
public class ShowCalloutOnDataReceivedBehavior : BehaviorBase<MapView>
{
public static readonly BindableProperty MeterMasterRequestProperty =
BindableProperty.Create(nameof(MeterMasterRequest), typeof(MeterMasterRequest), typeof(ShowCalloutOnDataReceivedBehavior));
public static readonly BindableProperty MapProperty =
BindableProperty.Create(nameof(Map), typeof(Map), typeof(ShowCalloutOnDataReceivedBehavior));
public MeterMasterRequest MeterMasterRequest
{
get { return (MeterMasterRequest)GetValue(MeterMasterRequestProperty); }
set { SetValue(MeterMasterRequestProperty, value); }
}
public Map Map
{
get { return (Map)GetValue(MapProperty); }
set { SetValue(MapProperty, value); }
}
How can I bind to the Map event from here? I don't know how I can get BehaviorBase to be of type Map. I seem to only be able to set behaviors at MapView level.
You can access the Map from the MapView.Map property and the MapView is passed in to the OnAttachedTo and OnDetachingFrom methods of the Behavior class. I see you have a BehaviorBase, which hopefully has the OnAttachedTo and OnDetachingFrom overrides still marked as protected virtual so you can override them in your ShowCalloutOnDataReceivedBehavior class
Override the OnAttachedTo method and then subscribe to the LoadStatusChanged event, override OnDetachingFrom so you can unsubscribe like so:
protected override void OnAttachedTo(MapView bindable)
{
base.OnAttachedTo(bindable);
bindable.Map.LoadStatusChanged += Map_LoadStatusChanged;
}
protected override void OnDetachingFrom(MapView bindable)
{
base.OnDetachingFrom(bindable);
bindable.Map.LoadStatusChanged -= Map_LoadStatusChanged;
}
private void Map_LoadStatusChanged(object sender, Esri.ArcGISRuntime.LoadStatusEventArgs e)
{
// Do stuff when LoadStatusChanged event is fired on the Map
}

How to implement AutoFill functionality in Xamarin Forms for iOS?

I have to implement autofill service for iOS in Xamarin forms, Is there any sample code ? How we can achieve it?
You can use package dotMorten.Xamarin.Forms.AutoSuggestBox from nuget .
Usage
xaml
<AutoSuggestBox PlaceholderText="Search" WidthRequest="200"
TextChanged="AutoSuggestBox_TextChanged"
QuerySubmitted="AutoSuggestBox_QuerySubmitted"
SuggestionChosen="AutoSuggestBox_SuggestionChosen"/>
Code Behind
private void AutoSuggestBox_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
{
// Only get results when it was a user typing,
// otherwise assume the value got filled in by TextMemberPath
// or the handler for SuggestionChosen.
if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
{
//Set the ItemsSource to be your filtered dataset
//sender.ItemsSource = dataset;
}
}
private void AutoSuggestBox_SuggestionChosen(AutoSuggestBox sender, AutoSuggestBoxSuggestionChosenEventArgs args)
{
// Set sender.Text. You can use args.SelectedItem to build your text string.
}
private void AutoSuggestBox_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
{
if (args.ChosenSuggestion != null)
{
// User selected an item from the suggestion list, take an action on it here.
}
else
{
// User hit Enter from the search box. Use args.QueryText to determine what to do.
}
}
For more details you can check https://github.com/dotMorten/XamarinFormsControls/tree/master/AutoSuggestBox

Xamarin MVVM Databinding not refreshing

i am struggling with the mvvm data binding. I am not using any framework for the mvvm, I got a very basic base class for my view models. I uploaded my example-app with my problem to GitHub, find the link below.
My problem:
I got a simple app with an tab menu. there are 2 tabs called "TabA" and "TabB". Both views have a simple view model. The view models are referencing a manager class which holds the data. The Manager class has to objects (objects of my datamodel-class which just contains a string and implements INotifyPropertyChanged) in an observablecollection. There is also a Property in the Manager which references the current choosen object (its just one of the 2 objects from the list).
There are 2 actions which can be done by "TabB". The first one works as expected. If you enter some new string into the entry an hit the first button, it updates the string of the current choosen object and updates the label in TabA.
What is not working? With the second Button in my "TabB" class you switch the value of the current choosen object in the Manager. In the debugger I can see that the value is changed, but the Label in "TabA" does not recognize that it has to update the value.
Can you help me?
https://github.com/dercdev/MVVM-Xamarin
With the help of Jason I came to something like this:
In my TabAViewModel I subscribed the event of the Manager:
public TabAViewModel()
{
_mgr = Manager.Instance;
_mgr.PropertyChanged += new PropertyChangedEventHandler(obj_PropertyChanged);
}
Then I raise the event:
private void obj_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
OnPropertyChanged("CurrentData");
}
Which updates the label of the view.
Is that okay or is there a "better" way to do it?
As far as I know, the better way is to use INotifyPropertyChanged. If you want to implement Notify, I think you need to implement INotifyPropertyChanged interface, you can create one class name ViewModelBase that inheriting INotifyPropertyChanged, like this:
public class ViewModelBase:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Then you can call RaisePropertyChanged method to inotify when property changed,
private string _text;
public string Text
{
get
{
return _text;
}
set
{
_text = value;
RaisePropertyChanged("Text");
}
}
ObservableCollection implements INotifyPropertyChanged, allowing the collection to notify the user when the contents of the collection have changed - and specifically, what changed within the collection. For example, if you add an item to the collection, the CollectionChanged event will be raised with properties that tell you the index of the new item as well as including the item in a list.
So ObservableCollection _list don't need to call RaisePropertyChanged method.
https://learn.microsoft.com/en-us/dotnet/api/system.collections.objectmodel.observablecollection-1.system-componentmodel-inotifypropertychanged-propertychanged?view=netframework-4.7.2

WP7 delete item from listBox via message box

need some help, when i click the tap_event I get a message box delete or cancel which works and the price is taken off the total but it does'nt update the shopping cart after, it crashes on "ListBoxCart.Items.Remove(curr), thanks in advance!
private void listBoxCart_Tap(object sender, GestureEventArgs e)
{
if (MessageBox.Show("Are you sure!", "Delete", MessageBoxButton.OKCancel)
== MessageBoxResult.OK)
{
foreach (Dvd curr in thisapp.ShoppingCart)
{
if (curr.Equals(listBoxCart.SelectedItem))
{
listBoxCart.Items.Remove(curr);
listBoxCart.SelectedIndex = -1;
total -= Convert.ToDecimal(curr.price);
NavigationService.Navigate(new Uri("/ShoppingCart.xaml", UriKind.RelativeOrAbsolute));
}
}
txtBoxTotal.Text = total.ToString();
listBoxCart.ItemsSource = thisapp.ShoppingCart;
}
else
{
NavigationService.Navigate(new Uri("/ShoppingCart.xaml", UriKind.RelativeOrAbsolute));
}
}
I have wrote an artile (sorry in french but you can read the XAML) : http://www.peug.net/2012/05/17/contextmenu-dans-listbox-datatemplate/
and in the code-behind : an example :
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
var menuItem = sender as MenuItem;
var fe = VisualTreeHelper.GetParent(menuItem) as FrameworkElement;
Dvd _fig = fe.DataContext as Dvd;
thisapp.ShoppingCart.Remove(_fig);
reloading();
}
When you set the ItemsSource property for the ListBox, it generates a read-only collection and displays them. What you're trying to do is access this read-only collection and modify it but because it's read-only, you can't do that.
Instead you can either have your collection implement the INotifyCollectionChanged interface and raise a collection changed event when the user has deleted the item or use an ObservableCollection instead to store your items. ObservableCollection implements the INotifyCollectionChanged interface for you so you can remove items from the ObservableCollection and the changes will reflect in the Listbox automatically.
ObservableCollection also implements INotifyPropertyChanged so any property updates will also be updated in the ListBox.

Resources