在 Windows Phone 7 上处理日期





0/5 (0投票)
本文介绍了各种数据编辑器,所有这些编辑器都是借助 Resco 的 MobileForms Toolkit 构建的。
本文介绍了各种数据编辑器,所有这些编辑器都是借助 Resco 的 MobileForms Toolkit 构建的。
引言
Windows Phone 7 (WP7) 编程结合了多种技术
- .NET (WP7 使用了完整 .NET 类库的一个子集。)
- C#
- Silverlight for WP7。(桌面 SVL 的简化版。)
对于一位从 (例如) Windows Mobile 编程转过来的程序员来说,精通这些技术是一个真正的挑战。本文将展示如何使用 Resco 的 MobileForms Toolkit 来处理日期。
必备组件
目标读者是具备 Silverlight 阅读能力的 C# 程序员。至少,读者应该理解基本的 Silverlight 控件和布局。
您需要安装 Windows Phone Developer Tools。
此外,您应该从 http://www.resco.net/developer/mobileformstoolkit/ 安装 Resco MobileForms Toolkit, Windows Phone 7 Edition。该工具包包含一套有用的控件,可以简化 Windows Phone 7 编程。
简单用例
上面的截图是所有 Windows Phone 7 用户都非常熟悉的。是的,就是那个著名的日期/时间选择器。
MobileForms Toolkit (目前) 不直接提供对这些选择器的访问,而是将它们打包成几个更高级别的控件。以下截图展示了其中三个控件的实际应用。
这是相应的 XAML 代码
xmlns:r="clr-namespace:Resco.Controls;assembly=Resco.Controls"
…
<r:DateEditor Text="{Binding Path=ModifiedOn}"
TextDecoration="Underline" FormatString="{}{0:D}" />1
<r:TimeEditor Text="{Binding Path=ModifiedOn}"
FormatString="{}Modified on {0:t}" />
<r:DetailItemDateTime Label="Last Transaction" DataMember="ModifiedOn" />
<r:DetailItemDateTime Label="Last Transaction"
DataMember="ModifiedOn" TimeVisibility="Collapsed" />
DataContext 被设置为一个具有属性的对象
public DateTime ModifiedOn { get; set; }
所有控件都充当该属性的编辑器。(效果是通过绑定实现的。)
DateEditor/TimeEditor 是文本控件,点击后会打开相应的选择器。上面的代码演示了自定义格式;通常情况下您不会使用它。
DetailItemDateTime 使用所有 DetailItem 派生类 2 的典型外观,即 (可选) 标签和特定的外观与感觉。您可以将其设置为日期或时间编辑器 (使用 DateVisibility/TimeVisibility 属性),或者设置为完整的 DateTime 编辑器 (默认)。除了绑定,您还可以使用更简单的 DataMember 语法 3。您可以自定义日期和时间格式以及许多其他属性。
通过显式绑定
3) DataMember 指的是要在类代码中创建的用于绑定的属性名称。在 .NET 世界中,这种术语通常用于与数据相关的控件。实际上,我们可以替换
DataMember="ModifiedOn"
by explicit binding
Value={Binding Path=ModifiedOn, Mode=TwoWay}
关于基本的日期控件就讲到这里。未来会根据用户请求添加更多类似的控件。本文的其余部分将演示另一个控件 – MonthCalendar 的一些有趣用法。
使用 MonthCalendar 选择日期
MonthCalendar 是一个相当高级的控件。它的任务是示意性地呈现一系列约会。然而,我们现在只需要能够选择单个日期。没有约会 4,只需要对选择的响应。
UI 如下所示
MonthCalendar 控件位于底部。您可以列出单个月份并选择合适的日期。
顶部的 TextBlock 将反映当前的选择。这只是为了证明概念。您的布局可能会有所不同 – 例如,带有确定/取消按钮的弹出对话框。
重要的代码几乎微不足道
// Excerpt from MonthCalendarPage.xaml
<StackPanel Orientation="Horizontal">
<r:PhoneTextControl Text="Selected date: />
<r:PhoneTextControl x:Name="m_selected" />
</StackPanel>
<r:MonthCalendar x:Name="m_calendar" Height="480" SelectionChanged="DayChanged"/>
// MonthCalendarPage.xaml.cs
// Calendar has null DataSource; we only react to the item selection.
public partial class MonthCalendarPage : PhoneApplicationPage
{
public MonthCalendarPage()
{
InitializeComponent();
}
private void DayChanged(object sender, EventArgs e)
{
DateTime? dt = m_calendar.SelectedDate;
if (dt != null)
m_selected.Text = m_calendar.SelectedDate.Value.ToString("d");
// NavigationService.GoBack();
// Also possible: Return to previous page
}
}
XAML 代码定义了 MonthCalendar 实例和一个提供日期选择反馈的文本字段。正如我们上面提到的——这只是一个概念证明。对于实际使用,您需要以某种方式将 MonthCalendarPage 打包到一个新的 DateEditor 类中。
- 它可以是一个 PhoneTextControl 派生类 (即一个具有格式化功能的 TextBlock),可以绑定到基于 DateTime 的属性。该控件将在 Popup 中打开 MonthCalendarPage,作为对 MouseLeftButtonDown 事件的响应。
- 或者,它可以是一个 HyperlinkButton 派生控件,能够绑定到基于 DateTime 的属性。此解决方案将使用 HyperlinkNavigation 替换 Popup。
这两种解决方案都需要额外的非平凡的编程。直接使用 MonthCalendarPage 稍微容易一些。(NavigationService.GoBack()) 然而,您仍然需要处理编辑日期值的传输。
使用 MonthCalendar 的日期多选器
这是另一个有趣的例子——使用 MonthCalendar 控件来选择多个日期。该控件允许您逐月翻页并选择/取消选择单个日期。页面顶部显示了当前选择的简要摘要。
日历实现为 1 天事件的集合;事件由 DateTime 对象表示 5。除了 ICalendarDataSource 实现,我们将提供两个方法
- AddOrRemove() 用作切换——将新日期添加到列表中,删除旧日期。
- ToString() 提供当前选择的简要摘要。
public class MyCalendar : ICalendarDataSource
{
// An event is represented by DateTime object
List<DateTime> m_list = new List<DateTime>();
// Return list of events between start-end dates
IEnumerable ICalendarDataSource.GetRange(DateTime start, DateTime end)
{
var list = new List<Object>();
foreach( DateTime d in m_list) {
if( start <= d && d <= end )
list.Add(d);
}
return list;
}
// Returns time interval describing given event
void ICalendarDataSource.GetElementRange(object elem,
out DateTime start, out DateTime end) {
// In our case the event itself describes the time interval
start = end = (DateTime)elem ;
}
// Reaction to the click
public void AddOrRemove(DateTime dt)
{
foreach (DateTime d in m_list) {
if (d == dt) {
m_list.Remove(d); // Old event is removed
return;
}
}
m_list.Add(dt); // No old event found => add a new event
}
// Used for presentation purposes
public override string ToString()
{
DateTime min = DateTime.MaxValue;
DateTime max = DateTime.MinValue;
foreach (DateTime d in m_list) {
if (min > d) min = d;
if (max < d max = d;
}
if (m_list.Count == 0)
return "Nothing; click some day(s)";
if (m_list.Count == 1)
return min.ToString("d");
else
return String.Format("{0} days; {1:d} - {2:d}", m_list.Count, min, max);
}
}
我们终于可以实现 MonthCalendarPage 了。
// C# code
public partial class MonthCalendarPage : PhoneApplicationPage
{
// We use static DataSource to provide continuity between
// multiple uses of MonthCalendarPage.
//Replace by the implementation that best suits to your needs.
static MyCalendar DataSource = new MyCalendar();
public ICalendarDataSource MyCalendar {
get { return DataSource; }
}
public MonthCalendarPage() {
DataContext = this;
InitializeComponent();
m_selected.Text = DataSource.ToString();
}
// SelectionChanged event handler
private void DayChanged(object sender, EventArgs e) {
DateTime? val = m_calendar.SelectedDate;
if (val != null) {
DateTime dt = val.Value;
DataSource.AddOrRemove(dt);
m_selected.Text = DataSource.ToString();
// A trick to force calendar refresh because
// theResetList method is private.
//m_calendar.ResetList();
// (Will be available in next release.)
DateTime x = m_calendar.Date;
// Just force Date change
m_calendar.Date = m_calendar.Date.AddDays(1);
// Revert the original ate value
m_calendar.Date = x;
}
}
}
// Excerpt from MonthCalendarPage.xaml
<StackPanel Grid.Row="1">
<StackPanel Orientation="Horizontal">
<r:PhoneTextControl Text="Selected date:" Margin="10" />
<r:PhoneTextControl x:Name="m_selected" />
</StackPanel>
<r:MonthCalendar x:Name="m_calendar" Height="480"
SelectionChanged="DayChanged"/>
</StackPanel>
StringToDayTimeConverter
您是否曾经想过日期如何输入到 Xaml 中?例如
<WeekCalendar Minimum="11/1/2010" Maximum="11/30/2011" Date="today" />
嗯,Silverlight 比 WPF 简单,但这却是许多你真正怀念 WPF 功能的例子之一。我们无法为 (例如) 系统类添加读取日期的能力,但如果代码在我们控制之下,那么我们所需要做的就是为我们想在 Xaml 中使用的属性定义合适的 TypeConverter 属性。
这是一个 WeekCalendar 控件如何定义 Xaml 代码中使用的属性的示例。
// The only added line
[TypeConverter(typeof(StringToDateTimeConverter))]
public DateTime Minimum { get; set; }
StringToDateTimeConverter 类是 Resco.Controls.Tools 命名空间的一部分。这个转换器的优点是它不仅处理大量的日期格式 (它基于 DateTime.TryParse()),还处理特殊字符串“today”和“Today”。
请注意,您无需为数值类型添加任何转换器,因为 Silverlight 会自动处理转换。
下一步
Resco 是一家在移动编程领域拥有悠久传统的公司,涵盖了多个平台以及终端用户应用程序和开发人员工具。该工具包——正如现在发布的——自第一个版本以来已经成熟了一些。然而,这仍然是一个早期开发阶段。在不久的将来,现有控件将得到增强,并会添加新控件。
任何反馈都欢迎——无论是在这里还是直接在 Resco 论坛上。
关于作者
Jan Slodicka。编程超过 30 年。涵盖了多个桌面平台和编程语言。自 2003 年以来一直为 Resco 从事移动技术工作——Palm OS、Windows Mobile,现在是 Windows Phone 7。
您可以通过以下方式联系我:jano (at) resco (dot) net。