Windows 运行时和 C++

将桌面应用程序迁移到 Windows 运行时

Diego Dagum

下载代码示例

Windows 8 体现了新的设计理念,为 Microsoft 平台。 在 Windows 8,您可以创建使用 XAML 和 HTML5 等用户界面技术的应用程序。 Microsoft 提供了两种新的应用程序模型:Windows 运行时库 (WRL),它可以帮助您开发 Windows 存储应用程序与 C#、 Visual Basic 和 c + + 和 Windows 库中的 JavaScript (WinJS),这使您可以创建 HTML5 和 JavaScript 的应用程序。

WRL 是 Windows 8 的 Microsoft 基础类框架 (MFC) 或类似 C 的 Win32 Api 哪些对桌面环境。 因此,现有的台式机应用程序必须调整,以根据 Windows 运行时运行。 困境都带有很大程度取决于 MFC、 Win32 或其他应用程序框架的应用程序。 如果他们要移植到 Windows 运行时,怎样与他们工作? 此后会发生什么? 是有必要保持两者基本代码 — — 平板电脑和桌面吗?

在本文中,我将展示你如何识别和从应用程序代码库中提取大量的部分和两个环境之间共享它们。 您将看到如何此重构的活动也是一个机会,可以利用一些新的 C + + 11 功能对精简和可维护性。 好处不仅仅正在获得新的 Windows 存储版本的现有应用程序 ; 以及升级现有的应用程序代码库。

可移植性和其困境

它总是更容易生成可移植性和其他非功能性要求从零开始,因此它构建的应用程序。 在实践中,不过,应用程序开发人员经常受到意外出现后已部署应用程序的需要。 满足这些以后需要可能证明有问题,如果应用程序与生俱来的方式可以使新功能难以实现,而重写很大一部分。 如果不仔细测试,则新的部件最终会导致在生产中应用破损。

因此,我决定作为示例使用现有的应用程序,而不是创建自己的演示。 正如您所看到的我选择了由 Microsoft Visual Studio 2005 出版 MFC 计算器示例 (bit.ly/OO494I)。

重写整个应用程序的选项似乎有吸引力起初,因为你想要摆脱代码您不想维护 — — 将从零开始,而是恢复,但这次做得很好。 但不能确信管理,因为它侵蚀了投资回报 (率 ROI) 从原始应用程序中,除非该应用程序将停留在生产为其平台不能运行新的应用程序的用户。 如果这种情况,两个相似基本代码将需要维护,增加成本 (两倍的工作 — — 或更多 — — 以实施新的功能或修复 bug,例如)。

不可能所有的原始代码可以在不同的环境 (Windows 桌面和 Windows 运行时,在这种情况下) 中重复使用。 然而,越多,可以共享,低成本,因此,更高的利润。

回到基础知识:关注点分离

(Soc) 的分离是既定的概念今天,许多软件体系结构书中发表。 其自然的后果之一是 API 相关代码紧密地进行分组 (更不用说隐藏) 到 well-segmented 的组件,其余的抽象接口。 因此,一个具体数据存储库是永远不会显式遭受执行域逻辑、 表示逻辑等等的代码。 这些组件只是"交谈"到的抽象接口。

SoC 开发人员之间如今被广泛采用。 由于 Web 发生爆炸,开始在九十年代后期,许多独立应用程序然后派跨层和层的模块中爆发了。

如果您有没有考虑 SoC 开发的应用程序,您可以将它们置于现代世界的重构。 重构是健康的做法,现在执行由于广泛采用灵活的做法,促进建设可以可能是工作的最简单的事情,获得通过的所有测试和最大化软件吞吐量,时间到市场等等。 然而,灵活性不能留下大启用新的渠道,直到这将成为必要的余地。 我们在这里,然后。

一个典型的移植方案

我刚才基本计算器的示例 MFC 应用程序中显示图 1

The Microsoft MFC Calculator
图 1 微软 MFC 计算器

此示例是伟大来说明移植过程,原因如下:

  • 它是足够小,您可以得到些什么的整体概念。
  • 它是足够大,可以让我详细显示的过程。 平均应用程序可能会较大的基本代码,但移植将包含重复的我将在这里描述的步骤。
  • 代码耦合不足以显示通过重构脱钩。 它可能故意加上保持基本代码紧凑和易于理解。 就会减弱经济的波动的代码,直到我得到一个共同的基本代码所使用的 MFC 和 Windows 8 的版本。 我不会脱钩进一步,但我会建议如何更多代码可以解耦。

