Summary of what's new in this drop

RegionManager

In the prior build the RegionManagerService was responsible for adding views to regions, which was redundant with the IRegion.Add method. We've removed AddViewToRegion method and now directly call Add on the region itself, as can be seen in the example below.

WatchListViewPresenter.cs

        public void ShowViews()
        {
            ...
            IRegion region = regionManagerService.GetRegion("CollapsibleSideRegion");
            region.Add((UIElement)View);
        }

regions.jpg

Shell sliding region

sideregion.jpg
In the previous build, the watchListRegion was an embedded <Grid> element at a fixed location which would show whenever the vertical "WatchList" tab was hovered over. The previous functionality was buggy, and the Watchlist would not auto hide properly when it was unpinned. We've now changed he watchListRegion to be a collapsible region which slides out to display the watchlist.

Sliding region walkthrough
  1. Launch the application.
  2. Move your mouse over the "Watchlist" tab. The side region will appear.
  3. Push the pushpin to pin the region.

We've added a SlidingTemplate as a static resource which includes the functionality. The template uses triggers to handle showing the list when it is hovered over, and for auto-hiding. Triggers also handle the pinning of the region. This template is then applied to a TabControl that is registered as a "CollapsibleSideRegion".

Shell.xaml - Sliding Template definition

    <Window.Resources>
        <Image x:Key="PinCheckedContent" Source="images/pinIconDocked.gif" />
        <Image x:Key="PinUncheckedContent" Source="images/pinIconNonDocked.gif" />
        <ControlTemplate x:Key="SlidingTemplate" TargetType="{x:Type TabControl}">
        ...


Shell.xaml - CollapsibleRegion definition 
        ...
        <TabControl Name="CollapsibleRegion" Grid.Row="1" Grid.Column="1" prism:RegionManager.Region="CollapsibleSideRegion" 
        Template="{StaticResource SlidingTemplate}" ItemContainerStyle="{StaticResource HeaderStyle}"/>
    </Grid>
</Window>

Watchlist

AddToWatchList.jpg Watchlist.jpg


In this release we've wired up the Add to Watch List and Remove functionality. Essentially what the "Add to Watch List" button does, is allow a trader to add a Symbol to the watch list so they can keep track of it. Items can be added to the list whether it is visible or not.

Watch List walkthrough

Adding to the watch list
  1. Launch the application.
  2. Enter Fund0 into the textboxt next to "Add To Watchlist".
  3. Press the button.
  4. Move your mouse over the "Watch List" tab. Fund0 should be present.
Removing items from the watch list
  1. Press the pushpin for pinning the Watchlist.
  2. Right click on Fund0.
  3. Click the Remove. Fund0 should be removed.
The challenge we faced around the watch list was related to commanding. WPF provides built in commanding support which allows a human designer to wire up the UI to invoke functionality. The out-of-the-box method of implementing WPF commands is to use RoutedCommands. Routed commands can be handled by either the UIElement that has the focus or any of it's parent elements in the visual tree through a <CommandBinding>. The logic for handling the command resides in the code behind where the binding appears.

In a Composite WPF Application, the challenge is that the handler of the command will normally exist in a separate module than the invoker. This is the case for the AddToWatchList control which exists in Shell module while the WatchList module contains the handler. We've been looking into several different approaches for how to handle this kind of commanding, the first being a more explicit wiring, and the second being a more generic and framework-like. In this case we chose the explicit approach.

A visual of how this works can be seen below.

watchlistflow.jpg

The AddWatchControl invokes a static AddWatchCommand that resides in WatchListModuleCommands class. This command exposes a SetFiredCallback method which accepts a Fired callback (delegate) that is invoked whenever the command is executed.

AddWatchCommand.cs 

        private Fired firedCallback;

        public void SetFiredCallback(Fired callback)
        {
             ...
        }

        ...

        public void Execute(object parameter)
        {
            if (firedCallback != null)
            {
                firedCallback(parameter!=null ? parameter.ToString() : null);
            }
        }


We've created a WatchListService to handle the command execution. That service implements the Observer pattern and listens for when the command is executed.
WatchListService.cs

        public WatchListService(IMarketFeedService marketFeedService)
        {
            this.marketFeedService = marketFeedService;
            WatchItems = new ObservableCollection<string>();
            WatchListModuleCommands.AddWatchCommand.SetFiredCallback(OnFiredCallback);
        }


The WatchListService is also a Mediator between the command and the WatchListViewPresenter. Whenever the callback is fired, the Watchlist then adds a new watch item to its watch items' ObservableCollection.

WatchListService.cs
        private void OnFiredCallback(string tickerSymbol)
        {
            if (!String.IsNullOrEmpty(tickerSymbol))
            {
                string upperCasedTrimmedSymbol = tickerSymbol.ToUpperInvariant().Trim();
                if (!WatchItems.Contains(upperCasedTrimmedSymbol))
                {
                    if (marketFeedService.SymbolExists(upperCasedTrimmedSymbol))
                    {
                        WatchItems.Add(upperCasedTrimmedSymbol);
                    }
                }
            }
        }


WatchItems are exposed to the WatchListPresenter which retrieves it through the RetrieveWatchList method off of the WatchListService that was injected into it.

        public WatchListViewPresenter(IWatchListView view, IRegionManagerService regionManagerService,
            IWatchListService watchListService, IMarketFeedService marketFeedService)
        {
            View = view;

            this.regionManagerService = regionManagerService;
            this.marketFeedService = marketFeedService;
            this.watchList = watchListService.RetrieveWatchList();
            InitializeEvents();
        }


When the WatchListViewPresenter's ShowViews method is called, it populates calls PopulateWatchItemsList which populates a watchItems collection. This collection is the PresentationModel for the WatchListView. The The Presenter also subscribes to the CollectionChanged event so that it can receive notifications if any new items that are added by the WatchListService.

WatchListViewPresenter.cs
       public void ShowViews()
        {
            watchList.CollectionChanged += delegate { PopulateWatchItemsList(watchList); };
            PopulateWatchItemsList(watchList);
            IRegion region = regionManagerService.GetRegion("CollapsibleSideRegion");
            region.Add((UIElement)View);
        }

        private void PopulateWatchItemsList(ObservableCollection<string> watchItemsList)
        {
            ObservableCollection<WatchItem> watchItems = new ObservableCollection<WatchItem>();
            foreach (string tickerSymbol in watchItemsList)
            {
                 ...
            }
            View.SetWatchListItems(watchItems);
        }


Whever new items are added (or removed), the list is repopulated.

Using this approach facilitates wiring up a WPF command to a module in a loosely-coupled fashion. This just one of several ways for doing cross-module command handling. We will continue to refine and explore this method.

Your feedback is invited as always.

Last edited May 15, 2008 at 4:22 PM by mconverti, version 14

Comments

No comments yet.