views:

110

answers:

1

Assume a standard Desktop Explorer style app:

  • menu at top
  • navigation tree on left
  • item view on right

and everything is neatly separated into Menu, Navigation and Item modules.

The menu has a generic "View" -> "Display mode" menu selection, that changes the current item view between:

  • "Icon view"
  • "List view"
  • "Detail view"

To catch the display type change, I currently publish and subscribe to a DisplayModeChanged event.

First problem:

This menu option should only display when an appropriate view is visible that has the display modes. What is the best way to control shared menu buttons so they only show if at least one relevant view is active?

Second Problem:

If a view comes into existence after the selection was made, how should it pickup the current display mode state from the menu?

+2  A: 

My first thought was that you could have solved the first problem by storing item view menu settings somewhere together with your item view. Then, on view activation you would ask your new view about its "custom menu actions" that it wants to show and one of them would be "Display mode" for item view. Other views would provide other menu actions and this one will not be shown.

But this solution contradicts with your second requirement, because you obviously want to have some global 'ItemViewDisplayModeSetting', and whenever it is changed you want all item views to be notified and change their display mode together.

So, let's just solve it right by applying the dependency injection principle. Do not look for things, ask for things. Your menu presenter (view model) requires some service that knows whether there are active item views or not. Your item view presenter requires a service that will provide initial display mode and will notify about its changes. We end up with this code:

interface IActiveViewsService : INotifyPropertyChanged
{
    bool HasActiveViewsSupportingDisplayMode { get; }
}

interface IDisplayModeService : INotifyPropertyChanged
{
    DisplayMode DisplayMode { get; }
}

//your item view presenter (view model)
class ItemViewModel
{
    public ItemViewModel(IDisplayModeService displayModeService)
    {
        //obtain initial setting
        //subscribe to property changes
    }
}

//your menu presenter (view model)
class MenuViewModel
{
    public MenuViewModel(IActiveViewsService activeViewsService)
    {
        //obtain initial setting
        //subscribe to property changes
    }
}

Then you need some way to edit your Diplay Mode from menu... you may combine that together into IDisplayModeService or you may create a new interface for that. The implementation will be a simple class that holds one DisplayMode instance and you inject one instance of that class into all your menus and item views. You'll also need to implement IActiveViewsService, this one will probably wrap IRegionManager from PRISM or whatever is your UI management mechanism... it will listen for region changes and react when a new item view is created or when there is no one left.

interface IDisplayModeEditor
{
    void ChangeDisplayMode(DisplayMode newMode);
}

//your final menu presenter (view model)
class MenuViewModel
{
    public MenuViewModel(IActiveViewsService activeViewsService, IDisplayModeEditor modeEditor)
    {
        //obtain initial setting
        //subscribe to property changes
    }
}

//your final menu presenter (view model)
class DisplayModeStorage : IDisplayModeService, IDisplayModeEditor
{
    private DisplayMode displayMode;

    public DisplayMode DisplayMode 
    { 
        get { return this.displayMode; } 

        //standard propertychange notification
        set
        {
             if(value == this.displayMode)
                return;

             this.displayMode = value;
             this.RaisePropertyChanged("DisplayMode");
        }
    }

    public void ChangeDisplayMode(DisplayMode newMode)
    {
         this.DisplayMode = newMode;
    }
}

HTH.

Yacoder
That seems almost exactly what I needed. Can you please clarify your comment on implementing IActiveViewsService? We are using IRegionManager from PRISM at the moment but I don't see any suitable events at the top level (only for specific regions).
Enough already
Well... ok there must be a place in code where you create views and insert them into the item view region... that class can expose some event on region change, for example... Maybe that's not in standard PRISM events, you're right, but think about it in terms of dependency injection again -- you need someone who knows about your regions...I remember, in our project we made a wrapper around PRISM region manager to be able to use enum values instead of strings as region identifiers... such a wrapper could also be a notifier like you need.
Yacoder
+50: There are still some complexities to your answer I am trying to simplify, but it was a very useful answer and gave me fresh ideas so well done and thanks :)
Enough already