Martyn Ball
Martyn Ball

Reputation: 4885

Adding to a ObservableCollection<string>

Edit: Changed List to ObservableCollection, got the same issue

I have got a global object which holds values needed for the duration of the application runtime. For some reason the ListView isn't being populated, not too sure if I have coded something wrong, or if the ListView simply isn't being updated for some reason.

Here is the class:

App.xaml.cs

/// <summary>
/// Global values for use during application runtime
/// </summary>
public class runtimeObject : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string name)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(name));
        }

    }
    private ObservableCollection<string> _hashList;
    public ObservableCollection<string> hashList
    {
        get { return _hashList; }
        set
        {
            if (value = "true")
            {
                _hashList.Clear();
            }
            else
            {
                _hashList.Add(value);
                OnPropertyChanged("hashList");
            }
        }
    }
}

I have created a command to populate this List so I can test it's binding. Here is the command:

Commands.cs

/// <summary>
/// Command: Test
/// </summary>
public static RoutedUICommand Test
{
    get { return _Test; }
} 
public static void Test_Executed(object sender,
            ExecutedRoutedEventArgs e)
{
    var runtime = (runtimeObject)Application.Current.TryFindResource("runtimeVariables");
    runtime.hashList = "ONE";
    runtime.hashList = "ONE";
    runtime.hashList = "ONE";
    runtime.hashList = "ONE";
    runtime.hashList = "ONE";
}
public static void Test_CanExecute(object sender,
                    CanExecuteRoutedEventArgs e)
{
    e.CanExecute = true;
}

Here is my ListView with the binding, the way I have binded it works for a text box on another property, I have defined the static resource like this <local:runtimeObject x:Key="runtimeVariables" /> within App.xaml MainWindow.xaml

<ListView Height="150" Width="400" ItemsSource="{Binding Source={StaticResource runtimeVariables},Path=hashList}"/>

Edit:

why this?

  runtime.hashList = new ObservableCollection<string> { "one" }; shouldn't it be:

 runtime.hashList.Add("one"); ?

That's what I though, however if I change runtime.hashList = new ObservableCollection<string> { "five" }; to this runtime.hashList.Add("one");

How then do I handle that in the class property?

else
{
    _hashList.Add(value);
    OnPropertyChanged("hashList");
}

I get this error:

Argument 1: cannot convert "System.Collections.ObjectModel.ObservableCollection" to "string"

Edit 2: I want to be able to send a string to my class property, so that I can simply either add a new value to my List, or clear it, but it needs to return the List when requested.

But I can't do this can I? As in order to return the ObservableCollection<string> I need to set it like so:

public ObservableCollection<string> hashList { }

but this doesn't allow me to send just string data, as it can't convert string to System.Collections...

If this makes sense.

Upvotes: 0

Views: 7329

Answers (4)

Tony Vitabile
Tony Vitabile

Reputation: 8594

Your problem is here:

var runtime = (runtimeObject)Application.Current.TryFindResource("runtimeVariables");
runtime.hashList = new ObservableCollection<string> { "one" };
runtime.hashList = new ObservableCollection<string> { "two" };
runtime.hashList = new ObservableCollection<string> { "three" };
runtime.hashList = new ObservableCollection<string> { "four" };
runtime.hashList = new ObservableCollection<string> { "five" };
runtime.hashList = new ObservableCollection<string> { "six" };

Each time, you're creating a new list. You want to assign the ObservableCollection to the runtime.hashList property once, then add each string to the collection:

var runtime = (runtimeObject)Application.Current.TryFindResource("runtimeVariables");
runtime.hashList.Add( "one" );
runtime.hashList.Add( "two" );
runtime.hashList.Add( "three" );
runtime.hashList.Add( "four" );
runtime.hashList.Add( "five" );
runtime.hashList.Add( "six" );

The ObservableCollection class implements another interface called INotifyCollectionChanged which follows a pattern similar to INotifyPropertyChanged, except that it raises an OnCollectionChanged event whenever the contents of the collection are changed. WPF listens for changes to the collection on that event and updates the display appropriately.

Upvotes: 1

Jason Boyd
Jason Boyd

Reputation: 7029

You don't need INotifyPropertyChanged in this case to have the ListView update itself. That event is used when the entire object is replaced, i.e. you replace the collection with an entirely different collection. What you are really interested in doing is updating the ListView when items are added to or removed from the collection. That is handled with an entirely different event: INotifyCollectionChanged. If you bind ItemsSource to a collection that implements that event then the ListView will automatically update itself when the collection changes. ObservableCollection implements that event so that is the collection you want to use.

I would replace your runtimeObject with this:

public class runtimeObject : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string name)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(name));
        }

    }

    private readonly ObservableCollection<string> _hashList = new ObservableCollection<string>();
    public ObservableCollection<string> hashList
    {
        get { return _hashList; }
    }
}

And then change yourTest-Executed method to this:

public static void Test_Executed(object sender, ExecutedRoutedEventArgs e)
{
    var runtime = (runtimeObject)Application.Current.TryFindResource("runtimeVariables");
    runtime.hashList.Add("one");
    runtime.hashList.Add("two");
    runtime.hashList.Add("three");
    runtime.hashList.Add("four");
    runtime.hashList.Add("five");
}

Upvotes: 1

alek kowalczyk
alek kowalczyk

Reputation: 4936

The ListView has no idea that there were new values added to your list, because List<T> doesn't implement the INotifyPropertyChanged interface. You have following options:

  • You can change the List<T> class to ObservableCollection<T> class.
  • you can make your own list class which implements INotifyPropertyChanged
  • you can after populating the list fire OnPropertyChanged on your object
  • you can just fill the list with values before your ListView instantiates.

EDIT:

why this?

      runtime.hashList = new ObservableCollection<string> { "one" };

shouldn't it be:

     runtime.hashList.Add("one");

?

Upvotes: 1

GazTheDestroyer
GazTheDestroyer

Reputation: 21241

Your are raising PropertyChanged for hashList, but the hashList instance has NOT changed, only the CONTENT of hashList has changed. WPF has optimisations built in that can prevent any updates occuring if the object hasn't actually changed.

If you want WPF to respond to changes to the content of the list, you need to fire CollectionChanged instead, and the simplest way to do that is to use ObservableCollection instead of List.

Upvotes: 2

Related Questions