Xamarin Forms DataBinding Wont update - c#

I am struggling with DataBindings. I have a ListView (Displays Correctly) With items and I need to be able to edit the items in the list.
I select an item which opens a modal (works fine) with the information of the selected item (works fine). When I click save, the item is not updated - The display is not updated, but if I select the item again, the data is correctly held.
I have the following object:
public class Investigation : IDisposable
{
public List<InjuredPerson> InjuredPersonnel { get; set; }
...
}
My ViewModel is like this:
public class InvestigateUtilityDamagesViewModel : INotifyPropertyChanged
{
Investigation investigation;
private InvestigateDamages damage;
public event PropertyChangedEventHandler PropertyChanged;
public InvestigateUtilityDamagesViewModel(InvestigateDamages damage)
{
this.damage = damage;
Investigation = new Investigation();
Investigation.DamageID = damage.DamageID;
Investigation.InjuredPersonnel = damage.DamageDetails.InjuredPersonnel;
}
public Investigation Investigation
{
get { return investigation; }
set
{
if (investigation == value)
{
return;
}
investigation = value;
OnPropertyChanged("Investigation");
}
}
void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
SaveInvestigation();
}
}
The XAML:
<ListView ItemsSource="{Binding Investigation.InjuredPersonnel, Mode=TwoWay}">
...
The page which updates Information sends a Message like so: (works fine)
MessagingCenter.Send<EditInjuredPerson, InjuredPerson>(this, "InjuredPersonEdited", _injuredPerson);
And the receiving side (works fine)
private void SaveInjuredPerson(EditInjuredPerson sender, InjuredPerson InjuredPerson)
{
var Injured = this.FindByName<ListView>("listInjuries").SelectedItem as InjuredPerson;
if (Injured != null)
{
Injured.Name = InjuredPerson.Name;
Injured.Position = InjuredPerson.Position;
Injured.ContactNumber = InjuredPerson.ContactNumber;
Injured.Injury = InjuredPerson.Injury;
Injured.NextOfKinName = InjuredPerson.NextOfKinName;
Injured.NextOfKinNumber = InjuredPerson.NextOfKinNumber;
}
}

The InjuredPersonnel list needs the OnProprertyChanged event raised on it, not the Investigation (or in addition to).
Alternatively, convert the List<> to ObservableCollection<>.
public List<InjuredPerson> InjuredPersonnel { get; set; }
becomes
public ObservableCollection<InjuredPerson> InjuredPersonnel { get; set; }
Here is a related thread.
public class Investigation : IDisposable, INotifyPropertyChanged
{
private List<InjuredPerson> _injuredPersonnel;
public List<InjuredPerson> InjuredPersonnel {
get {
return _injuredPersonnel;
}
set
{
_injuredPersonnel = value;
OnPropertyChanged("InjuredPersonnel");
}
}
...
}

Related

Access to ObservableCollection

