Coproject - a RIA Caliburn.Micro demo, part 6

In this part, we will load data from database and start using Caliburn.Micro coroutines. Remember that you can get latest news and source code from Coproject Codeplex site.

ToDoListsView

Create a new Silverlight User Control called ToDoListsView in the Views folder. Then replace the original LayoutRoot grid with this one:

<Grid x:Name="LayoutRoot">
        <Grid.ColumnDefinitions>
                <ColumnDefinition Width="6*" />
                <ColumnDefinition Width="5*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
                <RowDefinition Height="auto" />
                <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <Button x:Name="LoadData" Content="Load" />

        <ItemsControl x:Name="Lists" Grid.Row="1">
                <ItemsControl.ItemTemplate>
                        <DataTemplate>
                                <StackPanel>
                                        <TextBlock Text="{Binding Name}" FontWeight="Bold" 
                                                                Style="{StaticResource DefaultTextBlockStyle}" />
                                        <TextBlock Text="{Binding Description}" TextWrapping="Wrap" 
                                                                Style="{StaticResource DefaultTextBlockStyle}" />
                                </StackPanel>
                        </DataTemplate>
                </ItemsControl.ItemTemplate>
        </ItemsControl>
</Grid>

As we already know, Caliburn.Micro will automatically bind Lists ItemsControl to ‘Lists’ property of respective view model. Therefore, you can guess that pressing LoadData button will execute function called LoadData, and you would be right. So let’s implement the view model.

ToDoListsViewModel

Open ToDoListsViewModel and add this code to it:

public IEnumerable<ToDoList> Lists { get; private set; }
public void LoadData()
{
        CoprojectContext context = new CoprojectContext();
        EntityQuery<ToDoList> query = context.GetToDoListsQuery().OrderByDescending(l => l.CreatedDate);
        context.Load(query, LoadDataCallback, null);
}

private void LoadDataCallback(LoadOperation<ToDoList> data)
{
        Lists = data.Entities;
        NotifyOfPropertyChange(() => Lists);
}

 

In Silverlight, all data loading is done asynchronously. It is good for keeping user interface responsive but programming asynchronous tasks requires more coding than working synchronously. To load data from a RIA Services service, you need to instantiate a context (created on compile time by RIA services), get default query from it, modify the query and then pass it back to the context to load it. You should also provide a callback that gets called when the data are loaded.

If you build the project and click Load, after a while, data should appear.
image

Coroutines

You probably noticed that if we wanted to do some other work after data are loaded, we would have to put it into LoadDataCallback. And if we wanted to load some other data, another callback would have to be created. This could easily become a a nightmare to maintain and debug. Happily, Caliburn.Micro comes to help with coroutines.

Caliburn coroutines are based on objects that implement IResult interface. Purpose of these objects is to execute some action and fire an event when the action is done. C.M then sequentially enumerates results returned from methods like LoadData, executes a result, waits for it to complete and then execute next one. This is achieved by yield return C# keyword. You probably see similarity to new keywords ‘async’ and ‘await’ planned for C# 5.

LoadDataResult

So, let’s implement result that would load data from a RIA Services context. Create LoadDataResult in Framework as follows:

public class LoadDataResult<TEntity> : IResult where TEntity : Entity
{
        public EntityQuery<TEntity> Query { get; set; }
        public DomainContext Context { get; set; }
        public LoadOperation<TEntity> Result { get; private set; }

        public event EventHandler<ResultCompletionEventArgs> Completed;

        public LoadDataResult(DomainContext context, EntityQuery<TEntity> query)
        {
                Query = query;
                Context = context;
        }

        public void Execute(ActionExecutionContext context)
        {
                Context.Load(Query, LoadDataCallback, null);
        }

        private void LoadDataCallback(LoadOperation<TEntity> data)
        {
                Result = data;
                OnCompleted();
        }

        private void OnCompleted()
        {
                var handler = Completed;
                if (handler != null)
                {
                        handler(this, new ResultCompletionEventArgs());
                }
        }
}

Although it might seem complicated, most of the code is just initialization and obvious definitions. The only functions worth attention are Exxecute and LoadDataCallback and these are the same as in the LoadData function. When this result is passed to C.M, it will run Execute and then wait until Completed event is raised. Then it will enumerate next result and so on.

We can replace LoadData and LodDataCallback in ToDoListsViewModel with this function:

public IEnumerable<IResult> LoadData()
{
        CoprojectContext context = new CoprojectContext();
        EntityQuery<ToDoList> query = context.GetToDoListsQuery().OrderByDescending(l => l.CreatedDate);
        var result = new LoadDataResult<ToDoList>(context, query);
        yield return result;

        Lists = result.Result.Entities;
        NotifyOfPropertyChange(() => Lists);
}

Now, although LoadDataResult loads data asynchronously, execution of this function will stop at yield return and continue once the result completes. Workflow of LoadData function is obvious now and can be easily edited later.

Include ToDoItems

We still need to add ToDoItems under their parent ToDoLists. First, we must tell RIA Services to include ToDoItems with lists. Open Services/CoprojectService.cs in Coproject.Web project and this function below GetToDoLists function:

public IQueryable<ToDoList> GetToDoListsWithItems()
{
        return this.ObjectContext.ToDoLists.Include("ToDoItems");
}

ObjectContext is an Entity Framework context, so we instruct EF to include nested ToDoItems when querying underlying database. If these properties are set, RIA Services will automatically include these into metadata transferred to the client ([Include] attribute on respective properties in CoprojectService.metadata.cs that we added in Step 2). Now rebuild the solution in order to have RIA Services recreate context on the client.

Next, point query creation in ToDoListsViewModel.LoadData from context.GetToDoListsQuery to context.GetToDoListsWithItemsQuery.

Finally, edit ToDoListsView and add the following control below the two TextBlocks in ItemTemplate of the ‘Lists’ control:

<ListBox ItemsSource="{Binding ToDoItems}"
                                Margin="5,0,0,0" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
        <ListBox.ItemTemplate>
                <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                                <TextBlock Text="{Binding DueDate,StringFormat='\{0:d\}'}" 
                                                   FontWeight="Bold" Margin="0,0,5,0" />
                                <TextBlock Text="{Binding Content}" />
                        </StackPanel>
                </DataTemplate>
        </ListBox.ItemTemplate>
</ListBox>

Now you can run the application. The list will probably run off the screen, so let’s wrap Lists control with ScrollViewer:

<ScrollViewer Grid.Row="1" Style="{StaticResource ListsScrollViewerStyle}">
        <ItemsControl x:Name="Lists">
                ...
        </ItemsControl>
</ScrollViewer>

That is all for this step. The application should look like that:
image

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值