I am having trouble using the Add method for an ObservableCollection to simply add a new string value to the observablecollection upon a click event. I create my ObservableCollection in a Settings.cs class and then reference that observablecollection throughout multiple pages in my wp7.1 project. This system has worked perfectly for when I need to add several items of one observablecollection to another, either setting one equal to the other or using .Union depending on the purpose needed. In this case though, I am attempting to add a single string item to my ObservableCollection of type string. My code is as follows
Settings.cs
public static Setting<ObservableCollection<string>> Favorites = new Setting<ObservableCollection<string>>("Favorites", null);
Favorites.xaml
<ListBox x:Name="FavoritesListBox" Grid.Row="1" ItemsSource="{Binding}" Margin="12,0,12,0"
SelectionChanged="FavoritesListBox_SelectionChanged">
FavoritesPage.xaml.cs
protected override void OnNavigatedTo(NavigationEventArgs e)
{
string favorUrl = null;
NavigationContext.QueryString.TryGetValue("curUrl", out favorUrl);
if (favorUrl != null )
{
//This works but the FavoritesListBox items are cleared upon new page navigation or closing
//this.FavoritesListBox.Items.Add(favorUrl);
//This does not work!?
//if (Settings.Favorites.Value == null)
//{
// //Settings.Favorites.Value.Add(favorUrl);
//}
//else
//{
// Settings.Favorites.Value.Add(favorUrl);
//}
}
//base.OnNavigatedTo(e);
}
private void FavoritesListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
this.NavigationService.Navigate(new Uri("/MainPage.xaml?favUrl=" + e.AddedItems[0], UriKind.Relative));
}
using the .Add method in FavoritesPage.xaml.cs does not give me any coding errors but when debugging I get a NullReferenceException. I also tried using .Insert and that did not work either. Please help this seems to be an easy fix but I have not been able to figure this out! Thanks in advance!
You're referencing a null object after confirming that it is null!
if (Settings.Favorites.Value == null)
{
Settings.Favorites.Value.Add(favorUrl); // throws NullReferenceException
// because Value is null
}
You need to do this:
if (Settings.Favorites.Value == null)
{
Settings.Favorites.Value = new ObservableCollection<string>();
}
Settings.Favorites.Value.Add(favorUrl);
Alternately, you can change the initialization from
public static Setting<ObservableCollection<string>> Favorites =
new Setting<ObservableCollection<string>>("Favorites", null);
to
public static Setting<ObservableCollection<string>> Favorites =
new Setting<ObservableCollection<string>>("Favorites",
new ObservableCollection<string>());
This way you can avoid the null check.
Related
So at first I create a uicontrol A ,In MainWindow.xaml.cs, I can click a button to create a new A and then I use A.DataContext = new Book(....). Also, in the Uicontrol, i can click a button to call the following method
private void OnShowBook(object sender, RoutedEventArgs e)
{
Book theBook = this.DataContext as Book;
if (theBook != null)
MessageBox.Show(theBook.Title, theBook.Isbn);
}
This works because I created a new Book using A.DataContent =... before.
Now I changed a little bit. i did not create an instance using A.DataContent = new Book(...). Instead I created another BookFactory.cs to hold the data and in the Uicontrol i used
<ObjectDataProvider x:Key="theBook" ObjectType="local:BookFactory" MethodName="GetTheBook" />
<Grid x:Name="grid1" DataContext="{StaticResource theBook}">
to use it. Every thing works except the button-->OnshowBook. My question is in this case how to access the Book object inside the Uicontrol XAML file ?
Update new question
I've tried to use
private void OnShowBook(object sender, RoutedEventArgs e)
{
Book thebook = ((this.FindResource("theBook") as ObjectDataProvider).Data as Book);
if (thebook != null)
MessageBox.Show(thebook.Title, thebook.Isbn);
}
The codes compiles ok but throws error at runtime. it can not find key theBook which i indeed declared.
Ok. i found out something . I need to use FindResource to access the object inside xmal.
I am writing my first application for WP8 platform in C#. I implemented three datatypes namely locationModel which has locationGroups. Each locationGroup has a ObservableCollection of type locationData.
locationData has two double types for latitude and longitude and a title string.
I used a textblock inside a stackpanel to show the locationData element's title, where the lat long are hidden to user.
There is a context menu on each of this textblock element which enable the user to delete the respective locationData.
When I open the app and delete any item, it succesfully does and updates the view too. But when I do it for another item it just doesnt work. I cannot delete more than one items for each time I open the app.
I used breakpoints to see where the problem is. the selected locationData is succesfully passed to the App.ViewModel.LocationModel.Items.Remove(). But it just that they are not deleted from the observable collection. I even tried to see the index of the locationData in observable collection and use RemoveAt method. Even it doesnt work.
I did a lot of research to find the problem, but no one seems to face the same problem as me. I referred to msdn article on how to implement inotifypropertychanged to update the collection. But its too complex for me to understand that.
I dont really understand why the observable collection delete the item for the second time even though if I pass the index of that item. And my usage of breakpoints showed me that the data is not even null.
So kindly tell me what is causing this problem and how do I overcome it so that I can implement my own workaround and not face this issue again. I can show you the code if you want me to.
Thanks.
CODE:
Adding an item
private void SaveLocationData(LocationData locationData)
{
IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings;
try
{
App.ViewModel.Custom.Items.Add(locationData);
var data = JsonConvert.SerializeObject(App.ViewModel.Custom);
appSettings[LocationModel.CustomKey] = data;
appSettings.Save();
//Notify that data is changed
App.ViewModel.LoadData();
NavigationService.Navigate(new Uri("/MainPage.xaml", UriKind.RelativeOrAbsolute));
}
catch(IsolatedStorageException ex)
{
MessageBox.Show(ex.Message);
}
}
Deleting item:
private void DeleteLocationData(LocationData locationData)
{
IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings;
try
{
App.ViewModel.Custom.Items.Remove(locationData);
var data = JsonConvert.SerializeObject(App.ViewModel.Custom);
appSettings[LocationModel.CustomKey] = data;
appSettings.Save();
//Notify that data is changed
App.ViewModel.LoadData();
NavigationService.Navigate(new Uri("/MainPage.xaml", UriKind.RelativeOrAbsolute));
}
catch (IsolatedStorageException ex)
{
MessageBox.Show(ex.Message);
}
}
One more thing that I want to say is, whenever I add a locationData to the collection, it updates automatically. Because adding is done on another page and when the mainpage.xaml loads(in which the observable collection data is), it updates automatically because of the code in OnNavigatedTo method.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (!App.ViewModel.IsDataLoaded)
{
App.ViewModel.LoadData();
}
}
And LoadData method is :
public void LoadData()
{
Custom = LoadCustomLocations();
IsDataLoaded = true;
}
private LocationGroup LoadCustomLocations()
{
string dataFromAppSettings;
LocationGroup data;
if (IsolatedStorageSettings.ApplicationSettings.TryGetValue(CustomKey, out dataFromAppSettings))
{
data = JsonConvert.DeserializeObject<LocationGroup>(dataFromAppSettings);
}
else
{
data = new LocationGroup();
}
return data;
}
So, can anyone help ?
In the case you described on your comment I think you set the DataContext to your Items. When you create a new Items-List the DataContext will be lost. So you have to reset the DataContext to the new loaded Items-List
I have 2 LookUpEdit controls from DevExpress on my form. Both use an ObservableCollection as it's datasource, one being of type string and the other of type double. The LookUpEdit control has an event called ProcessNewValue which fires when, you guessed it, a new value is entered in the control. I've added some code in this event to add the newly added value to the ObservableCollection and it automatically selects it once done. This works as expected for the string LooUpEdit but when I try it with the double LookUpEdit`, it adds it to the collection but then it clears out the control.
Here's the code to load the controls, which gets called in Form_Load():
void InitControls()
{
double[] issueNumbers = new double[5];
issueNumbers[0] = 155;
issueNumbers[1] = 156;
issueNumbers[2] = 157;
issueNumbers[3] = 158;
issueNumbers[4] = 159;
ObservableCollection<double> issues = new ObservableCollection<double>(issueNumbers);
lookupIssues.Properties.DataSource = issues;
DevExpress.XtraEditors.Controls.LookUpColumnInfoCollection colInfo = lookupIssues.Properties.Columns;
colInfo.Clear();
colInfo.Add(new DevExpress.XtraEditors.Controls.LookUpColumnInfo("Column"));
colInfo[0].Caption = "Issue ID's";
string[] stringNumbers = Array.ConvertAll<double, string>(issueNumbers, Convert.ToString);
ObservableCollection<string> issuesString = new ObservableCollection<string>(stringNumbers);
lookupStringValue.Properties.DataSource = issuesString;
colInfo.Clear();
colInfo.Add(new DevExpress.XtraEditors.Controls.LookUpColumnInfo("Column"));
colInfo[0].Caption = "String Issue ID's";
}
And here's the ProcessNewValue event for both (I've renamed them to try to make it easier to see which does what):
private void OnProcessNewValue_Double(object sender, DevExpress.XtraEditors.Controls.ProcessNewValueEventArgs e)
{
ObservableCollection<double> source = (ObservableCollection<double>)(sender as LookUpEdit).Properties.DataSource;
if (source != null)
{
if ((sender as LookUpEdit).Text.Length > 0)
{
source.Add(Convert.ToDouble((sender as LookUpEdit).Text));
(sender as LookUpEdit).Refresh();
}
}
e.Handled = true;
}
private void OnProcessNewValue_String(object sender, DevExpress.XtraEditors.Controls.ProcessNewValueEventArgs e)
{
ObservableCollection<string> source = (ObservableCollection<string>)(sender as LookUpEdit).Properties.DataSource;
if (source != null)
{
if ((sender as LookUpEdit).Text.Length > 0)
{
source.Add((sender as LookUpEdit).Text);
(sender as LookUpEdit).Refresh();
}
}
e.Handled = true;
}
As you can see, the code it identical with the exception of one converting text to a double before adding it to the collection.
Anyone know why the double value gets added to the collection but the control doesn't automatically select it like it does with a string collection? I've even tried to hard-code the newly added value right after e.Handled = true; but it still doesn't select it. What's weird is that if I run it through the debugger, I can step through and see that the lookupIssues control indeed gets the newly added value AND it's Text property is set to it, but as soon as the event terminates, the control clears it out.....really strange.
Any help is greatly appreciated!
BTW, I can add a link to a sample project that duplicates the problem but you would need to have DevExpress v12.2.6 controls installed in order to compile the project.
I posted this to the DevExpress team as well and they were gracious enough to provide the solution:
I agree that this discrepancy appears confusing as-is. The reason for the discrepancy is LookUpEdit.ProcessNewValueCore makes a call to RepositoryItemLookUpEdit.GetKeyValueByDisplayValue which returns a null value from the LookUpListDataAdapter because no implicit conversion exists from double to string. You may resolve the discrepancy with the following change to your ProcessNewValue handler:
private void OnProcessNewValue_Double(object sender, DevExpress.XtraEditors.Controls.ProcessNewValueEventArgs e)
{
ObservableCollection<double> source = (ObservableCollection<double>)(sender as LookUpEdit).Properties.DataSource;
if (source != null) {
if ((sender as LookUpEdit).Text.Length > 0) {
double val = Convert.ToDouble((sender as LookUpEdit).Text);
source.Add(val);
e.DisplayValue = val;
(sender as LookUpEdit).Refresh();
}
}
e.Handled = true;
}
The control now behaves as expected. I hope this can help someone else out :)
I'm still in the learning Phase of WPF, EF and MVVM and now I got the following problem. I can delete and insert new items in my DataGridView but I don't know how to update my items.
All I do is select an emptyrow which already has a primary key and then I put the data into it. It's working (updating database) but the GridView is not refreshing. I Need to restart the program first to see my updated data.
My Execute Command to Update my Database. I'm in the ViewModel class
public void ExecuteUpdate(object obj)
{
try
{
SelectedIndex.Child_Update(new Farbe { FarbauswahlNr = SelectedIndex.FarbauswahlNr, Kurztext = SelectedIndex.Kurztext, Ressource = SelectedIndex.Ressource, Vari1 = SelectedIndex.Vari1, Vari2 = SelectedIndex.Vari2 });
//ListeAktualisieren --> Refreshing the List
ListeAktualisieren();
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
Here is my Refresh Method which SHOULD Refresh the GridView. I'm in the ViewModel class
public void ListeAktualisieren()
{
farbliste.ListeAktualisieren(db);
farbliste.Model = farbliste.Model.Concat(farbliste.Addlist).ToList();
Model = farbliste.Model;
farbliste.Addlist.Clear();
}
The method is calling my Business List which also got a Refresh Method. Reading from my database here. I'm in the Business List class
public void ListeAktualisieren(TestDBEntities db)
{
Model.Clear();
foreach (var item in db.Farben)
{
//Insert and delete working
add = new Farbe { FarbauswahlNr = item.FarbauswahlNr, Kurztext = item.Kurztext, Ressource = item.Ressource, Vari1 = Convert.ToBoolean(item.Var1), Vari2 = item.Vari2 };
Addlist.Add(add);
}
}
Model is the Source of my GridView which is not Refreshing changed data when Updated but is showing new data rows when inserting or deleting.
You need Observablecollections and Classes with implemented INotifyPropertyChanged. Add the new element to the Observablecollection by insert and raise the event propertychanged by a change.
The rest should be done by WPF.
Edit: The Sourcecollection for the DataGrid needs to be the Observablecollection.
Edit2: To be nice I put the result of the comments here ;-)
Each row of the DataGrid is an element of the collection. Each cell of one row listens to a PropertyChangedEvent of its element (the String is Casesensitive so be carefull). If the getter of the property isn't called after the propertychangedevent the binding didn't receive the event.
This piece of Code can help asure that you don't call with nonexistent strings:
private void VerifyPropertyName(string PropertyName)
{
if (string.IsNullOrEmpty(PropertyName))
return;
if (TypeDescriptor.GetProperties(this)(PropertyName) == null) {
string msg = "Ungültiger PropertyName: " + PropertyName;
if (this.ThrowOnInvalidPropertyName) {
throw new isgException(msg);
} else {
Debug.Fail(msg);
}
}
}
Try adding this to your binding section
ItemsSource="{Binding Path=Model, UpdateSourceTrigger= PropertyChanged"}
Just started with Monodroid and I'm currently working on a ListView.
I got a List added to the ArrayAdapter and i can see my first two items correctly. However when i add a third element to the list, the listview doesnt update. Even though i call notifyDataSetChanged().
private ArrayAdapter<string> la;
private ListView list;
private List<String> dayData = new List<String>();
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
this.SetContentView(Resource.Layout.TestLayout);
dayData.Add(" Test");
dayData.Add(" Test2"); // Theese two elements shows up fine
list = this.FindViewById<ListView>(Resource.Id.menuList);
la = new ArrayAdapter<string>(this, Resource.Layout.list_item, dayData);
list.Adapter = la;
list.TextFilterEnabled = true;
dayData.Add(" Test3"); // This one is not shown
la.NotifyDataSetChanged();
} // OnCreate
Any clues on what i missed?
Found a solution myself at another forum. Somehow the ArrayAdapter does'nt take notice of list changes when using a List. Instead use a Android.Runtime.JavaList.
You can find the discussion here: http://mono-for-android.1047100.n5.nabble.com/Update-ListView-when-dataset-changes-td4757874.html
Works like a charm! :)
I am not entirely sure, but I think that the items are copied into the ArrayAdapter, thus what you need to do is:
la.Add(" Test3");
if you want to keep the list the same you will have to add it to list as well.
try this Add la.notifyDataSetInvalidated(); after la.NotifyDataSetChanged();
I'm not sure how much this will help anyone, but it seems to work fine in my instance.
I have a ViewModel class that keeps all the data I update within the App and trigger a "Collection Updated" action when a collection changes.
// All within ViewModel.cs
private Action SearchResultsUpdated;
private List<SearchResult> m_oSearchResults;
Public List<SearchResult> SearchResults
{
get
{
if (m_oSearchResults == null)
m_oSearchResults = new List<SearchResult> ();
return m_oSearchResults;
}
set
{
if (value != m_oSearchResults)
{
m_oSearchResults = value;
//
// Fire update event
if (SearchResultsUpdated != null)
SearchResultsUpdated ();
}
}
}
I then add a handler for this event within the adapter class.
// All within SearchResultsAdapter.cs
public class SearchResultsAdapter : BaseAdapter<SearchResult>
{
.
.
// Constructor
public SearchResultsAdapter (Activity oContext)
: base ()
{
// Add handler for list refresh
ViewModel.SearchResultsUpdated += NotifyDataSetChanged;
//
m_oContext = oContext;
}
}
Within the adapter I use the collection ViewModel.SearchResults as the data context for the list view. Hope that helps and is thorough enough for everyone to understand.
To update the ListView
private ListView lvAnuncios = null;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
this.lvAnuncios = this.FindViewById<ListView>(Resource.Id.MisAnuncios_lvAnuncios);
}
private void ReloadListView()
{
if (this.lvAnuncios.Adapter != null)
{
this.lvAnuncios.Adapter.Dispose();
this.lvAnuncios.Adapter = null;
}
//Class that inherits de ArrayAdapter
this.lvAnuncios.Adapter = new adAnuncio(this, Resource.Layout.FilaListViewAnuncio, csVariable.objUsr.lstAnuncios);
}