Thursday Jun 24, 2010 at 04:12 PM | Posted by: | Category: LINQ | WPF

Essential Chart Silverlight supports binding any IEnumerable source. This blog shows a sample HiLo chart bound to stocks.

We have a Stock model object which contains Current Date-Time and the Highest, Lowest, Open and Close Price of each day. The Stock updates class can contain a collection of Stock objects, which are created in XAML by using the following code snippet.

 

    <UserControl.Resources>
        <local:StockUpdates x:Key="data" />
    </UserControl.Resources>

 

Now, we can bind the stock data to the chart by using the ChartSeries control. The ChartSeries control contains the DataSource, BindingPathX, and BindingPathsY properties to initialize the data for populating the chart. The observable collection of the object is bound with the DataSource property, and the BindingPathX property is used to bind the property name, whose value is going to bind on the X direction. The BindingPathsY property is used to bind the list of property names, whose values are going to bind on the Y direction.

Some chart types require multiple Y values to render a chart. In this example, we use a High-Low-Open-Close chart type for the second series in the chart area. This chart type requires four Y direction values such as the high, low, open, and close price of each day. So the BindingPathsY property is used to initialize the list of property names as a string. The following code snippet shows how to bind data to the ChartSeries control.

 

<syncfusion:ChartSeries Type="HiLoOpenClose" DataSource="{StaticResource data}" StrokeThickness="2" BindingPathX="CurrentTime" BindingPathsY="Low, High, Open, Close" Interior="Blue" Label="Stock Price" />

 

The first and last series denote the line curve to show the high and low price of each day in green and red, respectively. I have used the Adornment feature of the chart control to show the highest and lowest price values of each day.

The following XAML code snippet illustrates binding an observable object collection to the Syncfusion Essential Chart Silverlight control. In the following code, I have created an instance for a list of stock values in the user control resources attribute.

 

        <syncfusion:Chart x:Name="chart" ChartVisualStyle="GrayScreen" Margin="5">
            <syncfusion:ChartArea>
                <syncfusion:ChartArea.Header>
                    <TextBlock Text="Date-wise Stock Review" FontWeight="Bold" FontSize="15" FontStyle="Italic" />
                </syncfusion:ChartArea.Header>
                
                <syncfusion:ChartArea.Legends>
                    <syncfusion:ChartLegend DockPosition="Top" />
                </syncfusion:ChartArea.Legends>
                
                <syncfusion:ChartArea.PrimaryAxis>
                    <syncfusion:ChartAxis ValueType="DateTime" RangeCalculationMode="AdjustAcrossChartTypes" LabelFontSize="13" HidePartialLabel="True" LabelDateTimeFormat="dd-MM-yyy" IntersectAction="MultipleRows">
                        <syncfusion:ChartAxis.Header>
                            <TextBlock Text="Stock Date" FontWeight="Bold" />
                        </syncfusion:ChartAxis.Header>
                    </syncfusion:ChartAxis>
                </syncfusion:ChartArea.PrimaryAxis>
                <syncfusion:ChartArea.SecondaryAxis>
                    <syncfusion:ChartAxis IsAutoSetRange="False" Range="0,100" Interval="20"  LabelFontSize="13">
                        <syncfusion:ChartAxis.Header>
                            <TextBlock Text="Price" FontWeight="Bold" />
                        </syncfusion:ChartAxis.Header>
                    </syncfusion:ChartAxis>
                </syncfusion:ChartArea.SecondaryAxis>
                
                <syncfusion:ChartSeries Type="FastLine" DataSource="{StaticResource data}" StrokeThickness="1" BindingPathX="CurrentTime" BindingPathsY="High" Interior="Green" Label="High Price" >
                    <syncfusion:ChartSeries.AdornmentsInfo>
                        <syncfusion:ChartAdornmentInfo Visible="True" SegmentLabelFontSize="13" SegmentLabelFontWeight="Bold" VerticalAlignment="Top" Symbol="Cross" SymbolWidth="10" SymbolInterior="Green" SymbolHeight="10" />
                    </syncfusion:ChartSeries.AdornmentsInfo>
                </syncfusion:ChartSeries>
                
                <syncfusion:ChartSeries Type="HiLoOpenClose" DataSource="{StaticResource data}" StrokeThickness="2" BindingPathX="CurrentTime" BindingPathsY="Low, High, Open, Close" Interior="Blue" Label="Stock Price" />
                
                <syncfusion:ChartSeries Type="FastLine" DataSource="{StaticResource data}" StrokeThickness="1" BindingPathX="CurrentTime" BindingPathsY="Low" Interior="Red" Label="Low Price" >
                    <syncfusion:ChartSeries.AdornmentsInfo>
                        <syncfusion:ChartAdornmentInfo Visible="True" SegmentLabelFontSize="13" SegmentLabelFontWeight="Bold" VerticalAlignment="Bottom" Symbol="Cross" SymbolWidth="10" SymbolInterior="Red" SymbolHeight="10" />
                    </syncfusion:ChartSeries.AdornmentsInfo>
                </syncfusion:ChartSeries>
            </syncfusion:ChartArea>
        </syncfusion:Chart>

 

