Why the class NotifyCollectionChangedEventArgs in the ObservableCollection's event CollectionChanged have the properties:
IList NewItems
IList OldItems
As IList (notice the s at the end, also when I use it I have to call it like NewItems[0]) if the ObservableCollection don't allow Add Range, what is the purpose of that if the item modified will always be 1 item ? Or I am missing something ?
Related
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
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);
}
My code is,
DestMenu.Add(SelectedMenu);
ObservableCollection<MenuModel> temp =
(ObservableCollection<MenuModel>)DestMenu.OrderBy(p =>(p.MenuName));
DestMenu = temp;
Here, SelectedMenu is the new item to be added to collection. temp is dummy collection to swap. And, when i try to convert the sorted collection, if any, to ObservableCollection, it throws some exception.
Can someone help me sort the collection and make me understand the problem in my code?
Thanks
Manikandan
I got the trick.
DestMenu.Add(SelectedMenu);
IEnumerable<MenuModel> temp = DestMenu.OrderBy(p =>(p.MenuName));
DestMenu = new ObservableCollection<MenuModel>(temp);
This works and the DestMenu has the sorted collection now.
Thanks
Manikandan
Well so sad that the OrderBy Extension Method doesn't sort the collection but returns an IOrderdEnumerable so to handle this you should do the following
DestMenu = new ObservableCollection<MenuModel>(DestMenu.OrderBy(o=>o.MenuName));
Of course using ObservableCollection you need items to be updated on the view that wont actually happened when you use this approach of sorting because ObservableCollection updates items on the view because it fires CollectionChanged Event but in fact we have changed the Collection itself not an element within the collection so it would be good if you did the following
public YourClass : INotifyPropertyChanged
{
void YourMethod()
{
DestMenu = new ObservableCollection<MenuModel>(DestMenu.OrderBy(o=>o.MenuName));
RaisePropertyChanged("DestMenu");
}
event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged(string PropertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(PropertyName));
}
}
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.
I know that i can hide anything in codebehind, in selectionchanged event handler. But is it possible to, lets say, to have 2 PivotItems and one control outside of pivot, and hide that control, when 1st PivotItem is selected in xaml?
Worked, thanks to #Josh Earl, using the converter:
public class PivotIndexToVisibilityConverter : IValueConverter
{
public object Convert( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture )
{
int index = (int)value;
return index == 0 ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture )
{
Visibility visibility = ( Visibility )value;
return visibility == Visibility.Visible ? 0 : 1;
}
}
I don't think its possible to do this directly. You could get pretty close, though, if you databound your Visibility property to the PivotItem.SelectedItem property. You'd need to create a simple ValueConverter to translate your PivotItem index to a Visibility.Collapsed or Visibility.Visible as appropriate.
Here's a good intro to ValueConverter.