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

LightSwitch:集成 ComponentOne 调度程序

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.91/5 (12投票s)

2011 年 10 月 26 日

Ms-PL

6分钟阅读

viewsIcon

55651

本文的目的是演示在集成复杂控件时可以使用的各种方法。

在 LightSwitch 中数据绑定复杂的Silverlight多表控件:ComponentOne Scheduler

引言

在本文中,我们将处理 ComponentOne Silverlight Scheduler 控件中一个复杂的多表控件。这是一个功能齐全的控件,具有大量功能。使其与 LightSwitch 配合使用绝对值得付出努力。本文的目的是演示在集成复杂控件时可以使用的各种方法。

背景

ComponentOne 的一个强项是他们能够创建出色的聚合数据的 Silverlight 控件。LightSwitch 帮助网站之前介绍了他们的 LightSwitch 扩展:在 LightSwitch 中使用 OLAP。该控件主要设计为由最终用户配置。文章:LightSwitch 控件扩展制作者的困境解释了 LightSwitch 控件扩展易于使用,但配置有限。

该控件之所以如此复杂,是因为它同时与多个表进行通信。它允许您存储联系人资源预约。当集成到您自己的 LightSwitch 应用程序中时,这些表将指向您的数据。使用此控件,您可以例如创建一个允许员工预订会议室的应用程序。

LightSwitch

Scheduler 控件是一个普通的 Silverlight 控件。LightSwitch “带来的” 是在几分钟内创建应用程序的能力。当我阅读完控件的说明后,我能够在 5 分钟内创建所需的表格。此时,90% 的应用程序已经完成。

Scheduler 控件

image

当您运行示例(您可以从下载页面下载代码)时,您会看到一个弹出窗口,指示您正在使用评估版本。

单击“X”关闭弹出窗口。

image

双击某一天以打开一个弹出窗口,允许您创建或编辑预约

image

您还可以指定要附加到预约资源。您可以选择现有资源之一,或添加和删除资源

image

您还可以指定要附加到预约联系人。您可以选择现有联系人之一,或添加和删除联系人

image

类别也提供相同的选项。在此示例中,我们关闭了添加新类别的功能。

image

Scheduler 控件真正出彩之处在于它能够对这些数据进行分组并按联系人显示日历…

image

…或按资源

image

…或按类别

image

它还按工作周显示数据。

image

您可以创建循环预约。

image

它甚至会弹出提醒。

更多关于 Scheduler 控件

您可以通过此链接查看所有 ComponentOne 控件的现场演示:http://demo.componentone.com/Silverlight/ControlExplorer/

您可以从以下链接获取 Scheduler 控件的完整文档:http://helpcentral.componentone.com/nethelp/c1schedulerSL/

但是,如果您下载并安装演示包

image

… 并单击已安装的示例

image

您会找到可以直接运行的项目。然而,这些是普通的 Silverlight 项目,因此您需要知道如何在 LightSwitch 中绑定它们。好消息是,正如我们所演示的,LightSwitch 能够与任何 Silverlight 控件一起工作,无论其多么复杂。

实现控件

image

整个 解决方案 由三个实体(表)和一个包含 Schedule 控件Silverlight 项目组成。Schedule 控件是唯一放置在 Calendar 屏幕上的项,而 Calendar 屏幕是应用程序中唯一的屏幕

image

image

image

实体的模式是通过查看控件旨在使用的数据源和字段(以及阅读文档和查看示例代码)来确定的。

Silverlight 项目

image

注意:请参阅:创建 LightSwitch 自定义 Silverlight 控件,以获取在 LightSwitch 中实现 Silverlight 控件的逐步说明)。

Silverlight 项目中,我们添加对 ComponentOne 程序集和 LightSwitch 程序集的引用。另请注意,当控件放置在 LightSwitch 屏幕上时,需要再次引用相同的程序集。有关此示例,请参阅用于实现此控件的步骤:在 Visual Studio LightSwitch 中使用 Telerik 富文本编辑器

有很多 XAML 借用自 ComponentOne 示例代码以显示下拉列表和按钮,但以下 XAML 是用于显示实际 Scheduler 控件的全部内容

<c1:C1Scheduler Grid.Row="2" Grid.Column="1" 
                x:Name="sched1" 
                GroupPageSize="2" 
                StyleChanged="sched1_StyleChanged" 
                UserDeletingAppointment="sched1_UserDeletingAppointment">
    <c1sched:C1Scheduler.Settings>
        <c1sched:C1SchedulerSettings 
            AllowContactsEditing="True"
            AllowResourcesEditing="True"
            AllowCategoriesEditing="False" 
            AllowCategoriesMultiSelection="True" 
            AllowResourcesMultiSelection="True" 
            AllowContactsMultiSelection="True" 
            FirstVisibleTime="08:00:00" />
    </c1sched:C1Scheduler.Settings>
