65.9K
CodeProject 正在变化。 阅读更多。
Home

Silverlight 4 OData 分页(使用 RX 扩展)

starIconstarIconstarIconstarIconstarIcon

5.00/5 (6投票s)

2010 年 5 月 29 日

Ms-PL

5分钟阅读

viewsIcon

56600

downloadIcon

479

Silverlight 4 OData 分页配合 RX 扩展的示例。

Silverlight 4, OData 分页, RX 扩展, 使用 ViewModel

实时示例

本文接续 使用 oData 和 RX 扩展的简单 Silverlight 4 示例 一文的结尾。本文实现了分页功能。它还展示了 Dave Sexton (http://davesexton.com) 创建的一些精巧的RX 扩展代码。然而,令人有些讽刺的是,Dave Sexton 对这段代码并不满意(尽管我认为它完美到可以装裱)。

Dave Sexton 对代码不满的原因是,他认为有更具扩展性的替代写法。他在 此链接 的帖子中提供了替代版本的示例,并进行了详细解释。我写这篇文章的目标是使用“ViewModel 风格”,以最少的代码量实现分页。虽然上一篇文章中的代码可用,但 Dave Sexton 的代码大大减少了代码量,并将 ViewModel 调用 Model 的过程简化为一行代码。

如果您不熟悉ViewModel 风格,建议您阅读 Silverlight ViewModel 风格:一个(过度)简化的解释 以获得入门介绍。

使用 OData 和 RX 扩展的简单 Silverlight 4 示例

我们从 使用 OData 和 RX 扩展的简单 Silverlight 4 示例 中创建的项目开始。该教程解释了如何创建 OData 服务以及注册 RX 扩展的程序集。

命令

由于我们将启用分页,因此需要从 View(UI)在 ViewModel 中引发一个事件。我们通过命令(Commanding)来实现这一点。为了支持命令,我们将 _DelegateCommand.cs_ 文件添加到项目中。John Papa 在这篇博文中介绍了该文件的使用:http://johnpapa.net/silverlight/5-simple-steps-to-commanding-in-silverlight/

模型

使用 oData 和 RX 扩展的简单 Silverlight 4 示例 教程中,我们创建了一个连接到 OData 服务的 Model。我们将打开它,并用以下代码替换所有代码。

using System;
using System.Collections.ObjectModel;
using System.Data.Services.Client;
using System.Linq;
using SilverlightODataSample.wsSampleCustomerData;

namespace SilverlightODataSample
{
    public class Model
    {
        public static ObservableCollection<customerrecord> 
               GetCustomers(int intPage, int intRecordsPerPage)
        {
            // Adapted from code by Dave Sexton http://davesexton.com/
            // From: http://social.msdn.microsoft.com/Forums/
            //     en-US/rx/thread/5b12668e-680f-436d-82e6-ebf1e0be66f5/

            // Collection to hold the final results
            var collection = new ObservableCollection<customerrecord>();

            // Create a URI to the service
            var uri = new Uri(GetBaseAddress(), UriKind.RelativeOrAbsolute);

            // Set-up call to OData WCF Service
            var sds = new SampleDataSource(uri);

            // Compute the CurrentPage
            int CurrentPage = ((intPage * intRecordsPerPage) - intRecordsPerPage);

            // Create the query
            var query = (from data in sds.SampleCustomerData
                         select data).Skip(CurrentPage).Take(intRecordsPerPage);

            // A collection to hold the results that the oData Service will return
            var results = new DataServiceCollection<customerrecord>();

            // A variable to represent the Iobservable event 
            var whenLoaded = 
              Observable.FromEvent<loadcompletedeventargs>(results, "LoadCompleted");

            // Subscribe to the Event "LoadCompleted" with "disposable" (IObvervable)
            var disposable = whenLoaded.Subscribe(value =>
            {
                // If we have an error, report it
                if (value.EventArgs.Error != null)
                {
                    throw value.EventArgs.Error;
                }
                else
                {
                    // results contains the values
                    foreach (var item in results)
                    {
                        // Add each element from results 
                        // to the final collection
                        collection.Add(item);
                    }
                }
            });

            // This actually calls the OData WCF Service
            // And kicks everything off
            results.LoadAsync(query);

            // Return the final collection
            return collection;
        }

        #region GetBaseAddress
        private static string GetBaseAddress()
        {
            // This gets the address of the webservice by 
            // getting the AbsoluteUri and then stripping out the 
            // name of the .xap file
            string strXapFile = @"/ClientBin/SilverlightODataSample.xap";
            string strBaseWebAddress =
                App.Current.Host.Source.AbsoluteUri.Replace(strXapFile, "");
            return string.Format(@"{0}/{1}", strBaseWebAddress, @"Service.svc");
        }
        #endregion
    }
}

ViewModel

我们还将修改 ViewModel。首先,让我们看看从 ViewModel 调用 Model 所需的代码有多么简单。

#region GetCustomers
private void GetCustomers()
{
    // Fill the colCustomerRecord collection 
    colCustomerRecord = Model.GetCustomers(CurrentPage, RecordsPerPage);
}
#endregion

DataGrid(或任何可以容纳 Designer 决定使用的集合的 UI 元素)将使用 colCustomerRecord 作为其源。

#region CustomerRecord
private ObservableCollection<customerrecord> _colCustomerRecord
    = new ObservableCollection<customerrecord>();
public ObservableCollection<customerrecord> colCustomerRecord
{
    get { return _colCustomerRecord; }
    private set
    {
        if (colCustomerRecord == value)
        {
            return;
        }

        _colCustomerRecord = value;
        this.NotifyPropertyChanged("colCustomerRecord");
    }
}
#endregion

此集合是一个 ObservableCollection,因此每当发生更改时,它都会自动更新绑定到它的 UI 元素。

GetCustomers 方法需要请求的页码和每页记录数。我们在 ViewModel 中创建属性来保存这些值。

#region CurrentPage
private int _CurrentPage = 1;
public int CurrentPage
{
    get
    {
        return this._CurrentPage;
    }
    set
    {
        this._CurrentPage = value;
        this.NotifyPropertyChanged("CurrentPage");
    }
} 
#endregion

#region RecordsPerPage
private int _RecordsPerPage = 10;
public int RecordsPerPage
{
    get
    {
        return this._RecordsPerPage;
    }
    set
    {
        this._RecordsPerPage = value;
        this.NotifyPropertyChanged("RecordsPerPage");
    }
} 
#endregion

接下来,我们创建 ICommand 以允许 View 来回翻页。

#region PreviousPageCommand
public ICommand PreviousPageCommand { get; set; }
public void PreviousPage(object param)
{
    CurrentPage--;
    GetCustomers();
}

private bool CanPreviousPage(object param)
{
    // Must not already be on the first page
    return (CurrentPage > 1);
}
#endregion

#region NextPageCommand
public ICommand NextPageCommand { get; set; }
public void NextPage(object param)
{
    CurrentPage++;
    GetCustomers();
}

private bool CanNextPage(object param)
{
    // There must be records to move to the next page
    return (colCustomerRecord.Count > 0);
}
#endregion

我们还创建了一个 ICommand 来允许设置每页记录数。

#region SetRecordsPerPageCommand
public ICommand SetRecordsPerPageCommand { get; set; }
public void SetRecordsPerPage(object param)
{
    ContentControl Element = (ContentControl)param;
    RecordsPerPage = Convert.ToInt32(Element.Content);
    GetCustomers();
}

private bool CanSetRecordsPerPage(object param)
{
    return (param != null);
}
#endregion

我们实现 INotifyPropertyChanged 以支持属性更改时的自动通知。例如,以便在当前页等属性更改时自动更新 UI。

在这个示例中,它实际上不是必需的,因为唯一绑定到该属性的控件是实际更改该值的控件。但是,如果我们有一个显示当前页的文本框,我们将需要实现这一点来实现自动更新通知。

#region INotifyPropertyChanged
// This is a supporting method to raise a notification for any
// Element that is subscribed to a Property that implements
// NotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;

private void NotifyPropertyChanged(String info)
{
    if (PropertyChanged != null)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(info));
    }
}
#endregion

