1

Working on an windows store app, I try to update/refresh a listView when some data is updated. But despite all samples and documentations I read, it doesn't work...

Here my code behind : (I do not provide my xaml files, it's just a sample listView).

So the class which implements the INotifyPropertyChanged interface:

public class Download : INotifyPropertyChanged
    {
        public enum DownloadState
        {
            Running,
            Waiting,
            Pausing,
            Paused,
            Cancelling,
            Cancelled
        };

        private String Uri;
        private StorageFile storageFile;
        private String tempFileName;



        private String fileName;
        private String version ;

        private long totalSize ;
        private long downloadedBytes;

        private DownloadState state;
        private Protocol protocol;


        public Download(String Uri, StorageFile file, String fileName, String version, long totalSize, Protocol protocol)
        {
            this.Uri = Uri;
            this.storageFile = file;
            this.tempFileName = "";
            this.fileName = fileName;
            this.version = version;

            this.totalSize = totalSize;
            this.downloadedBytes = 0;

            this.state = DownloadState.Waiting;

            this.protocol = protocol;


        }


        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            System.Diagnostics.Debug.WriteLine("Update!"); //ok
            if (PropertyChanged != null)
            {
                //PropertyChanged is always null and shouldn't.
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
           public DownloadState State
        {
            get{return this.state;}
            set { 
                this.state = value;
                NotifyPropertyChanged();
            }
        }

        //+some others methods

    }
}

And the main page of the metro app :

// The Basic Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234237

namespace ClientAirNavLight_WS
{
    /// <summary>
    /// A basic page that provides characteristics common to most applications.
    /// </summary>
    public sealed partial class MainPage : ClientAirNavLight_WS.Common.LayoutAwarePage
    {
        /// <summary>
        /// Represent a Web Service proxy.
        /// </summary>
        private AirNavLight_WSClientClient proxyWS;

        /// <summary>
        /// Initiialize the component of the application's main page.
        /// </summary>
        public MainPage()
        {
            this.InitializeComponent();          

        }



        /// <summary>
        /// Populates the page with content passed during navigation.  Any saved state is also
        /// provided when recreating a page from a prior session.
        /// </summary>
        /// <param name="navigationParameter">The parameter value passed to
        /// <see cref="Frame.Navigate(Type, Object)"/> when this page was initially requested.
        /// </param>
        /// <param name="pageState">A dictionary of state preserved by this page during an earlier
        /// session.  This will be null the first time a page is visited.</param>
        protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
        {
        }

        /// <summary>
        /// Preserves state associated with this page in case the application is suspended or the
        /// page is discarded from the navigation cache.  Values must conform to the serialization
        /// requirements of <see cref="SuspensionManager.SessionState"/>.
        /// </summary>
        /// <param name="pageState">An empty dictionary to be populated with serializable state.</param>
        protected override void SaveState(Dictionary<String, Object> pageState)
        {
        }


        //Simulate data update.
        private async void Button_Click(object sender, RoutedEventArgs e)
        {

            object selected = this.ListResult.SelectedItem;
            if (selected != null)
            {
                //simulate an update in data.
                // ListView should be refresh in order to reflect changes made here.
                Download dl = (Download)selected;
                if (dl.State == Download.DownloadState.Paused)
                {
                    dl.State = Download.DownloadState.Running;
                }
                else
                {
                    dl.State = Download.DownloadState.Paused;
                }

            }
            else
            {
                //Just add an item to the list view.
                StorageFile file = await this.getStorageFile("test");
                Download dl = new Download("192.128.2.14", file, "test", "1.2", 100, Protocol.HTTP);
                this.ListResult.Items.Add(dl);


                this.ListResult.DataContext = dl;// Does not work.


            }
        }


        private async Task<StorageFile> getStorageFile(String suggestedFileName)
        {
            FileSavePicker savePicker = new FileSavePicker();
            savePicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
            // Dropdown of file types the user can save the file as
            savePicker.FileTypeChoices.Add("Application/pdf", new List<string>() { ".pdf" });
            savePicker.FileTypeChoices.Add("Archive", new List<string>() { ".zip", ".rar", ".7z" });
            savePicker.FileTypeChoices.Add("Plain-text", new List<string>() { ".txt" });
            // Default file name if the user does not type one in or select a file to replace
            savePicker.SuggestedFileName = suggestedFileName;
            return await savePicker.PickSaveFileAsync();
        }


    }
}

So How am I supposed to use listView.DataContext? Am I misunderstanding how use INotifyPropertyChanged?

EDIT :

How my listView is define :

 <ListView x:Name="ListResult" HorizontalAlignment="Left" Height="200" Margin="10,56,0,0" VerticalAlignment="Top" Width="653" SelectionMode="Single"/>
Zangdak
  • 242
  • 4
  • 22

3 Answers3

3

In your Download class, the only Property that will update in the UI is State, since it has a getter and setter.

Without your XAML it's not possible to see what you are trying to display, but normally, you would bind properties of your DataContext object (dl in this case) to different controls in a datatemplate that the listview could then display.

But these properties need to be public and have getters and setters for INotifyPropertyChanged to update the UI that these properties are bound to.

For instance, if you wanted the filename to show up in the ListView, you would need, as a minimum, to declare the filename property like this:

public String fileName {get; set; }
Stewbob
  • 16,759
  • 9
  • 63
  • 107
  • I Don't use datatemplate in my listview. The listview use the default toString method to display the different object. In the class Download, the method `NotifyPropertyChanged` is call when I change the state but `PropertyChanged ` is always null. – Zangdak Aug 14 '13 at 07:53
  • 1
    This really worked for me, I stuck with this problem for 2 days, now it solved by simple getter and setter, thanks Stewbob – Thamilan S Jun 01 '15 at 16:18
3

Why are you setting this.ListResult.DataContext = dl? When dealing with ListViews, the ItemsSource is the collection for the all the objects that get iterated, not the DataContext. Furthermore, ItemsSource should be a collection and not one item. So if you're binding to a property, that property should exist as an item's property in the ItemsSource collection.

However, it looks like you don't have a collection so you're adding the dl directly to the ListView with this.ListResult.Items.Add(dl). That approach should work, however take out the this.ListResult.DataContext = dl or set this.ListResult.ItemsSource to a collection and add dl to that collection

I'm not familiar with [CallerMemberName] but if you're having problems with propertyName being null, try the following

    public DownloadState State
    {
        get{return this.state;}
        set {
            if( this.state != value )
            {
                this.state = value;
                NotifyPropertyChanged("State");
            }
        }
    }

Furthermore, all properties that you want to bind to should be public and call NotifyPropertyChanged, like the following

    private long totalSize ;

    public long TotalSize
    {
        get{return this.totalSize;}
        set {
            if( this.totalSize != value )
            {
                this.totalSize = value;
                NotifyPropertyChanged("TotalSize");
            }
        }
    }
davron12
  • 101
  • 5
  • Thanks but when I set `this.ListResult.ItemsSource` to a collection and add dl to that collection, the content of the collection is displayed only if I add the items to collection before I set `this.ListResult.ItemsSource` to this collection. And then, the problem is still the same : in my class Download the method `NotifyPropertyChanged` is call when I change the state but `PropertyChanged` is always null (using a collection or not). – Zangdak Aug 14 '13 at 08:01
  • @Francois, If you data bind to a collection, NotifyPropertyChanged only fires when you change the entire collection. It does not fire when you add or remove items from the collection. – Stewbob Aug 14 '13 at 10:36
  • @Stewbob, by writing `this.ListResult.Items.Add(dl);` the listview is bind to an item, not a collection? So the listView should subscribe to PropertyChanged? – Zangdak Aug 14 '13 at 11:23
  • 2
    @Francois, I think you are mis-understanding. davron12 has outlined how to use `INotifyPropertyChanged` and the proper way to do the data binding. However, your UI won't update unless you change the entire collection. If you just add items to the collection, the UI will not update. – Stewbob Aug 14 '13 at 13:22
  • So is it possible to update the UI when an item is add to the collection and when an item of this collection is updated? Do I need to update the all collections for each update (add item/update item)? Do I need to code my own collection in order to fire an `PropertyChanged` event and update the UI when I add/update an item? – Zangdak Aug 14 '13 at 13:36
  • 1
    What type of collection are you assigning to `this.ListResult.ItemsSource`? Use `ObservableCollection` as it fires events when items are added and removed and regular `Collection` does not. `NotifyPropertyChanged` does not fire when items are added to a collection, rather only when the property itself changes value. – davron12 Aug 14 '13 at 16:26
  • Thanks for your help @davon12. I used a simple List. If I used `ObservableCollection`, Does it subscribe to the `PropertyChanged` of items? Or do I need to extend the `ObservableCollection` to do so? Unfortunately, I can't test today... – Zangdak Aug 15 '13 at 08:42
  • 1
    If you use `ObservableCollection`, and the `Download` you're adding implement `INotifyPropertyChanged`, you should be all set. The observable collection notifies your ListView that items are added/removed and the `Download` notifies the ListView that its properties have been changed, without you having to do anything different than what you're already doing in your code. – davron12 Aug 15 '13 at 16:23
  • Thanks. So my listView is updated when I add an item to the `ÒbservableCollection` but it doesn't when I update an item property which fire an PropertyChanged event. I found this [link](http://stackoverflow.com/questions/1427471/observablecollection-not-noticing-when-item-in-it-changes-even-with-inotifyprop). I will try this and I'll inform you of the result. – Zangdak Aug 16 '13 at 10:04
  • Well its works. I Used @Jack Kenyon's example. It works perfectly, but because of the `NotifyCollectionChangedAction.Reset`, I had to remove all items transitons of my listView. Anyway, thanks @davron12 and @Stewbob for your help. – Zangdak Aug 16 '13 at 12:14
  • @davron12, you're a hero. You were absolutely right! With an `ObservableCollection` and correct `DataTemplates` in my `listView`, the UI is refresh when an item is added or removed and when item's properties change =) No need to extend `ObservableCollection` and do some weird code. Thanks a lot! – Zangdak Aug 19 '13 at 16:03
-1

Zangdak -- One way around that problem is each time the items change in your collection just set set the ItemSource of the ListView to null and then set it back to your collection. If you do this the UI will update and you will see your new items added to your collection.

Paige Ake