Need "hello world" example of SyncActiveState

Topics: Prism v4 - WPF 4
Sep 26, 2013 at 6:43 AM
I am having trouble getting SyncActiveState to work. The Prism 4.1 readme states simply:

"To support scenarios where a parent view contains scoped regions, the regions and views within that scope may need to be notified when the parent view becomes active. To handle this the SyncActiveStateAttribute can be applied to a View or ViewModel to ensure notification of the views (or view models) within the scoped regions."

[SyncActiveState]

public class MyViewModel : NotificationObject, IActiveAware

{
   (...)
}

However, I am having trouble getting this to work. I don't think my problem is the known bug discussed elsewhere, namely, that the parent view must be active when the child view is added to its region. I believe my problem is in defining the correct region scope. Does anyone have a "hello world" complete example on how to use SyncActiveState? This would be a big help to me. Thanks to all.
Sep 26, 2013 at 6:55 PM
Hi richardfen,

I created a sample solution portraying this. You can find it in the following link in my SkyDrive account:

The SyncActiveStateAttributeSample sample is structured with a MainRegion with 2 tabs (which are views injected in the main region) and 2 buttons. Each tab contains a SubRegionView which defines a scoped region where another child view is added. Of those child views, the SubView defines the SyncActiveState attribute, while the SubNotSyncView doesn´t.
Finally, you can see how the SyncActiveState attribute works using the aforementioned buttons. SubView’s IsActive property will update when its parent’s IsActive property updates, unlike SubNotSyncView from which its IsActive property remain unchanged.

There is another available sample regarding the workaround for the issue you mentioned of adding sub views to a deactivated parent view. This workaround is explained in the following blog:

I hope this helps,
Gabriel Ostrowsky
http://blogs.southworks.net/gostrowsky
Sep 27, 2013 at 2:47 AM

Thanks for your prompt reply. This is what I was looking for. Just a couple of questions:

