Region.Remove Error Drop 10

Topics: Prism v4 - WPF 4
Oct 18, 2010 at 8:29 PM

WPF

I get the following error when removing a View from a Region with the following code:

 

 

Dim reg As IRegion = _regionManager.Regions(RegionNames .MainRegion)
Dim v As Object = reg.GetView(param.ScreenKey)
If v Is Nothing Then
Else
  reg.Remove(v)
End If

 

 

System.InvalidOperationException was unhandled
  Message=Collection Remove event must specify item position.
  Source=PresentationFramework
  StackTrace:
       at MS.Internal.Data.EnumerableCollectionView.ProcessCollectionChanged(NotifyCollectionChangedEventArgs args)
       at System.Windows.Data.CollectionView.OnCollectionChanged(Object sender, NotifyCollectionChangedEventArgs args)
       at System.Collections.Specialized.NotifyCollectionChangedEventHandler.Invoke(Object sender, NotifyCollectionChangedEventArgs e)
       at Microsoft.Practices.Prism.Regions.ViewsCollection.OnCollectionChanged(NotifyCollectionChangedEventArgs e) in c:\WorkingDir\PrismLibraryBuild\PrismLibrary\Desktop\Prism\Regions\ViewsCollection.cs:line 121
       at Microsoft.Practices.Prism.Regions.ViewsCollection.NotifyRemove(IList items) in c:\WorkingDir\PrismLibraryBuild\PrismLibrary\Desktop\Prism\Regions\ViewsCollection.Desktop.cs:line 40
       at Microsoft.Practices.Prism.Regions.ViewsCollection.SourceCollectionChanged(Object sender, NotifyCollectionChangedEventArgs e) in c:\WorkingDir\PrismLibraryBuild\PrismLibrary\Desktop\Prism\Regions\ViewsCollection.cs:line 266
       at System.Collections.ObjectModel.ObservableCollection`1.OnCollectionChanged(NotifyCollectionChangedEventArgs e)
       at System.Collections.ObjectModel.ObservableCollection`1.RemoveItem(Int32 index)
       at System.Collections.ObjectModel.Collection`1.Remove(T item)
       at Microsoft.Practices.Prism.Regions.Region.Remove(Object view) in c:\WorkingDir\PrismLibraryBuild\PrismLibrary\Desktop\Prism\Regions\Region.cs:line 287
       at Bestpass.AM.Desktop.ScreenConductor.CloseScreen(ScreenCloseEventArgs param) in C:\Development\BestpassEM\Bestpass.Desktop\Classes\ScreenConductor.vb:line 136
       at Microsoft.Practices.Prism.Events.EventSubscription`1.InvokeAction(Action`1 action, TPayload argument) in c:\WorkingDir\PrismLibraryBuild\PrismLibrary\Desktop\Prism\Events\EventSubscription.cs:line 126
       at Microsoft.Practices.Prism.Events.EventSubscription`1.<>c__DisplayClass2.<GetExecutionStrategy>b__0(Object[] arguments) in c:\WorkingDir\PrismLibraryBuild\PrismLibrary\Desktop\Prism\Events\EventSubscription.cs:line 109
       at Microsoft.Practices.Prism.Events.EventBase.InternalPublish(Object[] arguments) in c:\WorkingDir\PrismLibraryBuild\PrismLibrary\Desktop\Prism\Events\EventBase.cs:line 71
       at Microsoft.Practices.Prism.Events.CompositePresentationEvent`1.Publish(TPayload payload) in c:\WorkingDir\PrismLibraryBuild\PrismLibrary\Desktop\Prism\Events\CompositePresentationEvent.cs:line 167
       at Bestpass.UI.Infrastructure.ViewModelBase.ExecuteCloseCommand(Object param) in C:\Development\BestpassEM\WPFInfrastructure\Infrastructure\ViewModelBase.vb:line 130
       at Bestpass.AM.UI.AccountManagement.ViewModels.AccountMaintenanceViewModel.ExecuteCloseCommand(Object param) in C:\Development\BestpassEM\Bestpass.AM.Desktop.AccountManagement\ViewModel\CompanyAccount\AccountMaintenanceViewModel.vb:line 182
       at Bestpass.UI.Infrastructure.WPF.RelayCommand`1.Execute(Object parameter) in C:\Development\BestpassEM\WPFInfrastructure\Infrastructure\RelayCommand.vb:line 78
       at MS.Internal.Commands.CommandHelpers.CriticalExecuteCommandSource(ICommandSource commandSource, Boolean userInitiated)
       at System.Windows.Controls.Primitives.ButtonBase.OnClick()
       at System.Windows.Controls.Button.OnClick()
       at System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonUp(MouseButtonEventArgs e)
       at System.Windows.UIElement.OnMouseLeftButtonUpThunk(Object sender, MouseButtonEventArgs e)
       at System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget)
       at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
       at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
       at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
       at System.Windows.UIElement.ReRaiseEventAs(DependencyObject sender, RoutedEventArgs args, RoutedEvent newEvent)
       at System.Windows.UIElement.OnMouseUpThunk(Object sender, MouseButtonEventArgs e)
       at System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget)
       at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
       at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
       at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
       at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
       at System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args)
       at System.Windows.UIElement.RaiseEvent(RoutedEventArgs args, Boolean trusted)
       at System.Windows.Input.InputManager.ProcessStagingArea()
       at System.Windows.Input.InputManager.ProcessInput(InputEventArgs input)
       at System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport)
       at System.Windows.Interop.HwndMouseInputProvider.ReportInput(IntPtr hwnd, InputMode mode, Int32 timestamp, RawMouseActions actions, Int32 x, Int32 y, Int32 wheel)
       at System.Windows.Interop.HwndMouseInputProvider.FilterMessage(IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
       at System.Windows.Interop.HwndSource.InputFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
       at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
       at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
       at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
       at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
       at System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
       at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
       at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
       at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
       at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
       at System.Windows.Threading.Dispatcher.Run()
       at System.Windows.Application.RunDispatcher(Object ignore)
       at System.Windows.Application.RunInternal(Window window)
       at System.Windows.Application.Run(Window window)
       at System.Windows.Application.Run()
       at Bestpass.AM.Desktop.Application.Main() in C:\Development\BestpassEM\Bestpass.Desktop\obj\x86\Debug\Application.g.vb:line 72
       at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException:

Oct 19, 2010 at 1:23 PM
Edited Oct 19, 2010 at 1:50 PM

I forgot to add that this worked fine before upgrading from v2.2 to drop 10 of v4.0.  I also did a little more testing and it has to do with the control I am using as a region.  I am using the Telerik TabControl.  Can you point me in the direction of what possibly changed in the prism libs to change how it would interact with a 3rd party control?

Oct 19, 2010 at 2:42 PM

Hi,

I am glad that you solved this scenario. On the other hand, if you need to expose a UI control as a region (e.g. Telerik TabControl), you could create a Region Adapter. For more information on this topic you could take a  look at the following documentation section on MSDN:

Additionally, take into account that Prism provides region adapters out-of-the-box. From the MSDN documentation:

  • ContentControlRegionAdapter. This adapter adapts controls of type System.Windows.Controls.ContentControl and derived classes.
  • SelectorRegionAdapter. This adapter adapts controls derived from the class System.Windows.Controls.Primitives.Selector, such as the System.Windows.Controls.TabControl control.
  • ItemsControlRegionAdapter. This adapter adapts controls of type System.Windows.Controls.ItemsControl and derived classes.
  • The Silverlight version of the Composite Application Library contains a fourth region adapter named TabControlRegionAdapter. This is because the TabControl control in Silverlight 3 does not extend the Selector class and has a behavior that is different from its WPF counterpart.

I am not aware how the Telerik TabControl works. But I assume that it derives from Selector, so you will be able to use this control as a region (SelectorRegionAdapter). If it does not or you experience a non expected behavior, you will need to create a particular region adapter for this.

Hope this helps.

Fernando Antivero
http://blogs.southworks.net/fantivero

Oct 19, 2010 at 2:48 PM

Right, I know how to create a custom region adapter.  Its just that the control worked just fine with the SelectorRegionAdapter prior to upgrading from v2.2 to v4.0.  All I changed in my project were the Prism assemblies and something that had been working for weeks now broke.

Developer
Oct 19, 2010 at 4:14 PM

The CollectionView was reworked around Drop9 (perhaps between 9 and 10) to enable support of the RegionLifetimeBehavior.  It appears that in the remove notification, the item position is not specified.  Was this a Silverlight or WPF application you were building

Oct 19, 2010 at 4:24 PM

WPF

Oct 21, 2010 at 2:49 PM
Edited Oct 21, 2010 at 3:01 PM

I am having the exact same problem. Same error and mine also worked with the CAL before changing everything to Prism. Mine is also WPF. I am not using a 3rd party control though. I did get the remove to work with the code in http://blogs.southworks.net/matiasb/2009/07/02/how-to-hide-views-inside-composite-application-guidance-aka-prism-v2-regions/ after updating it to use Prism. I modified it to more closely match my situation, which is loading the catalog from a xaml file like this:

 

<Prism:ModuleCatalog xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
					 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
					 xmlns:sys="clr-namespace:System;assembly=mscorlib"
					 xmlns:Prism="clr-namespace:Microsoft.Practices.Prism.Modularity;assembly=Microsoft.Practices.Prism">
	<Prism:ModuleInfoGroup Ref="ViewsModule.dll"
						   InitializationMode="WhenAvailable">
		<Prism:ModuleInfo ModuleName="ViewsModule.ViewsModule"
						  ModuleType="ViewsModule.ViewsModule, ViewsModule, Version=1.0.0.0" />
	</Prism:ModuleInfoGroup>
</Prism:ModuleCatalog>

 

and it still worked. The one difference is that my project is using MVVM and rather then adding the regions like this:

 

public void Initialize()
        {
            regionManager.Regions["TabRegion"].Add(new View1(), "View1");
            regionManager.Regions["TabRegion"].Add(new View2(), "View2");
            regionManager.Regions["TabRegion"].Add(new View3(), "View3");
        }

 

 

I am using the container to resolve the view like this:
public override void Initialize()
        {
            this.regionManager.Regions[RegionConstants.DisplayRegion].Add(container.Resolve<GroupSummaryView>(), "GroupSummaryView");
        }
where the GroupSummaryView constructor is like this:
 public GroupSummaryView(GroupViewModel viewModel)
        {
            InitializeComponent();
            viewModel.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(GroupSummaryViewModel_PropertyChanged);
            this.Loaded += (s, e) => { this.DataContext = viewModel; viewModel.Initialize(); };
            
        }
Hopefully that gives some insight into what the problem could be.

 

Oct 21, 2010 at 8:11 PM

Update: I got my problem solved. Changed the xaml from 

<ItemsControl x:Name="DisplayRegionControl" regions:RegionManager.RegionName="DisplayRegion" />
to

<ContentControl x:Name="DisplayRegionControl" regions:RegionManager.RegionName="DisplayRegion" />
and everything removed correctly.

 

Oct 21, 2010 at 8:18 PM

So, if the ItemsControl throws the same error that means its not only occuring with a 3rd party control (I was using the Telerik Tab Control).

Oct 21, 2010 at 8:18 PM

Thanks to everyone on the thread for reporting the issue. We are looking into fixing this bug.   

 

Oct 21, 2010 at 8:21 PM

hnolan: Correct. I was injecting my own views on demand and trying to remove them, which worked before. Changing from ItemsControl to ContentControl fixed the problem. 

Oct 23, 2010 at 2:31 PM

Hi!

Our team experiencing the same issue and it is really a showstopper for us. We're using ItemsControl in WPF build and , unfortunately, due to some UI design reasons ContentControl is no solution for us.

When we can expect a fix?

Thanks.

Oct 23, 2010 at 7:08 PM

Last week, the whole p&p team was tied up hosting the patterns & practices Symposium and did not get much done outside of the Symposium.  Bob & I did look at this issue a little bit, and we have a repro case in the form of a unit test and a plan on how to fix it.  We plan on tackling this Monday.

Our next drop should include a fix, and I will talk to Bob and Karl about posting the required code changes here to unblock you all between the time we fix it and drop the code officially.

Please post any other issues you are facing, as we are in docs/bug-fix mode and will be wrapping the project up soon.

Thanks again for your help in posting the details (which helped us find the root cause of the issue quickly) and your patience working with pre-release software.

 

Oct 26, 2010 at 12:41 AM

I want to give those of you who ran into this issue a chance to try out the fix before the next drop. There are three files that are affected by the fix: ViewsCollection.cs, ViewsCollection.Desktop.cs, and ViewsCollection.Silverlight.cs which are all shown below, as well as the unit tests.

If you want to do a custom build to test this out, replace the files with the contents from below, rebuild the PrismLibrary solution, and run the UpdatePrismLibraries.bat file.  NOTE: The UpdatePrismLibraries.bat will overwrite the signed binaries we shipped with Prismv4 Drop10, so you may want to do a back up before you run this.

Let me know if you have any questions or run into problems after incorporating the fix,
Michael Puleio

 

 

// ViewsCollection.cs
//===================================================================================
// Microsoft patterns & practices
// Composite Application Guidance for Windows Presentation Foundation and Silverlight
//===================================================================================
// Copyright (c) Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===================================================================================
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===================================================================================
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;

namespace Microsoft.Practices.Prism.Regions
{
    /// <summary>
    /// Implementation of <see cref="IViewsCollection"/> that takes an <see cref="ObservableCollection{T}"/> of <see cref="ItemMetadata"/>
    /// and filters it to display an <see cref="INotifyCollectionChanged"/> collection of
    /// <see cref="object"/> elements (the items which the <see cref="ItemMetadata"/> wraps).
    /// </summary>
    public partial class ViewsCollection : IViewsCollection
    {
        private readonly ObservableCollection<ItemMetadata> subjectCollection;

        private readonly Dictionary<ItemMetadata, MonitorInfo> monitoredItems =
            new Dictionary<ItemMetadata, MonitorInfo>();

        private readonly Predicate<ItemMetadata> filter;
        private Comparison<object> sort;
        private List<object> filteredItems = new List<object>();

        /// <summary>
        /// Initializes a new instance of the <see cref="ViewsCollection"/> class.
        /// </summary>
        /// <param name="list">The list to wrap and filter.</param>
        /// <param name="filter">A predicate to filter the <paramref name="list"/> collection.</param>
        public ViewsCollection(ObservableCollection<ItemMetadata> list, Predicate<ItemMetadata> filter)
        {
            this.subjectCollection = list;
            this.filter = filter;
            this.MonitorAllMetadataItems();
            this.subjectCollection.CollectionChanged += this.SourceCollectionChanged;
            this.UpdateFilteredItemsList();
        }

        /// <summary>
        /// Occurs when the collection changes.
        /// </summary>
        public event NotifyCollectionChangedEventHandler CollectionChanged;

        /// <summary>
        /// Gets or sets the comparison used to sort the views.
        /// </summary>
        /// <value>The comparison to use.</value>
        public Comparison<object> SortComparison
        {
            get { return this.sort; }
            set
            {
                if (this.sort != value)
                {
                    this.sort = value;
                    this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
                }
            }
        }

        private IEnumerable<object> FilteredItems
        {
            get { return this.filteredItems; }
        }

        /// <summary>
        /// Determines whether the collection contains a specific value.
        /// </summary>
        /// <param name="value">The object to locate in the collection.</param>
        /// <returns><see langword="true" /> if <paramref name="value"/> is found in the collection; otherwise, <see langword="false" />.</returns>
        public bool Contains(object value)
        {
            return this.FilteredItems.Contains(value);
        }

        ///<summary>
        ///Returns an enumerator that iterates through the collection.
        ///</summary>
        ///<returns>
        ///A <see cref="T:System.Collections.Generic.IEnumerator`1" /> that can be used to iterate through the collection.
        ///</returns>
        public IEnumerator<object> GetEnumerator()
        {
            return
                this.FilteredItems.OrderBy<object, object>(o => o, new RegionItemComparer(this.SortComparison)).
                    GetEnumerator();
        }

        ///<summary>
        ///Returns an enumerator that iterates through a collection.
        ///</summary>
        ///<returns>
        ///An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
        ///</returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        /// <summary>
        /// Used to invoked the <see cref="CollectionChanged"/> event.
        /// </summary>
        /// <param name="e"></param>
        private void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            NotifyCollectionChangedEventHandler handler = this.CollectionChanged;
            if (handler != null) handler(this, e);
        }

        private void NotifyReset()
        {
            this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }

        /// <summary>
        /// Removes all monitoring of underlying MetadataItems and re-adds them.
        /// </summary>
        private void ResetAllMonitors()
        {
            this.RemoveAllMetadataMonitors();
            this.MonitorAllMetadataItems();
        }

        /// <summary>
        /// Adds all underlying MetadataItems to the list from the subjectCollection
        /// </summary>
        private void MonitorAllMetadataItems()
        {
            foreach (var item in this.subjectCollection)
            {
                this.AddMetadataMonitor(item, this.filter(item));
            }
        }

        /// <summary>
        /// Removes all monitored items from our monitoring list.
        /// </summary>
        private void RemoveAllMetadataMonitors()
        {
            foreach (var item in this.monitoredItems)
            {
                item.Key.MetadataChanged -= this.OnItemMetadataChanged;
            }

            this.monitoredItems.Clear();
        }

        /// <summary>
        /// Adds handler to monitor the MetadatItem and adds it to our monitoring list.
        /// </summary>
        /// <param name="itemMetadata"></param>
        /// <param name="isInList"></param>
        private void AddMetadataMonitor(ItemMetadata itemMetadata, bool isInList)
        {
            itemMetadata.MetadataChanged += this.OnItemMetadataChanged;
            this.monitoredItems.Add(
                itemMetadata,
                new MonitorInfo
                    {
                        IsInList = isInList
                    });
        }

        /// <summary>
        /// Unhooks from the MetadataItem change event and removes from our monitoring list.
        /// </summary>
        /// <param name="itemMetadata"></param>
        private void RemoveMetadataMonitor(ItemMetadata itemMetadata)
        {
            itemMetadata.MetadataChanged -= this.OnItemMetadataChanged;
            this.monitoredItems.Remove(itemMetadata);
        }

        /// <summary>
        /// Invoked when any of the underlying ItemMetadata items we're monitoring changes.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnItemMetadataChanged(object sender, EventArgs e)
        {
            ItemMetadata itemMetadata = (ItemMetadata) sender;

            // Our monitored item may have been removed during another event before
            // our OnItemMetadataChanged got called back, so it's not unexpected
            // that we may not have it in our list.
            MonitorInfo monitorInfo;
            bool foundInfo = this.monitoredItems.TryGetValue(itemMetadata, out monitorInfo);
            if (!foundInfo) return;

            if (this.filter(itemMetadata))
            {
                if (!monitorInfo.IsInList)
                {
                    // This passes our filter and wasn't marked
                    // as in our list so we can consider this
                    // an Add.
                    monitorInfo.IsInList = true;
                    this.UpdateFilteredItemsList();
                    NotifyAdd(itemMetadata.Item);
                }
            }
            else
            {
                // This doesn't fit our filter, we remove from our
                // tracking list, but should not remove any monitoring in
                // case it fits our filter in the future.
                monitorInfo.IsInList = false;
                this.RemoveFromFilteredList(itemMetadata.Item);
            }
        }

        /// <summary>
        /// The event handler due to changes in the underlying collection.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    this.UpdateFilteredItemsList();
                    foreach (ItemMetadata itemMetadata in e.NewItems)
                    {
                        bool isInFilter = this.filter(itemMetadata);
                        this.AddMetadataMonitor(itemMetadata, isInFilter);
                        if (isInFilter)
                        {
                            NotifyAdd(itemMetadata.Item);
                        }
                    }

                    // If we're sorting we can't predict how
                    // the collection has changed on an add so we 
                    // resort to a reset notification.
                    if (this.sort != null)
                    {
                        this.NotifyReset();
                    }

                    break;

                case NotifyCollectionChangedAction.Remove:
                    foreach (ItemMetadata itemMetadata in e.OldItems)
                    {
                        this.RemoveMetadataMonitor(itemMetadata);
                        if (this.filter(itemMetadata))
                        {
                            this.RemoveFromFilteredList(itemMetadata.Item);
                        }
                    }

                    break;

                default:
                    this.ResetAllMonitors();
                    this.UpdateFilteredItemsList();
                    this.NotifyReset();

                    break;
            }
        }

        private void NotifyAdd(object item)
        {
            int newIndex = this.filteredItems.IndexOf(item);
            this.NotifyAdd(new[] { item }, newIndex);
        }
        
        private void RemoveFromFilteredList(object item)
        {
            int index = this.filteredItems.IndexOf(item);
            this.UpdateFilteredItemsList();
            this.NotifyRemove(new[] { item }, index);
        }

        private void UpdateFilteredItemsList()
        {
            this.filteredItems = this.subjectCollection.Where(i => this.filter(i)).Select(i => i.Item).ToList();
        }
        
        private class MonitorInfo
        {
            public bool IsInList { get; set; }
        }

        private class RegionItemComparer : Comparer<object>
        {
            private readonly Comparison<object> comparer;

            public RegionItemComparer(Comparison<object> comparer)
            {
                this.comparer = comparer;
            }

            public override int Compare(object x, object y)
            {
                if (this.comparer == null)
                {
                    return 0;
                }

                return this.comparer(x, y);
            }
        }
    }
}

 

 

 

