The StockViewer example is a simple and yet powerful web application that shows features that are quite difficult to implement in a traditional HTML/CSS based system implemented in Wisej using familiar and intuitive structures.
The StockViewer application simulates a central service (StockManager) that collects information about a list of stocks at regular intervals by connecting (also simulated) to an external data provider (MarketService). When the values are retrieved it broadcasts an event. If an error occurs it also broadcasts an event.
Multiple listeners, corresponding to multiple clients or sessions, can subscribe to the events fired by the StockManager and update the view accordingly. Updates are immediate and unattended, pushed by the server, without requiring a browser refresh or timer.
Users can enable or disable receiving updates for a specific stock symbol.
The StockManager service may randomly throw an exception to simulate an error when connecting to the third party service. Depending on when the exception is simulated, the StockViewer may ask the user whether he wishes to continue receiving the update or just terminate.
Simple and yet really difficult to implement using HTML/CSS systems, especially because of these features:
Unattended push updates
Broadcast server-side events
Modal user interaction while loading
Dynamic interactive view
Localization using the designer and ResX Manager
Animations on multiple controls.
When you run the application you will notice two animations:
Blinking of the stock value that has been updated
Smooth motion when changing the layout
In this case, the blink animation has been applied to the DataRepeater template panel without an event. Therefore the animation will run only if invoked by code.
Since the Animation extender exposes a complex property with child fields it is not replicated by the DataRepeater. With a simple handler for the ItemCloned event we can add the blink animation to the repeated items:
When the text of the value label is changed by the data binding layer, we handle the TextChanged property and run the animation.
A second way to add simple animations is to use the Stylesheet extender to add a transition style to the elements.
For the MainView, we simply added a general transition to all div elements:
When Wisej moves the controls into their new position because the responsive property for a client profile kicks in, the element will move to the target location in 250 milliseconds.
The application is organized around a straightforward Model View Controller structure.
MarketService.cs is a static class that simulates a service to retrieve a list of stock symbols and to retrieve their latest value. It exposes two static methods:
Entry[] GetStockSymbols()
Quote[] GetQuotes()
Each method blocks the thread to simulate a long running operation and randomly throws an exception.
StockManager.cs is the controller. It is also a static class shared by all sessions. It starts a single task when it's loaded and periodically uses MarketService to retrieve stock values. It is also in charge of updating the model when a stock quote changes.
It exposes two static methods:
BindingList<Stock> GetWatchableStocks()
bool UpdateQuote(Stock stock)
And two static events:
Update
Error
The Stock class is a bindable model class (implements INotifyPropertyChanged) containing all the information used by the view (StockPanel) to render the UI.
StockPanel.cs is the view. An instance of the StockPanel user control is created and anchored inside the MainPage. The code in StockPanel contains a DataRepeater panel template that is bound to the model.
StockPanel subscribes to the Update and Error events broadcasted by the StockManager class and when it receives an Update notification it asks the controller to update the model. Any update to the model is automatically reflected in the UI through the data binding infrastructure.
When the StockPanel detects that a stock value has changed, it fired a StockValueChanged event. The only listener to this event is the MainView.
MainView.cs is the main page of the application. It contains a header and an instance of the StockPanel anchored to the top and bottom edge. It listens to the StockValueChanged event to play a sound and update the LastUpdate label.
The application contains one simple main page and one user panel.
The MainView is a simple page with an image (PictureBox) anchored only at the top to stay centered, two labels, also anchored only at the Top, and a StockPanel anchored at the top and at the bottom in order to resize vertically to fill the page.
The StockPanel view contains a DataRepeater control. it repeats the template panel and all its children vertically (or horizontally) to display all the records in the data source.
Each child control can be bound to a field in the data source.
Once the control is designed, all the child controls in the template are wired to the data source, the system takes care of the rest. It's enough to run a loop like the one below to update all the items in the panel:
Notice that the label next to the "Last Update" label says no data. We can use the Advanced Binding configuration to configure the binding of the last update label to display "no data" when the incoming value is null.
Wisej can add animations (even multiple animations) to any control or container simply by dropping one or more instances of the Animation extender to a container. All the child controls will show a new Animation property in the designer.
However, there is a trick in the MainView: Responsive Properties! The view is arranged differently when running in a Phone or in a Phone in Landscape mode.
In the code snippet above you'll notice the check on the m.Watching property. That property is bound to the switch control in the Data Repeater. When the user switches it on or off, it automatically updates the Watching property in the data model.
There are several aspects of the StockViewer tutorial application that are quite interesting to review:
Background Updates
Broadcasting Messages
Modal User Interaction
DataBinding UI updates
In this application we have a static controller class StockManager that runs in the background in a single dedicated thread and uses the MarketService provider to query stock values.
The class is static (the code cannot create any instance of this class) and its functionality is shared by all users. Since the class is static we cannot use the constructor to start our background task, but we can use the static constructor, invoked the first time a user code references this class, to start our single shared task:
StockPanel listens to the Update event and updates the browser through a push update when any value changes.
The magic is done by the Application.Update(this, ...) call. When the code block is done, the system will push any update to the browser in real time.
The method Listener() runs on its own thread. It's quite a simple method, in fact it's just a loop that periodically retrieves the stock quotes from the MarketService provider and broadcasts the Update and Error events.
We say broadcast instead of fire the events because they are static and since all events in .NET are multicast delegates (multiple listeners can attach to the same event). In our case, all users listen to the same single event fired by the StockManager class.
The MarketService simulator may throw an exception when the StockManager controller asks for the stock symbols to watch:
Since StockPanel uses StockManager.GetWatchableStocks() when it's displayed by handling the Appear event, if we are unable to get the list of stock symbols to watch we only have two options: try again or quit.
The cool and difficult part is the fact that this is a server-side process because the code has to run on the server to use a service that is not public and is not available to the whole world through a simple ajax call.
While running on the server, the code is able to ask the user on the client whether to continue or not and keep going within the same loop on the server! The result is a total server/client seamless integration that can be achieved with 1 line of code.
We have seen that the StockViewer application needs to dynamically add panels to the DataRepeater to display the watchable stock symbols and to update the name, value and last update date for each stock being watched.
It's all done without any code. All the StockPanel does is to ask the StockManager to update the model:
The method StockManager.UpdateQuote() doesn't know anything about the view. It simply sets the new value to the stock model Value property. If the value is different, the Stock model fires the PropertyChanged event.
The data binding system handles the PropertyChanged event and updates all the bound properties causing the UI to get immediately refreshed.
The application is localized in German, Italian, French and Spanish. All labels have been translated automatically using Azure Cognitive Services trough the excellent ResXManager Visual Studio extension.
Localized resources can be edited and inspected at design time simply by selecting the language in the Language property.
Wisej automatically detects the browser's language and loads the appropriate resources when the view is created. An application can force a specific language before creating a view simply by calling:
In alternative, you can load a particular language by adding "?lang=de-DE" to the URL.