I'm asking how we can acceed to an ObservableCollection.
Actually I'm working on a project and i have to collect the checked elements in an ObservableCollection in order to copy these elements to a PDF file.
public Class FianlElements
{
private int chapAr;
public int ChapAr
{
get { return chapAr; }
set
{
chapAr = value;
OnPropertyChanged("ChaprAr");
OnPropertyChanged("Article");
}
}
private string article;
public string Article
{
get { return article; }
set
{
article = value;
OnPropertyChanged("ChapAr");
OnPropertyChanged("Article");
}
}
private float somme;
public float Somme
{
get { return somme; }
set
{
somme = value;
OnPropertyChanged("Somme");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertySomme)
{
if (PropertyChanged != null)
PropertyChanged(this, new
PropertyChangedEventArgs(propertySomme));
}
}
Actually this is the class of the type of ObservableCollection
My declaration of the ObservableCollection is here
public ObservableCollection<FinalSelection> LesElem { get; set; }
I have another ObservableCollection
public ObservableCollection<ListBoxArticle> LesArticles { get; set; }
This one is Binded to a ListBox which contains CheckBox and TextBox
like this
so I want to copy only the checked elements to "LesElem"
So how can I get access to this ObservableCollection
Thanx
Let's imagine you are interested in LesArticle which are ticked.
AND
You have an IsChecked property on LesArticle.
AND
This is bound to the IsChecked property of the checkbox.
The list of checked ones would be
var checkedList = LesArticle.Where(x => x.IsChecked == true).ToList();
Observablecollection can take a List as it's constructor. So you can do:
FinalElem = new ObservableCollection<ListBoxArticle>(checkedList);

Accessing ViewModel properties in parent ViewModel

I'm using this technique for navigation between views: https://social.technet.microsoft.com/wiki/contents/articles/30898.simple-navigation-technique-in-wpf-using-mvvm.aspx
I have the main ViewModel with menu buttons bound to SelectedViewModel property change commands:
class MainViewModel : INotifyPropertyChanged
{
public ICommand SomeViewCommand { get; set; }
public ICommand OtherViewCommand { get; set; }
private object selectedViewModel;
public event PropertyChangedEventHandler PropertyChanged;
public object SelectedViewModel
{
get { return selectedViewModel; }
set { selectedViewModel = value; OnPropertyChanged("SelectedViewModel"); }
}
public MainViewModel()
{
SomeViewCommand = new RelayCommand<object, object>(null, (object o) => OpenSomeView());
OtherViewCommand = new RelayCommand<object, object>(null, (object o) => OpenOtherView());
}
private void OpenSomeView()
{
SelectedViewModel = new SomeViewModel();
}
private void OpenOtherView(object obj)
{
if(SelectedViewModel != null && SelectedViewModel.GetType() == typeof(SomeViewModel))
{
SomeViewModel s = (SomeViewModel)SelectedViewModel;
// always 0
if (s.NumberOfChanges > 0)
{
MessageBox.Show("test", "Error");
}
// SelectedViewModel = new OtherViewModel(); after confirmation dialog
}
else
SelectedViewModel = new OtherViewModel();
}
private void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
If I'm in SomeView, I'd like to check its property (number of changes) before switching to OtherView and show a confirmation dialog to the user to confirm their action. I need the current value, but any property seems to have its initialization value. Why?
What would be the cleanest way of doing this? I know it can be done by making the property static, but that seems dirty to me.
In OnPropertyChanged method you can set NumberOfChanges.

WPF how to bidirectionally link a checkBox to a class member AND to another static variable

I have a checkbox which is binded to a class variable in the xaml code:
<CheckBox x:Name="cbxUseBubbleNotifications" Margin="20" IsChecked="{Binding Path=pcdLoggerData.UseBubbleNotifications, Mode=TwoWay}" Content="_Use bubble notifications" HorizontalAlignment="Left" VerticalAlignment="Top" Style="{DynamicResource CheckboxSwitchStyle}" />
this should be supposed to be a two way binding but what happen is:
the checkbox is set to CHECKED ----> the var pcdLoggerData.UseBubbleNotifications is automatically OK
the class is serialized (through datacontract serialization but I think that doesn't change anything).
I restart the program and so the pcdLoggerData.UseBubbleNotifications is automatically set to true
4 the checkbox is not set to TRUE <----- ERROR
point 4 is not correct: since two way I expect to do that automatically.
My class is:
[DataContract]
public class PCDLoggerBinSerializableData
{
public PCDLoggerBinSerializableData() { }
public PCDLoggerBinSerializableData(string _languageInUse, bool _useBubbleNotifications)
{
LanguageInUse = _languageInUse;
UseBubbleNotifications = _useBubbleNotifications;
}
[DataMember]
public string LanguageInUse { get; set; }
[DataMember]
public bool UseBubbleNotifications { get; set; }
}
}
Even more important I have to set another variable according to the same value/variations of pcdLogger.UseBubbleNotifications and that is a STATIC var.
something like Bubble.NoBubbles = !pcdmisData.UseBubbleNotifications
So two problems:
databinding not TWO-WAY working (only one way)
how to databind also another static var?
Thanks
--ADD--
Not working I put breakpoints in all parts of the class and they never were it.
This is how I did it:
[DataContract]
public class PCDLoggerBinSerializableData: INotifyPropertyChanged
{
#region CONSTRUCTORS
public PCDLoggerBinSerializableData() { }
public PCDLoggerBinSerializableData(string _languageInUse, bool _useBubbleNotifications)
{
LanguageInUse = _languageInUse;
UseBubbleNotifications = _useBubbleNotifications;
}
#endregion
#region OPTIONS
[DataMember]
public string LanguageInUse { get; set; }
[DataMember]
private bool useBubbleNotifications;
public bool UseBubbleNotifications
{
get { return useBubbleNotifications; }
set
{
useBubbleNotifications = value;
Bubble.NoBubblesPlease = !useBubbleNotifications;
OnPropertyChange("UseBubbleNotifications");
}
}
#endregion
#region NOTIFIER
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChange(string inName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("inName"));
}
#endregion
}
It would be something like:
public bool UseBubbleNotifications
{
get
{
return useBubbleNotifications;
}
set
{
useBubbleNotifications = value;
Other_Static_Variable = value;
OnPropertyChange("UseBubbleNotifications");
}
}
public void OnPropertyChange(string inName)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("inName"));
}
}
Something like this may work. Of course your class would have to inherit the INotifyPropertyChanged interface.

