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);
}
Related
I'm navigating from one page to another (using Navigation.PushModalAsync). The first page has a list. The second page has a list view. I want to pass on that list to the second page and then populate a list view with data from that list. How do I go about this?
kind regards
UPDATE:
It appears as though my listview isn't appearing. I tried manually setting the itemsource and the next page is still blank. I have this method:
protected override void OnAppearing()
{
base.OnAppearing();
var listView = new ListView();
//listView.ItemsSource = dataSource;
listView.ItemsSource = new string[]{
"mono",
"monodroid",
"monotouch",
"monorail",
"monodevelop",
"monotone",
"monopoly",
"monomodal",
"mononucleosis"
};
listView.RowHeight = 40;
}
But the next page remains blank with just a red background. To add to this, I don't seem to have the listview.ItemSource.Add() method.
Have a look at the Messaging Center in Xamarin Forms here, which is one option: https://developer.xamarin.com/guides/xamarin-forms/application-fundamentals/messaging-center/
Or another option is just passing the list in the constructor of the modal you are pushing. In my opinion, using the messaging center is a much cleaner way.
in Page1
List<string> mydata;
var page2 = new Page2(mydata);
Navigation.PushModalAsync(page2);
in Page2
List<string> Data { get; set; }
public Page2(List<string> data) {
this.Data = data;
}
public override void OnAppearing() {
MyListView.ItemsSource = Data;
}
I have problem with ordering data for ListView. I have EventDisplay class which is an ObservableCollection for ListView(called Events)
private ObservableCollection<EventDisplay> currentEvents = new ObservableCollection<EventDisplay>();
private void Events_Loaded(object sender, RoutedEventArgs e)
{
sv = (ScrollViewer)VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(this.Events, 0), 0);
Events.ItemsSource = currentEvents;
}
I then add new data by function :
private void LoadDataToList(List<EventDisplay> newItems)
{
foreach (EventDisplay ed in newItems)
{
//Set some additional data
currentEvents.Add(ed);
}
//When this line below is commented ListView data is updated
//but is not sorted, when i uncomment the listview data is not being updated
//currentEvents = new ObservableCollection<EventDisplay>(currentEvents.OrderByDescending(x => x.ed.date).ToList());
}
So what is the proper way of ordering data for ListView in Windows 8.1 apps ?
You can sort & filter the view of your ObservableCollection (explanation here)
public class ViewableCollection<T> : ObservableCollection<T>
{
private ListCollectionView _View;
public ListCollectionView View
{
get
{
if (_View == null)
{
_View = new ListCollectionView(this);
}
return _View;
}
}
}
Data structure for the example:
interface ICustomer
{
string CuctomerName{get;set;}
int Age{get;set;}
}
Example use of the code:
ViewableCollection<ICustomer> vCustomers = new ViewableCollection<ICustomer>();
// Sorting settings:
ViewableCollection<ICustomer> vCustomers.View.SortDescriptions.Add(new SortDescription("CustomerName", ListSortDirection.Ascending));
vCustomers.View.Filter = MyCustomFilterMethod;
// add data to collection
MyCustomers.ToList().ForEach(customer => vCustomers.Add(customer));
Examlpe filter method:
private bool MyCustomFilterMethod(object item)
{
ICustomer customer = item as ICustomer;
return customer.Age > 25;
}
when you need to refresh the filter, the only thing you need to do is call:
this.vCustomers.View.Refresh();
Then you bind your GUI to vCustomers.View
You don't need to reset binding sources etc.
Use this for your add items code:
foreach (EventDisplay ed in newItems.OrderByDescending(x => x.ed.date).ToList()
{
//Set some additional data
currentEvents.Add(ed);
}
The reason your doesn't work is that you are reassigned the currentEvents reference rather than updating the ObservableCollection.
You should do the following :
currentEvents = new ObservableCollection<EventDisplay>(currentEvents.OrderByDescending(x => x.ed.date).ToList());
Events.ItemsSource = currentEvents;
This forces the ListView to rebind to your new sorted observable collection.
Another option is to sort the Observable collection in place. However, it may introduce flickering as the ListView will constantly update as the sort progresses.
If you don't want the ScrollView to reset its position, you can save the scrollview position and then restore it after sorting the list.
I've had success with Implementing a custom ObservableCollection that supports sorting but prevents UI flickering by suspending change notification during sort and then issuing a reset notification. The ScrollView should stay at its current position even when confronted with the reset event.
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"}
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.
I have a Form with a listview. After calling Form.Show I need to update my listview. However, after Form.Show is called regardless of my listview code, it comes up empty, no columns, no data. If I move the Form.Show till after my listview code, the listview shows correctly.
Here is my listview code :
private void InitializeListView()
{
_snapshotList.BeginUpdate();
_snapshotList.Items.Clear();
foreach (ISnapshot snapshot in _snapshots)
{
string comment = InstanceFactory<ProjectRecoveryService>.Instance.RetrieveCommentsforSnapshot(snapshot);
string[] sub = new string[] { snapshot.Name, snapshot.Version.ToString(), snapshot.CreatedDate.ToString(), comment };
ListViewItem item = new ListViewItem(sub);
item.Tag = snapshot;
this._snapshotList.Items.Add(item);
}
_snapshotList.EndUpdate();
this._snapshotList.Refresh();
}
A side note, I have another Form that is very similar but has a TreeView that someone else has extended which works as desired.
Any thoughts?
EDIT 1
This form needs be a single instance. After reading this post, my Form.Show code is structured like this :
public static RestoreSnapshotDialog GetInstance()
{
if (_dialog == null)
{
_dialog = new RestoreSnapshotDialog();
_dialog.Show(Control.FromHandle(System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle));
}
else
{
_dialog.BringToFront();
}
return _dialog;
}
On a FormClosed event I set _dialog = null.
You have to handle Form.Shown event to update the listview.
The only solution I could find was to call the Form.Show() after my listview was fully populated. So I create my own Form.Show by overriding Form.Show.
public new void Show()
{
if (_showdialog)
{
_dialog.Show(Control.FromHandle(System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle));
}
else
{
_dialog.BringToFront();
}
}
Calling this method after my listview solves my problem. However, all my other dialogs (not using listview) work as expected with the code from the original post. Thanks to Hans Passant for leading me to this solution.