Output

image

Chart Control Showing Stock Results

 

 Download the sample source from ChartDataBindingSample.

Wednesday Jun 16, 2010 at 09:30 PM | Posted by: | Category: ASP.NET | LINQ

In Essential Studio 2010 Vol. 3, we will support MultiColumnGrouping in PassThroughGrouping mode in the ASP.NET GridGroupingControl.

The performance of GridGroupingControl can be increased by using PassThroughGrouping DataSourceControl. PassThroughGrouping is a great way to take control of data being retrieved from the data source. In default mode, the grid will retrieve all the records from the data source and perform processes like sorting, grouping, and paging. But while using PassThroughGrouping, LINQ will take charge of the entire data retrieval and process part of grid. LINQ will do paging, sorting, and grouping by querying the data source, which leads to higher performance.

PassThroughGrouping mode can be enabled by using the PassThroughGrouping DataSourceControl. This can be added to the ASPX page, as shown in the following code:

[ASPX]
<Syncfusion:PassThruGroupingDataSource ID="PassThruGroupingDataSource1" runat="server"  GridGroupingControlID="GridGroupingControl1" SelectTable="Orders" SourceContext="DataClassesDataContext">
</Syncfusion:PassThruGroupingDataSource>

The GridGroupingControl’s definition could be as follows:

[ASPX]
 
<%
   1: --Grid Definition--
%>
<Syncfusion:GridGroupingControl ID="GridGroupingControl1"
    runat="server" AutoFormat="Office 2007 Blue" ShowGroupDropArea="True">
    <TableDescriptor AllowNew="False" AllowEdit="false">
    </TableDescriptor>
</Syncfusion:GridGroupingControl>
 
<%
   1: --GridPager Definition--
%>
<Syncfusion:GridPager ID="GridPager1" PagingControlID="GridGroupingControl1" runat="server" Skin="Office2007Blue" PageSize="10" Width="600px">
    <PagerItems>
        <Syncfusion:NextPreviousGridPagerItem FirstPageText="<<" PreviousPageText="<" ShowFirstPageButton="true"
            ShowPreviousPageButton="true" ShowLastPageButton="False" ShowNextPageButton="false" />
        <Syncfusion:NumericGridPagerItem ButtonCount="7" />
        <Syncfusion:NextPreviousGridPagerItem LastPageText=">>" NextPageText=">" ShowFirstPageButton="false"
            ShowPreviousPageButton="false" ShowLastPageButton="true" ShowNextPageButton="true" />
    </PagerItems>
</Syncfusion:GridPager>

 

The screen shots below depict the improved performance of the PassThroughGrouping data source control:

image

MultiColumn Grouping Performance

 

image

MultiColumn Sorting Performance

Wednesday Jun 16, 2010 at 02:18 PM | Posted by: | Category: LINQ | WPF

