Yet still more time on traveling, I thought I'd do a little demo of a data oriented scenario with Silverlight.
Here is what we are after:
The data for the app is loaded from a Linq model and sent to the Silverlight client via a WCF services.
As the user makes changes to the grid, we asynchronously update the Linq model on the service.
The app uses local storage to cache the data on the local machine to reduce network calls.
Create a new Silverlight application and associated web site.
Let's start by creating a data model in the web site.
Then drag over the Employees table.
Then, Select Photo and delete it. It is a large binary blob that we don't want to be sending back and forth.
Then, because we are going to expose this over a WCF service, let's make it Serializable.
Now let's add the WCF service. Be sure to select the Silverlight-enabled WCF service.
Then the implantation is straight forward. In this method you can customize based on the user's role, do paging, etc. For now, I will just take the first 15 for this demo.
[OperationContract]
public IEnumerable<Employee> GetAllEmployees()
{
var context = new NorthwindDataContext();
var q = from employee in context.Employees
select employee;
return q.Take(15).ToList();
}
Now in the client, we need to add a reference to this service.
Then define a very simple UI...
<StackPanel>
<Button x:Name='btn1' Width='100' Height='50' Content='Load Grid' Click='btn1_Click' RenderTransformOrigin="0.5,0.5"></Button>
<my:DataGrid x:Name='dataGrid' ></my:DataGrid>
<TextBlock x:Name="label"></TextBlock>
</StackPanel>
And handle the button click
private void btn1_Click(object sender, RoutedEventArgs e)
{
var client = new EmployeeService.EmployeesServiceClient();
client.GetAllEmployeesAsync();
client.GetAllEmployeesCompleted += new EventHandler<GetAllEmployeesCompletedEventArgs>(client_GetAllEmployeesCompleted);
label.Text = 'Sending Request';
}
void client_GetAllEmployeesCompleted(object sender, GetAllEmployeesCompletedEventArgs e)
{
dataGrid.ItemsSource = e.Result;
label.Text = 'Got results from network';
}
Now let's send up the changes as the user changes the values in the grid.
What we want to do is sign up for changes on each row. The interesting thing here is that we recycle the rows, so we have to be careful to not sign up for an event more than once. The solution we came up with was to listen for the Loading and Unloading row events and register, unregiser the events on each.
So DataGrid looks like this:
<my:DataGrid Width='600' Height='200' x:Name='dataGrid' UnloadingRow='dataGrid_UnloadingRow' LoadingRow='dataGrid_LoadingRow' AutoGenerateColumns="True"></my:DataGrid>
And the code:
private void dataGrid_LoadingRow(object sender, DataGridRowEventArgs e)
{
((Employee)e.Row.DataContext).PropertyChanged +=
new PropertyChangedEventHandler(Page_PropertyChanged);
}
private void dataGrid_UnloadingRow(object sender, DataGridRowEventArgs e)
{
((Employee)e.Row.DataContext).PropertyChanged -=
new PropertyChangedEventHandler(Page_PropertyChanged);
}
void Page_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Employee ep = sender as Employee;
label.Text = 'Employee ' + ep.EmployeeID + ' changed. Updating network';
}
With this, as the user changes values in the grid, this PropertyChanged event is called.
Now all we need to do is call the server with the changed data.
To do that, let's go back and add a new web service.
public void UpdateEmployee(Employee employee)
{
NorthwindDataContext context = new NorthwindDataContext();
var oldEmployee = (from e in context.Employees
where e.EmployeeID == employee.EmployeeID
select e).First();
oldEmployee.FirstName = employee.FirstName;
oldEmployee.LastName = employee.LastName;
// add the rest of the properites
context.SubmitChanges();
}
Note: There are of course easier\better ways to do this sort of updating in linq... http://msdn.microsoft.com/en-us/library/bb425822.aspx
Then back to the Silverlight client and update the reference
Now, add a few lines to call this method and we are set.
void Page_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Employee ep = sender as Employee;
label.Text = 'Employee ' + ep.EmployeeID + ' changed. Updating network';
var client = new EmployeeService.EmployeesServiceClient();
client.UpdateEmployeeCompleted += new EventHandler<AsyncCompletedEventArgs>(client_UpdateEmployeeCompleted);
client.UpdateEmployeeAsync(ep,ep.EmployeeID);
}
void client_UpdateEmployeeCompleted(object sender, AsyncCompletedEventArgs e)
{
label.Text = 'Employee ID'+e.UserState+' successfully updated';
}
Run it and it works great!
Make changes, refresh to be sure the changes are reflected in the underlying datamodel.
OK, for a final step, let's see if we can cache this data offline in Silverlight's per-application Isolated Storage.
Add the following to the page class
private IsolatedStorageSettings appSettings =
IsolatedStorageSettings.ApplicationSettings;
To client_GetAllEmployeesCompleted add
appSettings['backStore'] = e.Result;
appSettings.Save();
This saves the backStore into the local store.
To the Page constructor add
if (appSettings.Contains('backStore'))
{
label.Text = 'loading data from local store';
dataGrid.ItemsSource = appSettings['backStore'] as IEnumerable;
}
else
{
label.Text = 'No local data';
}
If there is data in the store, we use it.
Run it... the first time, no data..
Hit load and refresh and you should have local data
Now, just to be sure things are working the way we expect, right click on the Silverlight control, select Application Storage and Delete this one for this app. Notice it says zero as a rounding issue,there is so data there.
Hit refresh and it is back to no data. Just as we expected.
In this post we:
Got the data for the app is loaded from a Linq model and sent to the Silverlight client via a WCF services.As the user makes changes to the grid, we asynchronously update the Linq model on the service.
The app uses local storage to cache the data on the local machine to reduce network calls.
Tidak ada komentar:
Posting Komentar