原始的计算器应用程序包含两个类:CCalc­应用程序和 CCalcDlg。 CCalcApp 当正在运行的进程的 InitInstance 函数实例化 CCalcDlg 作为模型计算器 (见图 2)。 CCalcDlg 模特主窗口 (面板和按钮) 的控件及其关联的事件。

Original Calculator Sample Class Diagram Showing Essentials
图 2 原始计算器示例类关系图中显示要点

从 MFC CDialog CCalcDlg 派生的并且其执行不会从其基本的窗口消息,其职能和执行情况作为响应这些事件触发的计算器逻辑映射中的一切。 图 3 显示单击等号按钮时,会发生什么 (大概是两个操作数和操作符的输入后)。 图 4 显示所有级别添加行为的 CCalcDlg 函数:事件反应、 域逻辑和演示文稿。

Sequence Diagram for an Event—Clicking the Equal Sign Button
图 3 序列图的事件 — — 单击等号按钮

Figure 4 CCalcDlg
// CCalcDlg.cpp
// Window messages trigger CCalcDlg function invocations
BEGIN_MESSAGE_MAP(CCalcDlg, CDialog)
  ON_WM_PAINT()
  ON_COMMAND_RANGE(IDB_0, IDB_9, OnClickedNumber)
  ON_BN_CLICKED(IDB_CLEAR, OnClickedClear)
  ON_BN_CLICKED(IDB_DIVIDE, OnClickedDivide)
  ON_BN_CLICKED(IDB_EQUAL, OnClickedEqual)
  ON_BN_CLICKED(IDB_MINUS, OnClickedMinus)
  ON_BN_CLICKED(IDB_PLUS, OnClickedPlus)
  ON_BN_CLICKED(IDB_TIMES, OnClickedTimes)
  ON_EN_SETFOCUS(IDE_ACCUM, OnSetFocusAccum)
END_MESSAGE_MAP()
 
...
// Event reaction
void CCalcDlg::OnClickedEqual() {
  PerformOperation();
  m_operator = OpNone;
}
 
// Domain logic
void CCalcDlg::PerformOperation() {
  if (m_bOperandAvail) {
    if (m_operator == OpNone)
      m_accum = m_operand;
    else if (m_operator == OpMultiply)
      m_accum *= m_operand;
    else if (m_operator == OpDivide) {
      if (m_operand == 0)
        m_errorState = ErrDivideByZero;
      else
        m_accum /= m_operand;
      }
    else if (m_operator == OpAdd)
      m_accum += m_operand;
    else if (m_operator == OpSubtract)
      m_accum -= m_operand;
  }
 
  m_bOperandAvail = FALSE;
  UpdateDisplay();
}
 
// Presentation logic
void CCalcDlg::UpdateDisplay() {
  CString str;
  if (m_errorState != ErrNone)
    str.LoadString(IDS_ERROR);
  else {
    long lVal = (m_bOperandAvail) ?
m_operand : m_accum;
    str.Format(_T("%ld"), lVal);
  }
  GetDlgItem(IDE_ACCUM)->SetWindowText(str);
}

因为 CCalcDlg 绑在 MFC,是有我想要在 Windows 存储版本的应用程序中使用的逻辑,不能移植过来。 我要做一些重构。

重构以解耦可重用的组件

要创建此计算器 Windows 存储区版本,我不需要重新编码的一切。 很多的行为,如所示图 4,可以重复使用,如果它不那么挂钩基于 MFC 的 CCalcDlg。 我将重构可重用部件 (在本例中的计算器行为) 被隔离,不执行特定的组件的方式应用。

我会认为你不只听说模型-视图-控制器 (MVC) 的体系结构模式,但是还需要应用它。 我只会重述了这里的规律:模型包含的域对象 (既无国籍和不) 并不知道或不关心视图技术。 视图被实施的一些用户交互技术 (HTML,Qt,MFC,可可,除其他外的),适合于应用程序的设备。 它不知道域逻辑如何执行的 ; 它的功能只是显示域数据结构或其中一部分。 控制器充当中间人,捕获用户输入来触发操作在域中,这会导致视图进行更新以反映新的地位。