The Syncfusion WPF GridDataControl uses a LINQ-based engine for strongly typed object collections. It uses plain LINQ expression trees to formulate any query execution internally. PLINQ provides a parallel implementation of the LINQ pattern for executing operations in separate batch threads (for more details on the PLINQ documentation, refer to the MSDN link http://msdn.microsoft.com/en-us/library/dd997425.aspx). We have leveraged this ability to wrap up existing LINQ expressions on top of PLINQ with the AsParallel() extension, which can be enabled in the grid by setting the property as UsePLINQ=true. The grid supports PLINQ for sorting, grouping, and summaries.

We tested the results with a random Northwind Orders generator that constructed one million records.

 

 

image

Observable Collection Demo

Below are a few screenshots taken with dual-core and quad-core machines (running on Windows 7) when the grid is sorted by the ShipCountry column.

 

image

Dual Core (Windows 7 OS)

 

 

image

Quad Core (Windows 7 OS)

The above results clearly show that a quad-core machine keeps the CPU usage at 50% less than the dual-core machine for the same process.

Saturday May 1, 2010 at 09:23 PM | Posted by: | Category: LINQ | Silverlight | WPF

Microsoft StreamInsight CEP is a very powerful platform for developing complex event processing (CEP) systems. There are several development models that we can follow. In this post we will use the IObservable/IObserver model using .NET Reactive extensions (Rx). Since this will be a real-time application, we will also be using F# async workflows to pull stock data.

 

clip_image002[1]

 

 

F# async workflows are the coolest part of using F# in a real-time application. They allow writing concise code that (1) executes in parallel and (2) exposes to another .NET library with ease.

I won’t go into detail about F# except for the async workflow used in this application. There is a three-part series on using design patterns for F# async workflows; I have used Pattern #3 in this post, since we are using Rx to invoke the workflows. In this design pattern, the worker reports the progress through events—a modified version of AsyncWorker<> code is shown below,

    type JobCompletedEventArgs<'T>(job:int, result:'T) =
        inherit EventArgs()
        member x.Job with get() = job
        member x.Result with get() = result
    type AsyncWorker<'T>(jobs: seq<Async<'T>>) =
     
        // This declares an F# event that we can raise
        let allCompleted  = new Event<'T[]>()
        let error         = new Event<System.Exception>()
#if WPF
        let canceled      = new Event<System.OperationCanceledException>()
#else
        let canceled      = new Event<OperationCanceledException>()