最后,这是 ViewModel 的构造函数。它设置了 ICommand 并调用 GetCustomers 来加载第一页。

public MainPageModel()
{
    // Set the command properties
    PreviousPageCommand = 
      new DelegateCommand(PreviousPage, CanPreviousPage);
    NextPageCommand = new DelegateCommand(NextPage, CanNextPage);
    SetRecordsPerPageCommand = 
      new DelegateCommand(SetRecordsPerPage, CanSetRecordsPerPage);

    // Get the Customers
    GetCustomers();
}

创建视图 (View)

我们通过重新创建 View 来完成示例。

在上一篇文章中,它只是加载了第一页。

现在我们添加分页按钮和一个组合框以允许选择每页的记录数。上图显示了各个部分的连接关系。

注意:对于图表,我启用了设计时示例数据。您可以在此文章中了解如何实现:Silverlight MVVM:在使用 Web 服务时在 Expression Blend 中启用设计时数据

创建 View 的步骤在:使用 oData 和 RX 扩展的简单 Silverlight 4 示例 中已经介绍。但是,按钮和组合框是新的,所以让我们一步步地看看 Designer 将如何实现它们。

分页按钮

使用Expression Blend 4+,我们首先将两个按钮拖到页面上。

接下来,我们将一个 InvokeCommandAction 行为(Behavior)拖到每个按钮上。

在每个行为的属性中,我们将 EventName 选择为 Click,然后点击 Command 旁边的“高级选项”框。

我们选择“数据绑定...”

然后绑定到相应的命令。

组合框

我们也将一个 InvokeCommandAction 行为拖到组合框上。

在行为的属性中,我们将 EventName 设置为 SelectionChanged

我们将 Command 绑定到 SetRecordsPerPageCommand

但是,在这种情况下,我们还需要传递选择的记录数,所以我们点击 CommandParameter 旁边的“高级选项”框。

然后我们将 CommandParameter 绑定到 SelectedValue

ViewModel 风格”,简单 - 代码量不多

希望您觉得这个示例简单易懂。“ViewModel 风格”可以用于您 100% 的 Silverlight 项目。使用 ViewModel 所需的代码量与仅仅使用代码隐藏(code-behind)所需代码量大致相同。使用 ViewModel 而非代码隐藏的原因是,您可以让完全不懂编程的 Designer 来设计您的 UI。您可能会发现这一点对于制作专业的应用程序至关重要。它也是可测试的,请参见:单元测试 Silverlight ViewModel 风格的模态弹出窗口

如果您需要更复杂的架构,可以通过添加额外的抽象来实现 MVVM。例如,您不会直接从 Model 调用 OData 服务,而是创建一个“服务类”来实际调用服务并填充 Model。然而,View 不需要做任何更改。

© . All rights reserved.