質問

I was using .Net 4.0 and had a Dictionary in my code like the one below

 public Dictionary<DirectoryInfo, ObservableCollection<Media>> FoldersDictionary

I am using this dictionary of to display the folders from windows directory in a surface list box. DirectoryInfo will contain the directory information about folders e.g their names and Media class will contain the data inside these folders.

When I add or remove items from the ObservableCollection in my Dictionary, WPF threw this warning at me:

Collection of type 'System.Collections.Generic.Dictionary`2[[System.IO.DirectoryInfo, 
mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],
[System.Collections.ObjectModel.ObservableCollection`1[[Media, ViewModel, 
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], System, Version=4.0.0.0, 
Culture=neutral, PublicKeyToken=b77a5c561934e089]]' has been changed without raising
a CollectionChanged event.  Support for this is incomplete and inconsistent, 
and will be removed completely in a future version of WPF. Consider either (a) 
implementing INotifyCollectionChanged, or (b) avoiding changes to this type of 
collection`.

However I ignored this warning as my output was showing up alright. Yesterday I upgraded to .Net 4.5.1 on Windows 7 and ran this code. The warning was still the same but now every time I refresh my dictionary (meaning clearing it and then adding new stuff to ObservableCollection), the last ObservableCollection item is never visible on my UI. I debugged my code and found out that the Dictionary infact still has the last item added in it, its just that it is not being displayed on my WPF XAML UI. I wondered may be this behavior is due to the warning I see. I implemented the following CollectionChanged event to my code:

 private void OnUrlChanged(string newUrl)
        {
            if (string.IsNullOrEmpty(newUrl)) 
                return;

            var directoryInfo = new DirectoryInfo(newUrl);
            DirectoryInfo[] directoryInfos = directoryInfo.GetDirectories();

            if (directoryInfos.Any())
            {
                if (FoldersDictionary != null && FoldersDictionary.Any())
                {
                     FoldersDictionary.Clear();
                }

                FoldersDictionary = null;
                FoldersDictionary = new Dictionary<DirectoryInfo, ObservableCollection<Media>>();
                LoadFolders(FoldersDictionary, directoryInfos);
            }     
        }

 private void LoadFolders( Dictionary<DirectoryInfo, ObservableCollection<Media>> Folders, IEnumerable<DirectoryInfo> directoryInfos)
        {
            foreach (DirectoryInfo item in directoryInfos)
            {
                ObservableCollection<Media> medias = LoadMedias(item.FullName);

                //new code starts here
            medias.CollectionChanged+=new System.Collections.Specialized.NotifyCollectionChangedEventHandler(medias_CollectionChanged); 
                //new code ends here

                if (medias != null)
                {
                    Folders.Add(item, medias);
                }
            }
        }

//new code starts here
        void medias_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
             //do nothing
        }

//new code ends here

My Dictionary PropertyChanged is given as:

        public const string FoldersDictionaryPropertyName = "FoldersDictionary";

        private Dictionary<DirectoryInfo, ObservableCollection<Media>> _foldersDictionary;

        public Dictionary<DirectoryInfo, ObservableCollection<Media>> FoldersDictionary
        {
            get { return _foldersDictionary; }

            set
            {
                if (_foldersDictionary == value)
                {
                    return;
                }

                Dictionary<DirectoryInfo, ObservableCollection<Media>> oldValue = _foldersDictionary;
                _foldersDictionary = value;



                RaisePropertyChanged(FoldersDictionaryPropertyName);


            }
        }

This is the XAML I am using.

<Grid Height="180">
                    <s:SurfaceListBox x:Name="xFoldersListBox"
                                      Background="{x:Null}"
                                      ItemContainerStyle="{StaticResource                             SurfaceListBoxItemSquareStyle}" 
                                      ItemsSource="{Binding FoldersDictionary}"
                                      ItemTemplate="{StaticResource FolderDataTemplate}"
                                      SelectedValuePath="Value"
                                      Template="{StaticResource SurfaceListBoxHorizontalTemplate}" />
 </Grid>


<DataTemplate x:Key="FolderDataTemplate">
            <s:SurfaceToggleButton IsChecked="{Binding IsSelected,
                                                       Mode=TwoWay,
                                                       RelativeSource={RelativeSource   Mode=FindAncestor, AncestorType={x:Type s:SurfaceListBoxItem}}}" Style="{StaticResource ToggleButtonStyle}">
                <TextBlock Text="{Binding Path=Key.Name}"
                           TextAlignment="Center"
                           TextWrapping="Wrap" />
            </s:SurfaceToggleButton>
</DataTemplate>

However I am still getting the same warning as before and further more I cant see my last folder in my dictionary being displayed at all when I clear and repopulate my Dictionary. Any idea what may be causing this behaviour?

役に立ちましたか?

解決

The code is problematic when you recreate another instance of FoldersDictionary. If you just clear the Dictionary and re-populate it then the code works as expected, no warning either.

But I don't know WHY the last Directory is lost, I step into the code and found the key/value pairs are correct. Maybe Dictionary is not intended to be used like an ObservableCollection in data binding, as the warning suggests.

private void OnUrlChanged(string newUrl)
{
    if (string.IsNullOrEmpty(newUrl))
        return;

    //instantiate only once
    if (FoldersDictionary == null)
    {
        FoldersDictionary = new Dictionary<DirectoryInfo,   ObservableCollection<Media>>();
    }
    else if (FoldersDictionary.Any())
    {
        FoldersDictionary.Clear();
    }

    var directoryInfo = new DirectoryInfo(newUrl);
    DirectoryInfo[] directoryInfos = directoryInfo.GetDirectories();

    if (directoryInfos.Any())
    {                
        LoadFolders(FoldersDictionary, directoryInfos);
    }
}

他のヒント

As the warning suggested, Dictionary type doesn't implement INotifyCollectionChanged. You need to create custom Dictionary that implement the interface to make it work.

Or you can try using ObservableDictionary from this blog post. It is custom implementation of IDictionary<TKey,TValue> interface, combined with INotifyCollectionChanged and INotifyPropertyChanged interfaces.

You are binding FoldersDictionary to the UI element. In order for WPF to string together everything, FoldersDictionary should raise PropertyChanged event. As the warning suggests, implement INotifyPropertChanged interface and attach the event handler to this property.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top