#endif
        let jobCompleted  = new Event<JobCompletedEventArgs<'T>>()
        let cancellationCapability = new CancellationTokenSource()
        /// Start an instance of the work
        member x.Start()    =
            // Capture the synchronization context to allow us to raise events back on the GUI thread
            let syncContext = SynchronizationContext.CaptureCurrent()
     
            // Mark up the jobs with numbers
            let jobs = jobs |> Seq.mapi (fun i job -> (job, i+1))
            let raiseEventOnGuiThread(evt, args) = syncContext.RaiseEvent evt args                
            let work = 
                Async.Parallel
                   [ for (job,jobNumber) in jobs ->
                       async { let! result = job
                               syncContext.RaiseEvent jobCompleted (new JobCompletedEventArgs<'T>(jobNumber, result))
                               return result } ]
     
            Async.StartWithContinuations
                ( work,
                  (fun res -> raiseEventOnGuiThread(allCompleted, res)),
                  (fun exn -> raiseEventOnGuiThread(error, exn)),
                  (fun exn -> raiseEventOnGuiThread(canceled, exn)),
                    cancellationCapability.Token)
        /// Raised when a particular job completes
        [<CLIEvent>]
        member x.JobCompleted = jobCompleted.Publish
        /// Raised when all jobs complete
        [<CLIEvent>]
        member x.AllCompleted = allCompleted.Publish
        /// Raised when the composition is cancelled successfully
        [<CLIEvent>]
        member x.Canceled = canceled.Publish
        /// Raised when the composition exhibits an error
        [<CLIEvent>]
        member x.Error = error.Publish

We have used [<CLIEvent>] attributes to mark these events for exposing to other .NET CLI languages. Since we are using Rx, we need to have an event that inherits from System.EventArgs--JobCompletedEventArgs<T> does that here. The AsyncWorker is now ready to be used as a library for running parallel code.

Stock Quotes Reader

The Stock Quotes Reader defines a wrapper that performs a request to the server (it would be Yahoo finance here) and pulls the stocks.

    type StockAvailableEventArgs(stocks:string[]) =
        inherit EventArgs()
        member x.Stocks with get() = stocks
    type StockQuotesReader(quotes:string) =
        
        let stockAvailableEvent = new Event<StockAvailableEventArgs>()
        let httpLines (uri:string) =
          async { let request = WebRequest.Create uri   
                  use! response = Async.FromBeginEnd(request.BeginGetResponse, request.EndGetResponse)
                  use stream = response.GetResponseStream()
                  use reader = new StreamReader(stream)
                  let lines = [ while not reader.EndOfStream do yield reader.ReadLine() ]
                  return lines }
        
        // n - name, s - symbol, x - Stock Exchange, l1 - Last Trade, p2 - change in percent, h - high, l - low, o - open, p - previous close, v - volume
        let yahooUri (quotes:string) = 
            let uri = String.Format("http://finance.yahoo.com/d/quotes.csv?s={0}&f=nsxl1hlopv", quotes)
            uri
        member x.GetStocks() =
            let stocks = [httpLines(yahooUri quotes)]
            stocks
//        member x.TestAsync() =
//            let stocks = httpLines(yahooUri quotes)
//            Async.RunSynchronously(stocks)
        member x.PullStocks() =
            let stocks = x.GetStocks()
            let worker = new AsyncWorker<_>(stocks)
            worker.JobCompleted.Add(fun args ->
                stockAvailableEvent.Trigger(new StockAvailableEventArgs(args.Result |> List.toArray))
            )
            worker.Start()
        static member GetAsyncReader(quotes) =
            let reader = new StockQuotesReader(quotes)
            let stocks = reader.GetStocks()
            let worker = new AsyncWorker<_>(stocks)
            worker
        
        [<CLIEvent>]
        member x.StockAvailable = stockAvailableEvent.Publish

The above wrapper class does some interesting things:

  • It has an async block code that returns a line of data based on the response stream.
  • PullStocks will create async requests and raise the StockAvailable event whenever the async job is completed.

CEP Client

On the CEP client we will be using the following things:

· Syncfusion WPF GridDataControl – works well with high-speed data changes, keeping CPU usage to a minimum.

· Rx – creates requests and updates the ViewModel bound to the grid.

Application Setup

The WPF application uses a simple MV-VM by defining a StocksViewModel to hold stock data. The Stocks collection is bound to the Syncfusion WPF GridDataControl.

        <syncfusion:GridDataControl 
            x:Name="grid"                                         
            NotifyPropertyChanges="True"                                          
            AutoPopulateRelations="False"                                        
            Width="Auto"
            VisualStyle="Office14Silver"
            AllowGroup="True"                                    
            ShowGroupDropArea="True"                                    
            AutoFocusCurrentItem="False"     
            ColumnSizer="AutoOnLoad"
            ItemsSource="{Binding Model.Stocks}"                                         
            AllowEdit="False"                                         
            IsGroupsExpanded="True"
            ShowAddNewRow="False">
        </syncfusion:GridDataControl>

Using Rx to create requests

This real-time application requires real-time data that will be pulled over the wire for every 500 milliseconds. We will be making use of IObservable to create a streaming request and repeat that over a time delay.

        private void RealTimeStocks(int delay, string quotes)
        {
            var stockReader = new StockQuotesReader(quotes);
            var stockFeeds = Observable.Defer(() =>
            {
                stockReader.PullStocks();
                var evt = from e in Observable.FromEvent<StockAvailableEventArgs>(stockReader, "StockAvailable")
                          select new { Stocks = e.EventArgs.Stocks.ToStockQuotes() };
                var delayedEvt = Observable.Return(evt).Delay(TimeSpan.FromMilliseconds(delay));
                return delayedEvt;
            }).Repeat();
            var stocks = from s in stockFeeds
                         from t in s
                         select t.Stocks;
            stocks.SubscribeOnDispatcher().Subscribe((stockQuotes) =>
            {
                this.AddOrUpdateModel(stockQuotes);
            });
        }

We now have streaming real-time stock data pulled asynchronously over the Web and shown on the Syncfusion GridDataControl.

 

clip_image004[1]

This also works with Essential Grid Silverlight in out-of-browser (OOB) mode. If you want to get hold of the sample, send me a request.

 

Friday Jul 10, 2009 at 09:31 AM | Posted by: | Category: LINQ

Legacy applications, which are to be migrated to a new platform (WPF, Silverlight, etc.) require leveraging the underlying business layers too. We have an IQueryable interface implemented for DataTable in System.Data.DataSetExtensions. There are a couple of extension providers present in the .NET Framework itself:

  • DataTableExtensions
  • EnumerableRowCollectionExtensions

Data Table Extensions

This provides a list of functions to query with the DataTable. Most of them are casting functions:

  • AsDataView<T> -- Accepts an EnumerableRowCollection and returns a DataView.
  • AsDataView -- Accepts a DataTable and returns a DataView.
  • AsEnumerable -- Accepts a DataTable and returns an EnumerableRowCollection, which is used for querying with the DataRow collection.

Enumerable Row Collection Extensions

The LINQ provider is implemented for the DataRowCollection that is present in the DataTable.Rows property. The following are the LINQ extensions you can work with:

  • Select
  • OrderBy
  • OrderByDescending
  • ThenBy
  • ThenByDescending
  • Where
  • Cast

Select

Use the Select extension to provide the query with the required fields from the DataTable.

private static void SelectDataTable()
{
var dt = GetOrdersDataTable();
var orders = dt.AsEnumerable().Select(o =>
new
{
OrderID = o.Field<int>("OrderID"),
CustomerID = o.Field<string>("CustomerID"),
EmployeeID = o.Field<int>("EmployeeID"),
OrderDate = o.Field<DateTime>("OrderDate"),
ShipCountry = o.Field<string>("ShipCountry")
});
foreach (var order in orders)
{
Console.WriteLine(string.Format("OrderID : {0} / CustomerID : {1}
/ EmployeeID : {2} / OrderDate : {3} / ShipCountry : {4}", order.OrderID, order.CustomerID, 
order.EmployeeID, order.OrderDate, order.ShipCountry));
}
}

The Field<T> returns a strongly typed value from the underlying DataTable. You can also use SetField<T>

to set the field value thru a strongly typed object.

OrderBy/OrderByDescending/ThenBy /ThenByDescending

Sort operations can be performed by using the above functions. Check out the code below:

private static void SortDataTable()
{
var dt = GetOrdersDataTable();
var orders = dt.AsEnumerable().OrderBy(r => r.Field<string>("ShipCountry"));
var result = orders.ThenBy(r => r.Field<string>("CustomerID")).Select(o =>
new
{
OrderID = o.Field<int>("OrderID"),
CustomerID = o.Field<string>("CustomerID"),
EmployeeID = o.Field<int>("EmployeeID"),
OrderDate = o.Field<DateTime>("OrderDate"),
ShipCountry = o.Field<string>("ShipCountry")
});
foreach (var order in result)
{
Console.WriteLine(string.Format("OrderID : {0} / CustomerID : {1} / EmployeeID : {2}
/ OrderDate : {3} / ShipCountry : {4}", order.OrderID, order.CustomerID, order.EmployeeID, 
order.OrderDate, order.ShipCountry));
}
}
Where

Filter operations require a predicate match to be passed. See the code below:

private static void WhereOperation()
{
var dt = GetOrdersDataTable();
var filteredOrders = dt.AsEnumerable().Where(o => o.Field<string>("ShipCountry")
== "Brazil")
.Select(o =>
new
{
OrderID = o.Field<int>("OrderID"),
CustomerID = o.Field<string>("CustomerID"),
EmployeeID = o.Field<int>("EmployeeID"),
OrderDate = o.Field<DateTime>("OrderDate"),
ShipCountry = o.Field<string>("ShipCountry")
});
foreach (var order in filteredOrders)
{
Console.WriteLine(string.Format("OrderID : {0} / CustomerID : {1} / EmployeeID : {2}
/ OrderDate : {3} / ShipCountry : {4}", order.OrderID, order.CustomerID, 
order.EmployeeID, order.OrderDate, order.ShipCountry));
}
}

With these functions we can easily get expressions to work with legacy DataTable objects.

Note: The API documentation suggests that these APIs are used internally in the .NET Framework and are not intended to be used in our code directly :).

Hope this helps.

Tag cloud