Silverlight Chart User Control
Silverlight Chart User Control
In the last month I was creating a simple Silverlight Application which display a Financial Chart. It was in the following post: Displaying Chart using Silverlight 2.0 User Control
In this post I have a better version of the previous chart, which includes:
-
Handling large amount of data
-
Displaying the tags (x-y values) correctly
-
Supporting of resizing
-
Using RESTfull Web Service
The result of this project is as showed in the following image:
Stock Quote Service Contract
The Stock Quote Service is a WCF RESTfull Service, which can be called using HTTP Get method, with three arguments:
The Service and Data Contracts are defined below:
[ServiceContract]
public interface IStockQuoteService
{
[OperationContract, WebGet(UriTemplate = "HistoricalPrices?ticket={ticket}&fromDate={fromDate}&toDate={toDate}", ResponseFormat = WebMessageFormat.Xml)]
List<StockQuote> GetStockQuoteData(String ticket, String fromDate, String toDate);
}
[DataContract(Namespace = "http://www.finance.com/StockQuote")]
public class StockQuote
{
[DataMember]
public String Ticket { get; set; }
[DataMember]
public System.DateTime Date { get; set; }
[DataMember]
public Decimal Open { get; set; }
[DataMember]
public Decimal High { get; set; }
[DataMember]
public Decimal Low { get; set; }
[DataMember]
public Decimal Close { get; set; }
[DataMember]
public Int32 Volume { get; set; }
}
For learn how to implement WCF REST Services read HTTP Web Programming with WCF 3.5: Creating a Template based URI.
Stock Quote Service Implementation
The Stock Quote Service Implementation is used Linq to Sql in order to retrieve the data from the database, and send it back into the Silverlight Client Application:
public class StockQuoteService : IStockQuoteService
{
public List<StockQuote> GetStockQuoteData(String ticket, String fromDate, String toDate)
{
List<StockQuote> quotesList = new List<StockQuote>();
DateTime fromDate_ = DateTime.Parse(fromDate);
DateTime toDate_ = DateTime.Parse(toDate);
QuotesDataContext dataContext = new QuotesDataContext();
var quotes = from q in dataContext.Quotes
where (q.Ticket == ticket) && (q.Date >= fromDate_) && (q.Date <= toDate_)
select q;
foreach (var quote in quotes)
{
StockQuote stockQuote = new StockQuote()
{
Ticket = quote.Ticket,
Low = (Decimal)quote.Low,
High = (Decimal)quote.High,
Close = (Decimal)quote.Close,
Date = quote.Date,
Volume = (Int32)quote.Volume,
Open = (Decimal)quote.Open
};
quotesList.Add(stockQuote);
}
return quotesList;
}
}
Accessing the Stock Quote Service
For accessing this Stock Quote Service, using the following url (depends on the data inside Quotes table):
http://localhost:4958/ChartingApplication_Web/StockQuoteService.svc/HistoricalPrices?ticket=msft&fromDate=1/1/2000&toDate=1/1/2008
You can see the appropriate result:
<ArrayOfStockQuote
xmlns="http://www.finance.com/StockQuote"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<StockQuote>
<Close>58.28</Close>
<Date>2000-01-03T00:00:00</Date>
<High>59.31</High>
<Low>56.00</Low>
<Open>58.69</Open>
<Ticket>MSFT</Ticket>
<Volume>53228400</Volume>
</StockQuote>
<StockQuote>
<Close>56.31</Close>
<Date>2000-01-04T00:00:00</Date>
<High>58.56</High>
<Low>56.12</Low>
<Open>56.78</Open>
<Ticket>MSFT</Ticket>
<Volume>54119000</Volume>
</StockQuote>
<StockQuote>
...
Call Web Service from Silverlight Application
For getting the data inside a Silverlight Application, I use the following two methods:
-
GetStockQuoteDataAsync - Marked as ScriptableMember, which means can be called from javascript.
-
client_DownloadStringCompleted - The callback - each call outside from Silverlight is called asynchronously:
[ScriptableMember]
public void GetStockQuoteDataAsync(String ticket, String fromDate, String toDate)
{
try
{
WebClient client = new WebClient();
String address =
String.Format(
"http://localhost:4958/ChartingApplication_Web/StockQuoteService.svc/HistoricalPrices?ticket={0}&fromDate={1}&toDate={2}", ticket, fromDate, toDate);
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(
client_DownloadStringCompleted);
client.DownloadStringAsync(new Uri(address));
m_headerElement.SetAttribute(
"innerHTML", String.Format("<b>{0}</b> {1} - {2}", ticket, fromDate, toDate));
}
catch (Exception ex)
{
}
}
void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error == null)
{
XDocument xmlQuotes = XDocument.Parse(e.Result);
XNamespace ns = xmlQuotes.Root.GetDefaultNamespace();
var quotes = from quote in xmlQuotes.Descendants(ns + "StockQuote")
select new DataPoint
{
Date = DateTime.Parse(quote.Element(ns + "Date").Value),
Value = Decimal.Parse(quote.Element(ns + "Close").Value)
};
DataPoint[] data = quotes.ToArray<DataPoint>();
DisplayChart(data);
}
else
{
String errorMessage = e.Error.Message;
}
}
Cross Domain Calls
If you call remote one of the following from Silverlight 2 application:
-
REST
-
SOAP-WS*
-
RSS
-
JSON
-
XML HTTP Services
-
System.Net.Socket
Silverlight will throw "Download Failure" exception if the URI is not to the same domain of the Silverlight server (which the Silverlight control downloaded).
Silverlight 2 application can optionally make cross-domain calls (calls from different URL's that of the Silverlight downloaded domain) by setting XML policy file in the web server, that enables the clients to make these cross-domain calls.
Data Access Layer
The Data Access Layer is based on SQL Server 2005 with table Quotes.
To create the table Quotes, you can use the following script:
USE [FinancialData]
GO
/****** Object: Table [dbo].[Quotes] Script Date: 04/10/2008 16:35:05 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Quotes](
[Ticket] [varchar](250) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[Date] [datetime] NOT NULL,
[Open] [decimal](12, 2) NULL,
[High] [decimal](12, 2) NULL,
[Low] [decimal](12, 2) NULL,
[Close] [decimal](12, 2) NULL,
[Volume] [bigint] NULL,
CONSTRAINT [PK_Quotes] PRIMARY KEY CLUSTERED
(
[Ticket] ASC,
[Date] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
Conclusion
Although I have a lot of work to do, for the chart only I'm going to add zooming, tooltip and more functionality, but it showed here how simple is to create an end-to-end application, using these technologies.
I have declared on this project My new Innovative Project and after creating this chart connected to a database, the next missions will be to design the web pages, menues, and start manipulating some analysis on this chart...
I hope you enjoyed, and you can download the source code here: Silverlight 2 Chart User Control