MVC 广为人知,但它不是隔离域中的用户界面的唯一方法。 在计算器的情况下就会依靠那称为演示文稿模型 MVC 变异 (bit.ly/1187Bk)。 我最初认为模型-视图-ViewModel (MVVM),是受 Microsoft.NET 框架软件开发人员的另一种变化。 演示文稿模型虽然是这种情况下,更适合。 当您想要实现一种新的用户交互技术 (Windows 8,在这种情况下),但不不进行任何更改的用户界面行为作出时,演示文稿模型是理想。 这种模式仍然认为模型和视图作为前,但控制器作用发挥的抽象表示形式的命名表示模型的视图。 此组件实现共同行为的视图,包括其状态,而不考虑该视图技术的一部分。

图 5 描述 MFC 应用程序修改的版本。

The Namespace Calculator::View Consolidates the View Behavior in Its Associated Presentation Model
图 5 Namespace Calculator::View 整合视图及其关联的演示文稿模型中的行为方式

CalculatorPresentationModel 保留 (建模为接口 ICalculatorView),在视图的引用,因为一旦确定视图状态已更改,它将调用 UpdateDisplay 函数。 在 MFC 示例中,该视图是 CCalcDlg 本身,因为那是直接处理 MFC 的类。

CCalcDlg 在其构造函数中创建演示文稿及其模型:

CCalcDlg::CCalcDlg(CWnd* pParent) 
  : CDialog(CCalcDlg::IDD, pParent)
{
  presentationModel_ = 
    unique_ptr<CalculatorPresentationModel>(
    new CalculatorPresentationModel(this));
  ...
}

因为你可以看到,利用了一个 C + + 11 智能指针在这里称为 unique_ptr (见 bit.ly/KswVGy 的详细信息)。 智能指针有释放时它已不再需要它们引用的对象的能力。 我用这里的智能指针来确保演示文稿模型摧毁查看生命周期结束时。 视图保持捕获窗口事件,如中所示将下放给演示文稿模型或不按摩,输入图 6

图 6 显示代表团的某些视图功能

// The parameter nID contains the ASCII code of a digit
void CCalcDlg::OnClickedNumber(UINT nID) {
  ASSERT(nID >= IDB_0 && nID <= IDB_9);
  presentationModel_->ClickedNumber(nID - IDB_0);
}
 
// Unchanged delegation
void CCalcDlg::OnClickedClear() {
  presentationModel_->ClickedClear();
}
 
enum Operator { OpNone, OpAdd, OpSubtract, OpMultiply, OpDivide };
 
// The Presentation Model contains a single method for all binary operations
void CCalcDlg::OnClickedDivide() {
  presentationModel_->ClickedOperator(OpDivide);
}
 
void CalculatorPresentationModel::ClickedOperator(Operator oper) {
  // PerformOperation is now in the PresentationModel;
  // it was in CCalcDlg (now being "the View")
  PerformOperation();
  m_operator = oper;
}
 