//ViewsCollection.Desktop.cs
//===================================================================================
// Microsoft patterns & practices
// Composite Application Guidance for Windows Presentation Foundation and Silverlight
//===================================================================================
// Copyright (c) Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===================================================================================
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===================================================================================
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;

namespace Microsoft.Practices.Prism.Regions
{
    public partial class ViewsCollection
    {
        private void NotifyAdd(IList items, int newStartingIndex)
        {
            if (items.Count > 0)
            {
                OnCollectionChanged(new NotifyCollectionChangedEventArgs(
                                            NotifyCollectionChangedAction.Add,
                                            items,
                                            newStartingIndex));
            }
        }

        private void NotifyRemove(IList items, int originalIndex)
        {
            if (items.Count > 0)
            {
                OnCollectionChanged(new NotifyCollectionChangedEventArgs(
                    NotifyCollectionChangedAction.Remove,
                    items, 
                    originalIndex));
            }
        }
    }
}

//ViewsCollection.Silverlight.cs
//===================================================================================
// Microsoft patterns & practices
// Composite Application Guidance for Windows Presentation Foundation and Silverlight
//===================================================================================
// Copyright (c) Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===================================================================================
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===================================================================================
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;

namespace Microsoft.Practices.Prism.Regions
{
    public partial class ViewsCollection
    {
        private void NotifyAdd(IList items, int newStartingIndex)
        {
            if (items.Count <= 0)
            {
                return;
            }

            foreach (var item in items)
            {
                this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(
                                             NotifyCollectionChangedAction.Add,
                                             item,
                                             newStartingIndex));
            }
        }

