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 桌面構成挑戰的開發人員不需要支付額外的維護單獨的應用程式,每個平臺的成本。 在本文中,我演示了可以利用現有的代碼庫,不僅以啟用新的管道,但也改善通過重構品質的現有基本代碼。

Diego Dagum 是一種軟體架構師和教練機 20 多年的行業經驗。他可以達成 email@diegodagum.com

由於下面的技術專家對本文的審閱: 馬呂斯 Bancila、 天使耶穌埃爾南德斯和 Windows 8 的開發團隊