void CalculatorPresentationModel::PerformOperation() {
  if (m_errorState != ErrNone)
    return;
 
  if (m_bOperandAvail) {
    if (m_operator == OpNone)
      m_accum = m_operand;
    else if (m_operator == OpMultiply)
      m_accum *= m_operand;
  ...
// Same as in Figure 4
 
  m_bOperandAvail = false;
  // This is an inline function defined just below
  UpdateDisplay();
}
 
// The UI refresh is deferred back to the actual View
inline void CalculatorPresentationModel::UpdateDisplay() {
  if (view_)
    view_->UpdateDisplay();
}

可下载的同伴样品中的 mfccalc 文件夹中,您会发现此改写的 MFC 示例文本。 您可能会注意到没有任何模式。 在此示例中,域逻辑将已经包含四个基本的算术运算,有点像什么所示的函数的类图 7

图 7 纯实现,其中包括一个计算器"模式"

// Namespace Calculator::Model
class CalculatorModel {
public:
  long Add(const long op1, const long op2) const {
    return op1+op2;
  }
 
  long Subtract(const long op1, const long op2) const {
    return op1-op2;
  }
 
  long Multiply(const long op1, const long op2) const {
    return op1*op2;
  }
 
  long Divide(const long op1, const long op2) const {
    if (operand2)
      return operand1/operand2;
    else
      throw std::invalid_argument("Divisor can't be zero.");  }
};
 
// Namespace Calculator::View
class CalculatorPresentationModel {
public:
  ...
void PerformOperation();
  ...
private:
  // The Presentation Model contains a reference to a Model
  unique_ptr<Model::CalculatorModel> model_;
  ...
}
 
void CalculatorPresentationModel::PerformOperation()
{
  if (m_errorState != ErrNone)
    return;
 
  // Same like before, but this time the PresentationModel asks
  // the model to execute domain activities instead of doing it itself
  if (m_bOperandAvail) {
    if (m_operator == OpNone)
      m_accum = m_operand;
    else if (m_operator == OpMultiply)
      m_accum = model_->Multiply(m_accum, m_operand);
    else if (m_operator == OpDivide) {
      if (m_operand == 0)
        m_errorState = ErrDivideByZero;
      else
        m_accum = model_->Divide(m_accum, m_operand);
    }
    else if (m_operator == OpAdd)
      m_accum = model_->Add(m_accum, m_operand);
    else if (m_operator == OpSubtract)
      m_accum = model_->Subtract(m_accum, m_operand);
  }
 
  m_bOperandAvail = false;
  UpdateDisplay();
}

我决定要跳过在这个小的情况下,离开其逻辑演示文稿模型内的模型。 这将不是在大多数情况下,典型和模型逻辑将不得不在其自己的类或一组类中实现。 如果您愿意,您可以作为练习重构朝着纯粹方法示例。 如果您这样做,您必须特别注意时执行 CalculatorModel::Divide 函数,因为该模型可重用的组件可能会调用一个自动化的过程,而不是某些用户交互的。 在这种情况下,那没关系除数为零将引发异常 ; 在该点将意外的情况。 但是,您不必从演示文稿模型中删除非零除数检查。 防止转发到内部层错误数据始终是健康的。 没有其他可用时,模型所引发的异常是最后的措施。

前进和同伴在运行重构的解决方案 mfccalc,代码和它仍然那样,之前,这是我想要的通知:设置端口的代码而不会丢失功能。 严重的重构过程必须考虑一套自动测试以确认应用程序的行为并没有因此受到影响。 本机单元测试是在 Visual Studio 2012 和所有开发人员版,包括自由表达的 Windows 8 (其中不虽然来与 MFC 或桌面开发其他框架) 中可用。

看看在同伴的代码中的 Calculator\CalculatorTests 解决方案。 Calculator::Testing 命名空间包含一个类的方法测试那些 CalculatorPresentationModel,PresentationModelTest,如中所示图 8

图 8 隔离与本机单元测试错误

// Namespace Calculator::Testing
TEST_CLASS(PresentationModelTest) {
private:
  CalculatorPresentationModel presentationModel_;
public:
  TEST_METHOD_INITIALIZE(TestInit) {
    presentationModel_.ClickedClear();
  }
 
  TEST_METHOD(TestDivide) {
    // 784 / 324 = 2 (integer division)
    presentationModel_.ClickedNumber(7);
    presentationModel_.ClickedNumber(8);
    presentationModel_.ClickedNumber(4);
 
    presentationModel_.ClickedOperator(OpDivide);
 
    presentationModel_.ClickedNumber(3);
    presentationModel_.ClickedNumber(2);
    presentationModel_.ClickedNumber(4);
 
    presentationModel_.ClickedOperator(OpNone);
 
    Assert::AreEqual<long>(2, presentationModel_.GetAccum(),
      L"Divide operation leads to wrong result.");
    Assert::AreEqual<CalcError>(ErrNone, 
      presentationModel_.GetErrorState(),
      L"Divide operation ends with wrong error state.");
  }
 
  TEST_METHOD(TestDivideByZero) {
    // 784 / 0 => ErrDivideByZero
    presentationModel_.ClickedNumber(7);
    presentationModel_.ClickedNumber(8);
    presentationModel_.ClickedNumber(4);
 
    presentationModel_.ClickedOperator(OpDivide);
 
    presentationModel_.ClickedNumber(0);
 
    presentationModel_.ClickedOperator(OpNone);
 
    Assert::AreEqual<CalcError>(ErrDivideByZero, 
      presentationModel_.GetErrorState(),
      L"Divide by zero doesn't end with error state.");
  }
 
  ...
// More tests for the remaining calculator operations
};

顺便说一句,单元测试是最赞赏后果的此演示文稿模型模式之一:用户界面行为测试自动化是相同的在此示例中,我嵌入在演示文稿模型中的域逻辑。 因此,您可以打开新的渠道向您的应用程序 (例如,可可、 Qt、 包含或甚至非母语的 HTML5 或 Windows 演示文稿基金会等) 肯定地该错误,如果任何,正在发生的新的用户交互组件,而不是现有的。 您可以利用,以确保所有代码行进行至少一次都测试的代码覆盖率功能。

打开计算器应用程序的 XAML 正面

使用重构代码,我准备好新的 Windows 用户界面创建 MFC 计算器。 它只是创建新视图的前面所述的演示文稿的模式。

Windows 8 提供了三种技术:XAML、 HTML 和 DirectX。

  • XAML 是基于 XML 的标记语言,它允许您视觉 UI 元素,数据绑定的 UI 控件和事件处理程序调用中响应事件的声明。 这些事件通常可以在扩展 c + + 语法中称为 c + + 组件扩展为 Windows 运行时创建的所谓代码隐藏组件中定义 (C + + / CX),或在其他编程语言中创建。 我将介绍基于 XAML 的计算器的脸。
  • HTML 允许您设置在 Java 中定义哪些用户界面行为­"脉轮"互联网资源管理器中运行的脚本引擎。 有可能 — — 如我将在下一节演示 — — 要调用的 JavaScript 代码从基于 c + + 的组件。
  • DirectX 是多媒体密集型应用程序的理想选择。 计算器不是多媒体的应用程序,因为我不会在这里对此进行讨论。 DirectX 应用程序可以通过互操作,你可以阅读更多有关在使用 XAML bit.ly/NeUhO4

若要创建 XAML 视图,我从 Visual c + + Windows 8 的模板列表中选择基本空白应用程序并创建一个叫做 XamlCalc 的项目。

此空白模板包含只空 MainPage.xaml,而我会填充控件,从而使 Windows 8 对应的前 CCalcDlg 控件中的 MFC 版本。 这是所有所需端口的计算器应用程序,因为它包含一个单一窗口只,不带导航。 当移植您的应用程序,您可能会考虑其他模板,以便您可以提供一个页面导航机制,提供了直观和可预测的体验 您可以找到有关这"设计 UX 的程序"页上的指导 (bit.ly/Izbxky)。

空白模板还附带 App.xaml 文件,目的在类似于 MFC 版本中的类 CCalcApp (见图 2)。 它是引导加载程序初始化其余组件,并将控制传递给他们。 CCalcApp::InitInstance 函数创建一个 CCalcDlg 窗口,然后作为一个用户界面。 (你会发现所有这些 XamlCalc 解决方案中可下载的同伴的代码中。)在 XAML 案例中,App::OnLaunched 生成默认情况下,在代码隐藏源文件中,App.xaml.cpp,并触发到首页的初始导航:

void App::OnLaunched(
  Windows::ApplicationModel::Activation::LaunchActivatedEventArgs^ pArgs) {
  ...
// Create a Frame to act navigation context and navigate to the first page
  auto rootFrame = ref new Frame();
  if (!rootFrame->Navigate(TypeName(MainPage::typeid))) {
    throw ref new FailureException("Failed to create initial page");
  }
  ...
}

使用 Visual Studio 内置 XAML 编辑器创建沉浸式计算器页,通过从工具箱中拖动控件并完成一些手动编辑,如用户定义的样式,数据绑定,关联的事件等等。 由此产生的 XAML 看起来像中的代码图 9。 计算器示图 10

图 9 XAML 版本的 MFC 计算器

<Page
  Loaded="Page_Loaded"
  x:Class="XamlCalc.MainPage" ...>
 
  <Grid Background="Maroon">
    ...
<Border Grid.Row="1" Background="White" Margin="20,0">
      <TextBlock x:Name="display_" TextAlignment="Right" FontSize="90"
        Margin="0,0,20,0" Foreground="Maroon" HorizontalAlignment="Right"
        VerticalAlignment="Center"/>
    </Border>
    <Grid Grid.Row="2">
      ...
<Button Grid.Column="0" Style="{StaticResource Number}"
        Click="Number_Click">7</Button>
      <Button Grid.Column="1" Style="{StaticResource Number}"
        Click="Number_Click">8</Button>
      <Button Grid.Column="2" Style="{StaticResource Number}"
        Click="Number_Click">9</Button>
      <Button Grid.Column="3" Style="{StaticResource Operator}"
        Click="Plus_Click">+</Button>
    </Grid>
    ...
<Grid Grid.Row="5">
      ...
<Button Grid.Column="0" Style="{StaticResource Number}"
        Click="Number_Click">0</Button>
      <Button Grid.Column="1" Style="{StaticResource Operator}"
        Click="Clear_Click">C</Button>
      <Button x:Name="button_equal_" Grid.Column="2"
        Style="{StaticResource Operator}" Click="Equal_Click"
        KeyUp="Key_Press">=</Button>
      <Button Grid.Column="3" Style="{StaticResource Operator}"
        Click="Divide_Click">/</Button>
    </Grid>
  </Grid>
</Page>

The Look and Feel of the XAML Calculator
图 10 XAML 计算器的外观

所以我不需要重申颜色、 对齐、 字体和其他属性的每个按钮在 App.xaml,定义 (用于数字和运算符) 按钮的样式。 同样地,我关联到的每个按钮 ; 单击属性的事件处理程序 处理程序中的代码隐藏源文件 MainPage.xaml.cpp 定义的首页方法。 下面是几个例子,一个被单击号码,一个用于分割操作:

void MainPage::Number_Click(Platform::Object^ sender,
  Windows::UI::Xaml::RoutedEventArgs^ e)
{
  Button^ b = safe_cast<Button^>(sender);
  long nID = (safe_cast<String^>(b->Content)->Data())[0] - L'0';
  presentationModel_->ClickedNumber(nID);
}
 
void MainPage::Divide_Click(Platform::Object^ sender,
  Windows::UI::Xaml::RoutedEventArgs^ e)
{
  presentationModel_->ClickedOperator(OpDivide);
}

您可以看到,这些 C + + / CX 方法在首页中的只是采取事件信息,并把它给我的标准 c + + 类,CalculatorPresentationModel,来执行实际的 UI 活动的实例。 这表明,可能要考虑现有的本机应用程序中的标准 c + + 逻辑和重用它全新的 C + / CX Windows 存储应用程序。 这可重用性降低成本维持两个版本,因为他们既可以利用常见组件内的任何更新 — — 在这种情况下 CalculatorPresentationModel。

只要他们实施明确的抽象接口,可以通过通用组件回叫执行特定的组件。 在我的示例中,例如,计算器­PresentationModel::UpdateDisplay ICalculatorView 的实例委托实际作业:

inline void CalculatorPresentationModel::UpdateDisplay(void) {
  if (view_)
    view_->UpdateDisplay();
}

在 MFC 版本中,ICalculatorView 是由基于 MFC 的 CCalcDlg 类实现的。 看一看的重构的序列图中图 11 和比较中的原始与图 3

The Sequence Diagram for the Equal Sign Button in the Decoupled MFC Version
图 11 中解耦的 MFC 版本的等号按钮的序列图

要保留的 XAML 版本类似于 MFC 案例,我应实现 ICalculatorView 首页。 相反,我不得不实施 ICalculatorView 作为一个不同的类,因为首页是 C + + / CX 类,因此,不能从标准 c + + 类派生。 C + + 和其投影到 Windows 运行时 (C + + / CX) 有不同类型的系统 — — 它在任何情况下互操作很好地。 实现纯 c + + ICalculatorView 界面并不是一个大问题:

namespace XamlCalc {
  class CalcView : public ICalculatorView {
  public:
    CalcView() {}
    CalcView(MainPage^ page) : page_(page) {}
    inline void UpdateDisplay()
      { page_->UpdateDisplay(); }
  private:
    MainPage^ page_;
  };
}

标准 c + + 和 C + + / CX 类不能从彼此,但他们仍可持有互相引用 — — 在本例中私有成员 page_,这是引用 C + + / CX 首页。 若要更新显示控制 XAML 版中的,只是更改称为 display_ 的 MainPage.xaml TextBlock 控件的 Text 属性:

void MainPage::UpdateDisplay() {
  display_->Text = (presentationModel_->GetErrorState() != ErrNone) ?
L"ERROR!" :
    safe_cast<int64_t>(presentationModel_->GetValue()).ToString();
}

图 12 显示 XAML 计算器类关系图。

The XAML-C++/CX Calculator Application Class Diagram
图 12 XAML-C + + / CX 计算器应用程序类关系图

图 13 显示对应于在 XAML 版本中按等号按钮的操作序列。

The Sequence Diagram for the Equal Sign Button in the XAML version.
图 13 序列图中的 XAML 版本的等号按钮

我应该提及 WRL 大力提倡通过其 Api,可用于事件处理的所有异步处理。 我的代码虽然是同步的 100%。 我可以在异步通过使用基于任务的并行模式库,实现任务和基于 Windows 8 的异步概念的延续。 这不在我小的示例中,有道理,但还是值得阅读更多关于它在"异步编程在 c + +"页中,Windows 开发人员中心 (bit.ly/Mi84D1)。

按 f5 键并运行应用程序,查看中行动的 XAML 版本。 当迁移或创建您的 Windows 存储应用程序,是很重要的是你设计基于 Microsoft 开发中心中所述的新 Windows 体验设计模式的用户界面的应用程序 (bit.ly/Oxo3S9)。 按照指挥的推荐的模式、 触摸,翻转的方向、 魅力和以保持 UX 直观用户第一次为您应用程序的更多。

另一个示例:新的 Windows 用户界面 HTML 计算器

XAML 示例就足以让运行与 Windows 8 的初始基于 MFC 的计算器示例。 但是,在某些情况下 (如您的团队或利用现有资产的专门知识),您可能考虑 HTML 和 JavaScript 代替 XAML ui。

本文中描述的演示文稿模型设计模式是仍然有用,即使您的用户界面包含非 c + + 语言 (如 JavaScript 中的逻辑。 这一奇迹是可能的因为在 Windows 8 环境中,向 Windows 运行时也不是 c + + 的 JavaScript 项目并使两者进行互操作,他们都分享设立的 WRL 的类型系统。

在同伴的代码,你会发现一个称为 HtmlCalc,其中包含一个类似于 MainPage.xaml 的 default.html 页的解决方案。 图 14 显示用户界面说明媲美中显示的 XAML 版本图 9

图 14 计算器 UI 的 HTML 标记

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>HTMLCalc</title>
    <!-- WinJS references -->
    <link href="//Microsoft.WinJS.0.6/css/ui-dark.css" rel="stylesheet">
    <script src="//Microsoft.WinJS.0.6/js/base.js"></script>
    <script src="//Microsoft.WinJS.0.6/js/ui.js"></script>
    <!-- HTMLCalc references -->
    <link href="/css/default.css" rel="stylesheet">
    <script src="/js/default.js"></script>
  </head>
  <body onkeypress="Key_Press()">
    <table border="0">
      <tr>
        <td class="Display" colspan="7" id="display_">0 </td>
      </tr>
      <tr>
        <td>
          <button class="Number" onclick="Number_Click(7)">7</button>
        </td>
        <td>
          <button class="Number" onclick="Number_Click(8)">8</button>
        </td>
        <td>
          <button class="Number" onclick="Number_Click(9)">9</button>
        </td>
        <td>
          <button class="Operator" onclick="Plus_Click()">+</button>
        </td>
      </tr>
      ...
<tr>
        <td>
          <button class="Number" onclick="Number_Click(0)">0</button>
        </td>
        <td>
          <button class="Operator" onclick="Clear_Click()">C</button>
        </td>
        <td>
          <button class="Operator" onclick="Equal_Click()">=</button>
        </td>
        <td>
          <button class="Operator" onclick="Divide_Click()">/</button>
        </td>
      </tr>
    </table>
  </body>
</html>

在 HTML 页中的代码隐藏作用发挥的 JavaScript 代码中。 事实上,你会发现这样的代码的文件 js\default.js。 我的计算器­PresentationModel,因为它是一个标准的 c + + 类,不能直接从调用的 JavaScript 部分,但您可以做间接通过桥接 C + + / CX 组件 — — Calculator::View::CalcView。

实例化此组件从 JavaScript 非常简单,宣布在 default.js 以下:

// This is JavaScript code, instancing a C++/CX proxy to my PresentationModel
var nativeBridge = new Calculator.View.CalcView();

作为这种方法的示例,按等号按钮触发对以下的 JavaScript 函数的调用:

function Equal_Click() {
    display_.textContent = nativeBridge.equal_click();
}

这将传播到 CalcView::equal_click,"谈"以本机方式与我国标准 c + + CalculatorPresentationModel 调用:

String^ CalcView::equal_click() {
  presentationModel_->ClickedOperator(OpNone);
  return get_display();
}
 
String^ CalcView::get_display() {
  return (presentationModel_->GetErrorState() != ErrNone) ?
L"ERROR!" :
    safe_cast<int64_t>(presentationModel_->GetValue()).ToString();
}

在此特定情形,C + + / CX 组件 CalcView 只是将每个请求转发到标准 c + + PresentationModel (请参阅中的序列图图 15)。 虽然我们不能避免的可重复使用的 c + + 组件,去的路上 (见图 15)。

The Sequence Diagram for the Equal Sign Button in the Hybrid HTML-C++ Version
图 15 中的混合 HTML-c + + 版本的等号按钮的序列图

因为 C + + / CX 代理必须手动创建,相关的费用不应被忽视。 仍然,可以平衡它的重用组件,好处,我与 CalculatorPresentationModel 的方案中一样。

向前走,然后按 F5 见行动中的 HTML 版本。 我展示了如何重用现有 c + + 代码,以扩大其范围在 Windows 8 的新型框架不放弃原有的渠道 (MFC 以我为例)。 我们现在准备最后的几点思考。

你好 (真实) 世界 !!

我国移植方案是种特定的情况下,这可能不是您的具体情形,这可能不是别人的个别情况。 我在这里展示的是适用于 MFC 计算器方案,和我可能会做出不同的决定,如果我被移植到 WRL 不同的应用程序。 因此,这里有一些一般性的结论,有关移植应用程序:

  • 标准平原对象 — — 那些具有与第三方的 Api 没有特定关系 — — 有最大的可重用性,因此,无或低成本的可移植性。 相比之下,可重用性约束时对象具有显式关系的非标准的 Api,MFC、 Qt 和 WRL 等。 例如,MFC 是仅在 Windows 桌面上可用。 Qt,另一方面,是目前在其他环境中,虽然不是在所有。 在这种情况下,避免削弱使"交谈",抽象类的应用程序对象的可重用性的混合物。 然后从这些类创建第三方认识到实现派生。 看看我做与 ICalculatorView (抽象类) 和其实现 CCalcDlg 和 XamlCalc::CalcView。 为 Windows 8 的发展,你要熟悉的 WRL Api,和他们取代的 Win32 Api。 你会发现更多的资料 bit.ly/IBKphR
  • 因为我的目标是要模仿在 Windows 运行时我已有的桌面应用演示文稿的模式。 您可能决定削减功能,如果他们不做太大意义 — — 如将样式应用于移动电子邮件应用程序中的文本。 或者您可能添加功能,充分利用新的目标平台 — — 想,例如,想通过图像查看器应用程序中的多点触控拉伸图像。 在这种情况下,另一种设计模式可能更合适。
  • 我使用的演示文稿模型是伟大保持业务连续性和维修成本低。 这让我与客户的首选原始 MFC 选项传送未经藕断丝连 app Windows 存储区版本。 保持两个渠道 (XAML 和 MFC 或 HTML 和 MFC) 不是费用的两倍,只要我有可重用的组件,如 CalculatorPresentationModel。
  • 总体可重用性应用程序是由通用的所有版本与维护 (由第三方维护的组件不会被视为) 的特定版本的代码行的代码行的比率确定的。 有应用程序严重依赖非标准 Api (如利用了 OpenGL 和 iOS 传感器的增强现实应用程序) 的情况。 可重用性比可如此之低你可能会最终决定端口没有组件可重用性概念以外的应用程序。
  • 不要开始问谁能得这么差设计现有的应用程序,使您的移植工作如此困难。 启动重构它相反。 请记住敏捷方法不目的是成熟、 稳健、 高度可重用的体系结构 ; 他们的强调是软件交付。 泛型和可扩展未来可重用性和可移植性制作软件需要很多的经验,,因为它不容易使设计决策在黑暗中。
  • 您可能移植到 Windows 8、 iOS 或 Android 应用程序打算卖掉它通过这些平台的市场。 在这种情况下,请记住,您的应用程序必须通过认证过程,正在接受之前 (bit.ly/L0sY9i)。 这可能会迫使你支持你从未考虑过原始版本 (如触摸首先,魅力等等) 中的用户界面行为。 未能符合某些标准可能导致您的应用程序被拒绝。 不要忽视这种"法规遵从性"当成本估算。

迎接挑战

新的 Windows 运行时和仍然无处不在的 Windows 桌面构成挑战的开发人员不需要支付额外的维护单独的应用程序,每个平台的成本。 在本文中,我演示了可以利用现有的代码库,不仅以启用新的渠道,但也改善通过重构质量的现有基本代码。

迭戈 Dagum 是一种软件架构师和教练机 20 多年的行业经验。他可以达成 email@diegodagum.com

衷心感谢以下技术专家对本文的审阅: 马吕斯 Bancila、 天使耶稣埃尔南德斯和 Windows 8 的开发团队