        private void NotifyRemove(IList items, int originalIndex)
        {
            if (items.Count <= 0)
            {
                return;
            }

            foreach (var item in items)
            {
                this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(
                                             NotifyCollectionChangedAction.Remove,
                                             item,
                                             originalIndex));
            }
        }
    }
}

Here are the updated unit tests as well:

//ViewsCollectionFixture.cs
//===================================================================================
// Microsoft patterns & practices
// Composite Application Guidance for Windows Presentation Foundation and Silverlight
//===================================================================================
// Copyright (c) Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===================================================================================
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===================================================================================
using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Windows.Data;
using Microsoft.Practices.Prism.Regions;
using Microsoft.Practices.Prism.TestSupport;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Microsoft.Practices.Prism.Tests.Regions
{
    [TestClass]
    public class ViewsCollectionFixture
    {
        [TestMethod]
        public void CanWrapCollectionCollection()
        {
            var originalCollection = new ObservableCollection<ItemMetadata>();
            IViewsCollection viewsCollection = new ViewsCollection(originalCollection, x => true);

            Assert.AreEqual(0, viewsCollection.Count());

            var item = new object();
            originalCollection.Add(new ItemMetadata(item));
            Assert.AreEqual(1, viewsCollection.Count());
            Assert.AreSame(item, viewsCollection.First());
        }

        [TestMethod]
        public void CanFilterCollection()
        {
            var originalCollection = new ObservableCollection<ItemMetadata>();
            IViewsCollection viewsCollection = new ViewsCollection(originalCollection, x => x.Name == "Possible");

            originalCollection.Add(new ItemMetadata(new object()));

            Assert.AreEqual(0, viewsCollection.Count());

            var item = new object();
            originalCollection.Add(new ItemMetadata(item) {Name = "Possible"});
            Assert.AreEqual(1, viewsCollection.Count());

            Assert.AreSame(item, viewsCollection.First());
        }

        [TestMethod]
        public void RaisesCollectionChangedWhenFilteredCollectionChanges()
        {
            var originalCollection = new ObservableCollection<ItemMetadata>();
            IViewsCollection viewsCollection = new ViewsCollection(originalCollection, x => x.IsActive);
            bool collectionChanged = false;
            viewsCollection.CollectionChanged += (s, e) => collectionChanged = true;

            originalCollection.Add(new ItemMetadata(new object()) {IsActive = true});

            Assert.IsTrue(collectionChanged);
        }

        [TestMethod]
        public void RaisesCollectionChangedWithAddAndRemoveWhenFilteredCollectionChanges()
        {
            var originalCollection = new ObservableCollection<ItemMetadata>();
            IViewsCollection viewsCollection = new ViewsCollection(originalCollection, x => x.IsActive);
            bool addedToCollection = false;
            bool removedFromCollection = false;
            viewsCollection.CollectionChanged += (s, e) =>
                                                     {
                                                         if (e.Action == NotifyCollectionChangedAction.Add)
                                                         {
                                                             addedToCollection = true;
                                                         }
                                                         else if (e.Action == NotifyCollectionChangedAction.Remove)
                                                         {
                                                             removedFromCollection = true;
                                                         }
                                                     };
            var filteredInObject = new ItemMetadata(new object()) {IsActive = true};

            originalCollection.Add(filteredInObject);

            Assert.IsTrue(addedToCollection);
            Assert.IsFalse(removedFromCollection);

            originalCollection.Remove(filteredInObject);

            Assert.IsTrue(removedFromCollection);
        }

        [TestMethod]
        public void DoesNotRaiseCollectionChangedWhenAddingOrRemovingFilteredOutObject()
        {
            var originalCollection = new ObservableCollection<ItemMetadata>();
            IViewsCollection viewsCollection = new ViewsCollection(originalCollection, x => x.IsActive);
            bool collectionChanged = false;
            viewsCollection.CollectionChanged += (s, e) => collectionChanged = true;
            var filteredOutObject = new ItemMetadata(new object()) {IsActive = false};

            originalCollection.Add(filteredOutObject);
            originalCollection.Remove(filteredOutObject);

            Assert.IsFalse(collectionChanged);
        }

        [TestMethod]
        public void CollectionChangedPassesWrappedItemInArgumentsWhenAdding()
        {
            var originalCollection = new ObservableCollection<ItemMetadata>();
            var filteredInObject = new ItemMetadata(new object());
            originalCollection.Add(filteredInObject);

            IViewsCollection viewsCollection = new ViewsCollection(originalCollection, x => true);
            IList oldItemsPassed = null;
            viewsCollection.CollectionChanged += (s, e) => { oldItemsPassed = e.OldItems; };
            originalCollection.Remove(filteredInObject);

            Assert.IsNotNull(oldItemsPassed);
            Assert.AreEqual(1, oldItemsPassed.Count);
            Assert.AreSame(filteredInObject.Item, oldItemsPassed[0]);
        }

        [TestMethod]
        public void CollectionChangedPassesWrappedItemInArgumentsWhenRemoving()
        {
            var originalCollection = new ObservableCollection<ItemMetadata>();
            IViewsCollection viewsCollection = new ViewsCollection(originalCollection, x => true);
            IList newItemsPassed = null;
            viewsCollection.CollectionChanged += (s, e) => { newItemsPassed = e.NewItems; };
            var filteredInObject = new ItemMetadata(new object());

            originalCollection.Add(filteredInObject);

            Assert.IsNotNull(newItemsPassed);
            Assert.AreEqual(1, newItemsPassed.Count);
            Assert.AreSame(filteredInObject.Item, newItemsPassed[0]);
        }

        [TestMethod]
        public void EnumeratesWrappedItems()
        {
            var originalCollection = new ObservableCollection<ItemMetadata>()
                                         {
                                             new ItemMetadata(new object()),
                                             new ItemMetadata(new object())
                                         };
            IViewsCollection viewsCollection = new ViewsCollection(originalCollection, x => true);
            Assert.AreEqual(2, viewsCollection.Count());

            Assert.AreSame(originalCollection[0].Item, viewsCollection.ElementAt(0));
            Assert.AreSame(originalCollection[1].Item, viewsCollection.ElementAt(1));
        }

        [TestMethod]
        public void ChangingMetadataOnItemAddsOrRemovesItFromTheFilteredCollection()
        {
            var originalCollection = new ObservableCollection<ItemMetadata>();
            IViewsCollection viewsCollection = new ViewsCollection(originalCollection, x => x.IsActive);
            bool addedToCollection = false;
            bool removedFromCollection = false;
            viewsCollection.CollectionChanged += (s, e) =>
                                                     {
                                                         if (e.Action == NotifyCollectionChangedAction.Add)
                                                         {
                                                             addedToCollection = true;
                                                         }
                                                         else if (e.Action == NotifyCollectionChangedAction.Remove)
                                                         {
                                                             removedFromCollection = true;
                                                         }
                                                     };

            originalCollection.Add(new ItemMetadata(new object()) {IsActive = true});
            Assert.IsTrue(addedToCollection);
            Assert.IsFalse(removedFromCollection);
            addedToCollection = false;

            originalCollection[0].IsActive = false;

            Assert.AreEqual(0, viewsCollection.Count());
            Assert.IsTrue(removedFromCollection);
            Assert.IsFalse(addedToCollection);
            Assert.AreEqual(0, viewsCollection.Count());
            addedToCollection = false;
            removedFromCollection = false;

            originalCollection[0].IsActive = true;

            Assert.AreEqual(1, viewsCollection.Count());
            Assert.IsTrue(addedToCollection);
            Assert.IsFalse(removedFromCollection);
        }

        [TestMethod]
        public void AddingToOriginalCollectionFiresAddCollectionChangeEvent()
        {
            var originalCollection = new ObservableCollection<ItemMetadata>();
            IViewsCollection viewsCollection = new ViewsCollection(originalCollection, (i) => true);

            var eventTracker = new CollectionChangedTracker(viewsCollection);

            originalCollection.Add(new ItemMetadata(new object()));

            Assert.IsTrue(eventTracker.ActionsFired.Contains(NotifyCollectionChangedAction.Add));
        }

        [TestMethod]
        public void AddingToOriginalCollectionFiresResetNotificationIfSortComparisonSet()
        {
            // Reset is fired to support the need to resort after updating the collection
            var originalCollection = new ObservableCollection<ItemMetadata>();
            var viewsCollection = new ViewsCollection(originalCollection, (i) => true);
            viewsCollection.SortComparison = (a, b) => { return 0; };

            var eventTracker = new CollectionChangedTracker(viewsCollection);

            originalCollection.Add(new ItemMetadata(new object()));

            Assert.IsTrue(eventTracker.ActionsFired.Contains(NotifyCollectionChangedAction.Add));
            Assert.AreEqual(
                1,
                eventTracker.ActionsFired.Count(a => a == NotifyCollectionChangedAction.Reset));
        }

        [TestMethod]
        public void OnAddNotifyCollectionChangedThenIndexProvided()
        {
            var originalCollection = new ObservableCollection<ItemMetadata>();
            IViewsCollection viewsCollection = new ViewsCollection(originalCollection, (i) => true);

            var eventTracker = new CollectionChangedTracker(viewsCollection);

            originalCollection.Add(new ItemMetadata("a"));

            var addEvent = eventTracker.NotifyEvents.Single(e => e.Action == NotifyCollectionChangedAction.Add);
            Assert.AreEqual(0, addEvent.NewStartingIndex);
        }

        [TestMethod]
        public void OnRemoveNotifyCollectionChangedThenIndexProvided()
        {
            var originalCollection = new ObservableCollection<ItemMetadata>();
            originalCollection.Add(new ItemMetadata("a"));
            originalCollection.Add(new ItemMetadata("b"));
            originalCollection.Add(new ItemMetadata("c"));
            IViewsCollection viewsCollection = new ViewsCollection(originalCollection, (i) => true);

            var eventTracker = new CollectionChangedTracker(viewsCollection);
            originalCollection.RemoveAt(1);

            var removeEvent = eventTracker.NotifyEvents.Single(e => e.Action == NotifyCollectionChangedAction.Remove);
            Assert.IsNotNull(removeEvent);
            Assert.AreEqual(1, removeEvent.OldStartingIndex);
        }

        [TestMethod]
        public void OnRemoveOfFilterMatchingItemThenViewCollectionRelativeIndexProvided()
        {
            var originalCollection = new ObservableCollection<ItemMetadata>();
            originalCollection.Add(new ItemMetadata("a"));
            originalCollection.Add(new ItemMetadata("b"));
            originalCollection.Add(new ItemMetadata("c"));
            IViewsCollection viewsCollection = new ViewsCollection(originalCollection, (i) => !"b".Equals(i.Item));

            var eventTracker = new CollectionChangedTracker(viewsCollection);
            originalCollection.RemoveAt(2);

            var removeEvent = eventTracker.NotifyEvents.Single(e => e.Action == NotifyCollectionChangedAction.Remove);
            Assert.IsNotNull(removeEvent);
            Assert.AreEqual(1, removeEvent.OldStartingIndex);
        }


        [TestMethod]
        public void RemovingFromFilteredCollectionDoesNotThrow()
        {
            var originalCollection = new ObservableCollection<ItemMetadata>();
            originalCollection.Add(new ItemMetadata("a"));
            originalCollection.Add(new ItemMetadata("b"));
            originalCollection.Add(new ItemMetadata("c"));
            IViewsCollection viewsCollection = new ViewsCollection(originalCollection, (i) => true);

            CollectionViewSource cvs = new CollectionViewSource {Source = viewsCollection};

            var view = cvs.View;
            try
            {
                originalCollection.RemoveAt(1);
            }
            catch (Exception ex)
            {
                Assert.Fail(ex.Message);
            }
        }
    }
}

 

 