</c1:C1Scheduler>

通常,我们会在 XAML 中包含绑定代码,但是当我们这样做时,此控件会抛出错误,因为 LightSwitch 会首先绑定到控件,但实际上不会传递任何数据。

通常,如果绑定出现问题或错误,它会静默失败。Scheduler 控件很复杂,会抛出错误。因此,我们在代码后面编写绑定以解决此问题。

简单地说,在我们知道 LightSwitch 确实有数据可以提供之前,我们不会实际设置任何绑定(是的,这也可以通过使用 Silverlight 值转换器来实现)。

LightSwitch 将数据绑定到控件时,我们连接一个属性来调用以下方法

 private static void OnScheduleControlPropertyChanged(
    DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    // Get the DataContext
    ScheduleControl objScheduleControl = (ScheduleControl)d;
    IContentItem contentItem = (IContentItem)objScheduleControl.DataContext;
    if (contentItem.Details != null)
    {
        // Fill the Group drop down
        objScheduleControl.cmbGroup.Items.Add("None");
        objScheduleControl.cmbGroup.Items.Add("Category");
        objScheduleControl.cmbGroup.Items.Add("Resource");
        objScheduleControl.cmbGroup.Items.Add("Contact");
        objScheduleControl.cmbGroup.SelectedIndex = 0;
        
        // Fill the Views drop down
        objScheduleControl.cmbView.Items.Add("Day");
        objScheduleControl.cmbView.Items.Add("Working Week");
        objScheduleControl.cmbView.Items.Add("Week");
        objScheduleControl.cmbView.Items.Add("Month");
        objScheduleControl.cmbView.SelectedIndex = 3;
        
        // Set mapping between ContactStorage and Contact table columns
        BaseObjectMappingCollection<Contact> ContactMappings =
            objScheduleControl.sched1.DataStorage.ContactStorage.Mappings;
        ContactMappings.IndexMapping.MappingName = "Id";
        ContactMappings.IdMapping.MappingName = "GuidId";
        ContactMappings.CaptionMapping.MappingName = "Caption";
        ContactMappings.ColorMapping.MappingName = "Color";        
        ContactMappings.TextMapping.MappingName = "Text";
        objScheduleControl.sched1.DataStorage.ContactStorage.DataMember = "Contacts";
        objScheduleControl.sched1.DataStorage.ContactStorage.DataSource = contentItem.Screen;
        
        // Set mapping between ResourceStorage and Resource table columns
        BaseObjectMappingCollection<Resource> ResourceStorage =
            objScheduleControl.sched1.DataStorage.ResourceStorage.Mappings;
        ResourceStorage.IndexMapping.MappingName = "Id";
        ResourceStorage.IdMapping.MappingName = "GuidId";
        ResourceStorage.CaptionMapping.MappingName = "Caption";
        ResourceStorage.ColorMapping.MappingName = "Color";
        ResourceStorage.TextMapping.MappingName = "Text";
        objScheduleControl.sched1.DataStorage.ResourceStorage.DataMember = "Resources";
        objScheduleControl.sched1.DataStorage.ResourceStorage.DataSource = contentItem.Screen;
        
        // Set mapping between AppointmentStorage and Appointment table columns
        AppointmentMappingCollection AppointmentMappings = 
            objScheduleControl.sched1.DataStorage.AppointmentStorage.Mappings;
        AppointmentMappings.IndexMapping.MappingName = "Id";
        AppointmentMappings.IdMapping.MappingName = "GuidId";
        AppointmentMappings.Subject.MappingName = "Subject";
        AppointmentMappings.Body.MappingName = "Body";
        AppointmentMappings.End.MappingName = "TimeEnd";
        AppointmentMappings.Start.MappingName = "TimeStart";
        AppointmentMappings.Location.MappingName = "Location";
        AppointmentMappings.AppointmentProperties.MappingName = "Properties";
        
        objScheduleControl.sched1.DataStorage.AppointmentStorage.DataMember = "Appointments";
        objScheduleControl.sched1.DataStorage.AppointmentStorage.DataSource = 
                            contentItem.Screen;  
    }

请注意,我们还指明了每个数据源如何映射到 LightSwitch 中的实体

每个控件都不同,因此重要的概念是您可以获取 LightSwitch DataContext 的实例,检查它是否确实有数据,并绑定到您的 Silverlight 控件。

删除故障

对于复杂控件,您可能会遇到控件无法以预期方式与 LightSwitch 通信的问题,从而导致错误。当您尝试删除预约时会发生这种情况。

Scheduler 控件删除预约时,需要通过 GUID 定位记录,并在 LightSwitch 中明确删除它。

这是使用的代码

private void sched1_UserDeletingAppointment(object sender, 
            C1.Silverlight.Schedule.AppointmentActionEventArgs e)
{
    // Get a reference to the LightSwitch DataContext 
    var objDataContext = (IContentItem)this.DataContext;
    // Get a reference to the LightSwitch Screen
    var Screen =
                (Microsoft.LightSwitch.Client.IScreenObject)objDataContext.Screen;
    // Delete the appointment in LightSwitch
    Screen.Details.Dispatcher.BeginInvoke(() =>
    {
        string strGuid = Convert.ToString(e.Appointment.Key[0]);
        Guid DeletedGuidId = new Guid(strGuid);
        LightSwitchApplication.DataWorkspace DataWorkspace = 
            (Screen.Details.DataWorkspace as LightSwitchApplication.DataWorkspace);
        LightSwitchApplication.Appointment DeletedAppointment = 
            DataWorkspace.ApplicationData.Appointments
            .Where(x => x.GuidId == DeletedGuidId).FirstOrDefault();
        if (DeletedAppointment != null)
        {
            DeletedAppointment.Delete();
        }
    });
} 

无法初始化 EntityObject 类的新实例,因为环境 IDataWorkspace 不可用。请使用指定 EntitySet 的构造函数。

image

如果您因为收到上述错误而来到本文,解决方案是在 LightSwitch 屏幕代码后端(而不是在 Silverlight 控件本身)执行您尝试执行的任何操作。

image

我们执行的第一步是在 Silverlight 控件中添加一个 public 属性,这将允许 LightSwitch 屏幕代码以编程方式访问 Silverlight 控件

然后我们在 LightSwitch 屏幕代码中使用以下内容

using System;
using Microsoft.LightSwitch.Presentation;
using Microsoft.LightSwitch.Presentation.Extensions;
using SilverlightLibrary;
namespace LightSwitchApplication
{
    public partial class Calendar 
    {
        // The following methods are by Raleigh Johnson 
        // http://ComponentOne.com
        partial void Calendar_Created()
        {
            // Get an instance of the ComponentOne Scheduler Control
            IContentItemProxy schedProxy = this.FindControl("Appointments");
            // Create a handler to fire when the control is actually available            
            schedProxy.ControlAvailable +=
   new EventHandler<ControlAvailableEventArgs>(schedProxy_ControlAvailable);
        }

        void schedProxy_ControlAvailable(object sender, ControlAvailableEventArgs e)
        {
            // Get an instance of the ComponentOne Scheduler Control
            ScheduleControl sc = e.Control as ScheduleControl;
            // Create an event that will fire when an Appointment is being created
            sc.Scheduler.DataStorage.AppointmentStorage.AddingNew += (obj, args) =>
            {
                // Create a new Appointment in LightSwitch
                Appointment app = new Appointment
                (this.DataWorkspace.ApplicationData.Appointments);
                // Set the NewObject in the ComponentOne Scheduler Control 
                args.NewObject = app;
            };
            // Create an event that will fire when a Contact is being created
            sc.Scheduler.DataStorage.ContactStorage.AddingNew += (obj, args) =>
            {
                // Create a new Contact in LightSwitch
                Contact app = new Contact(this.DataWorkspace.ApplicationData.Contacts);
                // Set the NewObject in the ComponentOne Scheduler Control 
                args.NewObject = app;
            };
            // Create an event that will fire when a Resource is being created
            sc.Scheduler.DataStorage.ResourceStorage.AddingNew += (obj, args) =>
            {
                // Create a new Resource in LightSwitch
                Resource app = new Resource(this.DataWorkspace.ApplicationData.Resources);
                // Set the NewObject in the ComponentOne Scheduler Control 
                args.NewObject = app;
            };
        }
    }
}

大多数控件不会这么难

我决定攻克这个控件,因为我知道它会很难。我的希望是证明 LightSwitch 能够处理复杂的业务应用程序。在普通 Silverlight 应用程序中可以完成的任何事情都可以在 LightSwitch 中完成。

特别感谢

没有 ComponentOne 的帮助,本文是不可能完成的。

特别感谢 ComponentOne 的 Raleigh Johnson 提供了正确创建新预约所需的代码。

我还想感谢 ComponentOne 的 Rich Dudley。

更多 LightSwitch Scheduler 控件

Paul Patterson 在此处提供了有关使用 Telerik Scheduler 的教程

更多 LightSwitch 教程请访问 LightSwitchHelpWebsite.com

http://LightSwitchHelpWebsite.com

© . All rights reserved.