(1 (1) When I comment out the code below I would expect to see the blue “Not in Sync” view:

public void Initialize()
{
IRegion mainRegion = this.regionManager.Regions["MainRegion"];

FrameworkElement tabView1 = ServiceLocator.Current.GetInstance<Views.ScopedRegionView>();

IRegionManager scopedManager1 = mainRegion.Add(tabView1, null, true);

FrameworkElement tabView2 = ServiceLocator.Current.GetInstance<Views.ScopedRegionView>();

IRegionManager scopedManager2 = mainRegion.Add(tabView2, null, true);

// FrameworkElement subView = ServiceLocator.Current.GetInstance();

// scopedManager1.Regions["SubRegion"].Add(subView);

FrameworkElement subNotSyncView = ServiceLocator.Current.GetInstance<Views.SubNotSyncView>();

scopedManager2.Regions["SubRegion"].Add(subNotSyncView);
}

However, the view is blank (just a black border).

( (2) I wanted to test the action whereby I would change the parent IsActive and see it reflected in the child that used [SyncActiveState]. Therefore, I modified CheckViewsState() to simply set the IsActive flag on the view model to false:

public void CheckViewsState()

{

foreach (object element in this.regionManager.Regions["MainRegion"].Views)

{

FrameworkElement view = element as FrameworkElement;

IActiveAware viewModel = view.DataContext as IActiveAware;

viewModel.IsActive = false;

}

}

I expected to hit a breakpoint in SubView.xaml.cs on the “set” accessor:

private bool _isActive;

public bool IsActive

{

get

{

return _isActive;

}

set

{

_isActive = value;

}

}

However, I did not hit the breakpoint.

Could you tell me what I am missing on these two points? Thanks again for your help.

Sep 27, 2013 at 4:11 PM
Hi richardfen,

I believe you have got confused with the code of adding the views to the Regions and ScopedRegions.
The HelloWorldModule’s initialized() method first add 2 Views to the MainRegion, where each one is defined in a different tab. Then, these 2 ScopeRegionView views define a scope region manager so both views would have one scoped region manager where the SubRegion would be defined. Finally, SubView and SubNotSyncView are added into the SubRegion region, but on different scope region managers:

  • SubView is added on “_scopedManager1_” which is defined in “_tabView1_”, and
  • SubNotSyncView is added on “_scopedManager2_” which is defined in “_tabView2_”.
Regarding your first question, the blue “_Not in Sync_” view wouldn’t appear on the first tab because you are adding it to the SubRegion contained in the scoped region manager “_scopedmanager2_”. A blank view on the first tab is expected as you are not adding any view to the SubRegion contained in “_scopedManager1_”. And if you move to the second tab window, you may actually see the blue “_Not in Sync_” view.

On regards to the second question, the SyncAtiveState attribute is validated on the RegionActiveAwareBehavior behavior when the region’s ActiveViews collection changes. Based on my understanding, the RegionActiveAwareBehavior subscribes to the CollectionChanged event of the ActiveViews collection of the corresponding Region. When the aforementioned collection is changed, the behavior changes the state of the views that are added or removed from the collection. This means that this behavior only executes its logic when a view is activated or deactivated.
Through the inclusion of the SyncActiveState attribute, during the state change, this behavior now also checks for any scoped views with the aforementioned attribute and set its state too. You can see this on the following RegionActiveAwareBehavior handler method:
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    if (e.Action == NotifyCollectionChangedAction.Add)
    {
        foreach (object item in e.NewItems)
        {
             Action<IActiveAware> invocation = activeAware => activeAware.IsActive = true;

             InvokeOnActiveAwareElement(item, invocation);
             InvokeOnSynchronizedActiveAwareChildren(item, invocation);
        }
    }
    else if (e.Action == NotifyCollectionChangedAction.Remove)
    {
        foreach (object item in e.OldItems)
        {
             Action<IActiveAware> invocation = activeAware => activeAware.IsActive = false;

             InvokeOnActiveAwareElement(item, invocation);
             InvokeOnSynchronizedActiveAwareChildren(item, invocation);
        }
    }

    // May need to handle other action values (reset, replace). Currently the ViewsCollection class does not raise CollectionChanged with these values.
}
Therefore, you could try to deactivate() or activate() the parent view in order to hit the breakpoint on the SubView setter as shown in the following code, or you could simply move from one tab to another (changing tabs make the views to get activated and deactivated):
public void CheckViewsState()
{   
    foreach (object element in this.regionManager.Regions["MainRegion"].Views)
    {
        FrameworkElement view = element as FrameworkElement;
        regionManager.Regions["MainRegion"].Deactivate(view);
    }
}
I hope this helped you.
Gabriel Ostrowsky
http://blogs.southworks.net/gostrowsky
Sep 28, 2013 at 9:14 AM
I tried all your suggestions and they work. Thanks very much for so much attention to this problem. This has given my understanding of Prism a quantum leap.

I added some of my own comments (from a neophyte) as shown below:


public void Initialize()
    {
        // we have here a main region into which we put a tab control. This is from Shell.xaml.  Then we add individual tabs
        // to this region.  Each tab contains a region, specifically "SubRegion".  Therefore, we can't add them to the main region
        // because the region manager will have nested sub regions using the same string name.

        IRegion mainRegion = this.regionManager.Regions["MainRegion"];

        FrameworkElement tabView1 = ServiceLocator.Current.GetInstance<Views.ScopedRegionView>();
        IRegionManager scopedManager1 = mainRegion.Add(tabView1, null, true);

        FrameworkElement tabView2 = ServiceLocator.Current.GetInstance<Views.ScopedRegionView>();
        IRegionManager scopedManager2 = mainRegion.Add(tabView2, null, true);

        // If we delete the next two lines, we do not see the blue screen and white text "subview does not synch 
        // with parent view screen" because tabView1 is selected by default.  We would have to select the 
        // tabView2 to see this screen.  Alternatively, we could change scopedManager2 below to scopedManager1 
        // and we would see the blue screen because // scoped manager 1 is the region that is now inside 
        // tabView1 which is selected.  
        FrameworkElement subView = ServiceLocator.Current.GetInstance<Views.SubView>();
        scopedManager1.Regions["SubRegion"].Add(subView);

        FrameworkElement subNotSyncView = ServiceLocator.Current.GetInstance<Views.SubNotSyncView>();
        scopedManager2.Regions["SubRegion"].Add(subNotSyncView);

We can close this incident now. Thanks again.