Oct 26, 2010 at 1:18 PM

So far its working for me.  Thank you!!!

Oct 26, 2010 at 4:06 PM

Works for me as well. Thanks.

Oct 27, 2010 at 6:26 PM

That is good to hear.

Let us know if you run into any other issues.

Nov 3, 2010 at 6:45 PM

Works here as well! Thank you!

P.S. test fixture failes to compile (NotifyEvents not found)

Nov 5, 2010 at 1:40 AM

It´s working, Thanks!!!

Nov 8, 2010 at 8:57 PM
michaelpuleio wrote:

That is good to hear.

Let us know if you run into any other issues.

Issue: Region.Remove( view ) crashes when ViewSortHintAttribute applied to a view.

We have workaround for it, so it is not urgent.

Let me know if you need a repro.

Developer
Nov 8, 2010 at 10:45 PM

DenisZ,

Do you have a simple repro for this?

Thanks,

-b

Nov 9, 2010 at 8:34 AM
brumfb wrote:

DenisZ,

Do you have a simple repro for this?

Thanks,

-b

 I'll try to prepare the repro today evening.

Denis.

Developer
Nov 9, 2010 at 12:22 PM
Thanks. With the code in our repo that includes the fix posted above, I can't repro this error in a simple test app I put together.
Nov 9, 2010 at 10:30 PM

Ok! I have a small repro (15K ZIP). How can I attach it here?

Developer
Nov 9, 2010 at 10:42 PM

DenisZ,

Great.  Can you either open an issue to which you can attache the repro or if you have a skydrive account, put it on a public folder where I can grab it.

-b

Nov 9, 2010 at 11:03 PM
Here it is http://cid-763513c3edb31492.skydrive.live.com/redir.aspx?resid=763513C3EDB31492!120
Developer
Nov 10, 2010 at 12:00 AM

Got it, thanks! 

So I used your project to repro it on Drop 10 and Drop 10 with the above modifications.  Then I updated the references to the prism bits we are planning to release today/tomorrow and it appears to work correctly then.  So I believe we have this taken care of.  Thanks for your effort in putting the repro together.

-b