C# strange Double ObservableCollection behaviour

Maybe I don't understand the ObservableCollection well enough. But as far as I knew it was similar to a normal list, but with event triggers so that you can react to changes.
So I have this Windows store app. And in this application I have a main BusinessModel class which is the main source for all data in my client application. This data will be updated when the server has made some changes elsewhere. In the future I'd like to have this class update the ViewModels for specific data updates etc.
So I also have a ViewModel class which contains, at least in my PoC's so far, a copy of that list (also in the near future this list will have an enriched version of the list).
Since it's a copy they should be both separate instances and have their own separate items.
However when I update the copy in the ViewModel, the BusinessModel version changes with it.
And vice versa.
I can't seem to figure out why this is happening. Underneath you will find the classes and their functions:
//the BusinessModel Class
public class ModelStuff : INotifyPropertyChanged
{
private ObservableCollection<DataObject> _modelStuff;
public ObservableCollection<DataObject> modelStuff
{
get
{
return _modelStuff;
}
set
{
_modelStuff = value;
NotifyPropertyChanged("modelStuff");
}
}
private static ModelStuff businessModel;
public static ModelStuff BusinessModel
{
get
{
if (businessModel == null)
{
businessModel = new ModelStuff();
}
return businessModel;
}
}
public ModelStuff()
{
modelStuff = new ObservableCollection<DataObject>();
modelStuff.Add(new DataObject(0));
modelStuff.Add(new DataObject(1));
modelStuff.Add(new DataObject(2));
modelStuff.Add(new DataObject(3));
modelStuff.Add(new DataObject(4));
modelStuff.Add(new DataObject(5));
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
#endregion
}
//the ViewModel class
public class ViewModel : INotifyPropertyChanged
{
private ObservableCollection<DataObject> _visibleStuff;
public ObservableCollection<DataObject> visibleStuff
{
get
{
return _visibleStuff;
}
set
{
_visibleStuff = value;
NotifyPropertyChanged("visibleStuff");
}
}
private static ViewModel tvm;
public static ViewModel TVM
{
get
{
if (tvm == null)
{
tvm = new ViewModel();
}
return tvm;
}
}
public ViewModel()
{
visibleStuff = new ObservableCollection<DataObject>(ModelStuff.BusinessModel.modelStuff.OrderBy(c => c.testNumber));
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
#endregion
}
//the TestObjects
public class DataObject
{
public int testNumber { get; set; }
public String testStr { get; set; }
public DataObject(int i)
{
testNumber = i;
testStr = "testje";
}
}
//A randomly placed button invokes this function when clicked.
private void Button_Click(object sender, RoutedEventArgs e)
{
//do stuff here
int i0 = ModelStuff.BusinessModel.modelStuff[0].testNumber;
ViewModel.TVM.visibleStuff[0].testNumber = 100;
int i1 = ModelStuff.BusinessModel.modelStuff[0].testNumber;
//i1 has the value 100 in my logs! :S
}
//Second version but vice versa
private void Button_Click(object sender, RoutedEventArgs e)
{
//do stuff here
int i0 = ViewModel.TVM.visibleStuff[0].testNumber;
ModelStuff.BusinessModel.modelStuff[0].testNumber = 100;
int i1 = ViewModel.TVM.visibleStuff[0].testNumber;
//i1 has the value 100 in my logs! :S
}
Where has my reasoning gone wrong?
Why is this happening?
And more importantly, how can I prevent this behaviour?
As far as I can see, your line of code:
visibleStuff = new ObservableCollection<DataObject>(ModelStuff.BusinessModel.modelStuff.OrderBy(c => c.testNumber));
is not making a copy of the underlying objects at all. It is adding the same DataObjects from the original list to a new ObservableCollection.
You need to clone the DataObjects individually and add them to the new collection. Something like this should do it:
visibleStuff = new ObservableCollection<DataObject>(ModelStuff.BusinessModel.modelStuff.OrderBy(c => c.testNumber).Select(i => new DataObject(i.testNumber)));

INotifyPropertyChanged 'Double' binding

I'm trying to bind some XAML code to a property in my ViewModel.
<Grid Visibility="{Binding HasMovies, Converter={StaticResources VisibilityConverter}}">
...
</Grid>
My ViewModel is setup like this:
private bool _hasMovies;
public bool HasMovies
{
get { return _hasMovies; }
set { _hasMovies = value; RaisePropertyChanged("HasMovies"); }
}
In the constructor of the ViewModel, I set the HasMovies link:
MovieListViewModel()
{
HasMovies = CP.Connection.HasMovies;
}
in CP:
public bool HasMovies
{
get { return MovieList != null && MovieList.Count > 0; }
}
private ObservableCollection<Movie> _movies;
public ObservableCollection<Movie> MovieList
{
get { return _movies; }
set
{
_movies = value;
RaisePropertyChanged("MovieList");
RaisePropertyChanged("HasMovies");
_movies.CollectionChanged += MovieListChanged;
}
}
private void MovieListChanged(object sender, NotifyCollectionChangedEventArgs e)
{
RaisePropertyChanged("HasMovies");
}
What am I doing wrong? How should I change this binding so that it reflects the current state of CP.Connection.HasMovies?
Either directly expose the object in the ViewModel and bind directly through that (so that the value is not just copied once which is what happens now) or subscribe to the PropertyChanged event and set HasMovies to the new value every time it changes in your source object.
e.g.
CP.Connection.PropertyChanged += (s,e) =>
{
if (e.PropertyName = "HasMovies") this.HasMovies = CP.Connection.HasMovies;
};
First of all, the setter for a collection type, such as your MovieList property, is not called when you change the content of the collection (ie. Add/Remove items).
This means all your setter code for the MovieList property is pointless.
Secondly, it's very silly code. A much better solution, is to use NotifyPropertyWeaver. Then your code would look like this, in the viewmodel:
[DependsOn("MovieList")]
public bool HasMovies
{
get { return MovieList != null && MovieList.Count > 0; }
}
public ObservableCollection<Movie> MovieList
{
get;
private set;
}
Alternatively you would have to add a listener for the CollectionChanged event when you initialize the MovieList property the first time (no reason to have a backing property, really really no reason!), and then call RaisePropertyChanged("HasMovies") in the event handler.
Example:
public class CP : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public CP()
{
MovieList = new ObservableCollection<Movie>();
MovieList.CollectionChanged += MovieListChanged;
}
public bool HasMovies
{
get { return MovieList != null && MovieList.Count > 0; }
}
public ObservableCollection<Movie> MovieList
{
get;
private set;
}
private void MovieListChanged(object sender, NotifyCollectionChangedEventArgs e)
{
RaisePropertyChanged("HasMovies");
}
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}

Categories