Dieser Artikel wurde maschinell übersetzt.

Windows-Runtime und C++

Portieren von Desktopanwendungen auf Windows-Runtime

Diego Dagum

Windows 8 verkörpert eine neue Designphilosophie für Microsoft-Plattformen. Sie können in Windows 8 apps über UI-Technologien wie XAML und HTML5 erstellen. Microsoft stellt zwei neue app-Modelle: die Windows Runtime Library (WRL), das Ihnen hilft, entwickeln Windows Store Anwendungen mit c#, Visual Basic und C++ und der Windows-Library für JavaScript (WinJS), ermöglicht Ihnen das Erstellen von HTML5 und JavaScript-Anwendungen.

Die WRL sind auf Windows 8 im Rahmen der Microsoft Foundation Class (MFC) oder der C-ähnlichen Win32-APIs auf die desktop-Umgebung. Daher müssen bestehende desktop-Anwendungen für die Ausführung unter Windows Runtime angepasst werden. Das Dilemma kommt mit Anwendungen, die Win32, MFC oder anderen Anwendungsframeworks stark abhängen. Was passiert mit ihnen, wenn sie zu Windows Runtime portiert sind? Was passiert danach? Ist es notwendig, beide pflegen die CodeBase — Tablet und Desktop?

In diesem Artikel werde ich Ihnen zeigen, wie Sie identifizieren und extrahieren Sie wesentliche Teile aus der Anwendung-Codebasis und teilen Sie sie zwischen den beiden Umgebungen. Sie werden sehen, wie diese Umgestaltung Aktivität ist auch die Möglichkeit nutzen, einige neuen C ++ 11 Funktionen für Prägnanz und Wartbarkeit. Und die Vorteile sind mehr als nur eine neue Windows-Speicher-Version einer vorhandenen App gewinnt; die vorhandenen Anwendung-Codebasis wird ebenfalls aktualisiert.

Portabilität und seiner Dilemmata

Es ist immer einfacher zum Erstellen einer Anwendung mit Portabilität und anderen nicht-funktionalen Anforderungen aus dem nichts, es entsprechend zu strukturieren. In der Praxis sind jedoch Anwendungsentwickler oft durch unvorhergesehene Anforderungen gefordert, die entstehen, nachdem die Anwendung bereitgestellt wurde. Befriedigung braucht diese später erweisen verfügt problematisch, wenn die Anwendung in einer Weise ausgelegt ist, die neue macht schwer umzusetzen ohne große Teile neu zu schreiben. Die neuen Teile können schließlich zu einer Anwendung Bruch in der Produktion führen, wenn nicht sorgfältig getestet.

Aus diesem Grund habe ich beschlossen, als Beispiel eine vorhandene Anwendung verwenden, anstatt meine eigene Demo. Wie Sie sehen, ich entschied mich für ein MFC-Taschenrechner-Beispiel von Microsoft für Visual Studio 2005 veröffentlicht (bit.ly/OO494I).

Die Möglichkeit, dass die gesamte Anwendung scheint ansprechend auf den ersten, weil Sie wollen loswerden der Code nicht möchten pflegen — Sie würden es lieber von Grund auf wiederholen, aber dieses Mal tun es auch. Aber Management kann nicht überzeugt sein, weil es den Return on Investment (ROI) erwartet von der ursprünglichen Anwendung erodiert, es sei denn, diese app in der Produktion für Benutzer bleibt die neue Anwendung von deren Plattformen nicht ausgeführt werden können. Wenn dies der Fall ist, die zwei ähnliche CodeBase Willen müssen gepflegt werden, steigende Kosten (zweimal die Arbeit — oder mehr — implementiert neue Features oder Fehler zu beheben, zum Beispiel).

Es ist unwahrscheinlich, dass alle ursprünglichen Code in verschiedenen Umgebungen (Windows Desktop und Windows-Runtime, in diesem Fall) wiederverwendet werden kann. Jedoch mehr, die freigegeben werden kann, je niedriger die Kosten und folglich desto höher die Gewinne.

Zurück zu den Grundlagen: Trennung von Bereichen

Trennung von Bereichen (SoC) ist ein etabliertes Konzept heute in vielen Software-Architektur-Bücher veröffentlicht. Seine natürlichen Konsequenzen ist, dass die API-bezogenen Code zusammenhängend gruppiert ist, um (nicht zu versteckten sagen) in mächtigen Komponenten, die eine abstrakte Schnittstelle zum Rest zu bieten. Daher ist eine konkrete Daten-Repository nie explizit Code ausgesetzt, die Domänenlogik, Darstellungslogik und So weiter führt. Diese Komponenten reden"nur" auf die abstrakte Schnittstelle.

SoC ist heutzutage weit unter Entwicklern angenommen. Als Folge der Web-Explosion, die in den späten 90er Jahren begann, waren viele Einzelanwendungen in Modulen aufgebrochen, die dann in Schichten und Ebenen verteilt wurden.

Wenn Sie Anwendungen, die ohne Rücksicht auf SoC entwickelt haben, können Sie sie in die moderne Welt durch Umgestaltung bringen. Refactoring ist eine gesunde Praxis heutzutage Dank der Breite Akzeptanz von Agile Praktiken, die Förderung Gebäude die einfachsten Dinge, die möglicherweise funktionieren können, bekommen alle Tests bestanden und Maximierung Software-Durchsatz, Markteinführung und So weiter durchgeführt. Jedoch lassen nicht Beweglichkeit viel Raum für neue Kanäle zu aktivieren, bis dies notwendig wird. Hier sind wir dann.

Ein typisches Szenario der Portierung

Die MFC-Beispielanwendung, die bereits erwähnt, ein Standardrechner, zeigt sich an Abbildung 1.

The Microsoft MFC Calculator
Abbildung 1 des Microsoft MFC-Rechners

In diesem Beispiel ist toll, den Portierung Prozess aus den folgenden Gründen zu veranschaulichen:

  • Es ist klein genug, damit Sie erhalten die allgemeine Idee, was es tut.
  • Es ist groß genug, um mir zu zeigen den Prozess im Detail ermöglichen. Die durchschnittliche Anwendung wird wahrscheinlich eine größere Codebasis haben, aber die Portierung besteht aus einer Wiederholung der Schritte, die ich hier beschreiben werde.
  • Der Code gekoppelt ist genug, um die Entkopplung durch Umgestaltung zu zeigen. Es war wahrscheinlich absichtlich gekoppelt, um die Codebasis kompakt und verständlich zu halten. Ich werde zu entkoppeln der Code bis ich bekommen eine gemeinsame Codebasis, die von MFC und Windows 8 Versionen verwendet wird. Ich wird nicht weiter zu entkoppeln, aber ich werde vorschlagen, wie viel mehr der Code entkoppelt werden kann.

Die ursprünglichen Rechner-Anwendung enthält zwei Klassen: CCalc­App und CCalcDlg. CCalcApp Modelle den Rechner als einen laufenden Prozess, dessen InitInstance-Funktion ein CCalcDlg instanziiert (siehe Abbildung 2). CCalcDlg Modelle im Hauptfenster, seine Steuerelemente (Panel und Schaltflächen) und die zugeordneten Ereignisse.

Original Calculator Sample Class Diagram Showing Essentials
Abbildung 2 Original Calculator-Beispiel Klassendiagramm anzeigen Essentials

CCalcDlg, die von MFC-CDialog abgeleitet, und seine Umsetzung tut alles, von dessen grundlegende Zuordnung von Fenstermeldungen, seine Funktionen und die Implementierung der Rechnerlogik als Reaktion auf diese Ereignisse ausgelöst. Abbildung 3 zeigt, was passiert, wenn die Gleichheitszeichen-Taste geklickt wird (vermutlich nach zwei Operanden und einen Operator eingegeben wurden). Abbildung 4 zeigt die CCalcDlg-Funktionen, die Verhalten auf allen Ebenen hinzufügen: Ereignis-Reaktion, Domänenlogik und Präsentation.

Sequence Diagram for an Event—Clicking the Equal Sign Button
Abbildung 3-Sequenzdiagramm für ein Ereignis — die Gleichheitszeichen-Taste klicken

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);
}

Da CCalcDlg mit MFC verknüpft ist, kann nicht die Logik, die ich in der Windows-Speicher-Version der Anwendung verwenden möchten portiert werden, da ist. Ich muss einige Umgestaltung zu tun.

Umgestaltung um wiederverwendbare Komponenten zu entkoppeln

Erstellen Sie eine Windows Store-Version von dieser Rechner, brauche ich nicht alles Umkodieren. Viel von dem Verhalten, z. B. was, in gezeigt wird Abbildung 4, können wiederverwendet werden, wenn es nicht so auf die MFC-basierten CCalcDlg gebunden waren. Ich werde die Anwendung so umgestalten, dass wiederverwendbare Teile (Rechner Verhalten in diesem Fall) von implementierungsspezifischen Komponenten isoliert sind.

Es wird vorausgesetzt, Sie haben nicht nur über das Model-View-Controller (MVC)-Architektur-Muster gehört aber auch angewendet haben. Ich werde nur das Muster hier rekapitulieren: Das Modell besteht aus Domänenobjekte (beide Staatenlosen und nicht) und nicht wissen, oder kümmern uns um die View-Technologie. Die Ansicht ist in einige Benutzer-Interaktion-Technologie (HTML, Qt, MFC, Kakao, u.a.) implementiert, die für das Gerät Anwendung geeignet ist. Es weiß nicht, wie die Domänenlogik implementiert wird; seine Funktion ist nur zum Anzeigen von Domäne-Datenstrukturen oder Teil von ihnen. Der Controller fungiert als Vermittler zwischen durch die Erfassung von Benutzereingaben in Trigger-Aktionen in der Domäne, wodurch die Ansicht zu aktualisieren, um eine neuere wieder zu geben.

MVC ist weithin bekannt, aber es ist nicht die einzige Möglichkeit, die Benutzeroberfläche aus der Domäne zu isolieren. Im Szenario Rechner werde ich verlasse mich auf eine MVC-Variante, die als Presentation Model bezeichnet (bit.ly/1187Bk). Ich betrachtete zunächst Model-View-ViewModel (MVVM), eine weitere Variante, die bei Microsoft .NET Framework Entwicklern beliebt ist. Präsentationsmodell war eine bessere Passform für dieses Szenario. Präsentationsmodell ist ideal, wenn Sie eine neue Benutzer-Interaktion-Technologie (Windows 8, in diesem Fall) implementieren möchten, aber keine Änderungen in Bezug auf das Verhalten der Benutzeroberfläche gemacht werden sollen. Dieses Muster weiterhin der Auffassung, ein Modell und eine Ansicht als vor, aber die Rolle des Domänencontrollers wird durch eine abstrakte Darstellung der Ansicht mit dem Namen das Presentation Model gespielt. Diese Komponente implementiert die allgemeinen Verhalten der Ansicht, einschließlich Teil seines Status, ohne Rücksicht auf die Technologie der Ansicht.

Abbildung 5 zeigt die geänderte Version der MFC-Anwendung.

The Namespace Calculator::View Consolidates the View Behavior in Its Associated Presentation Model
Abbildung 5 der Namespace-Calculator::View konsolidiert das Ansicht-Verhalten in seiner zugeordneten Präsentationsmodell

CalculatorPresentationModel behält einen Verweis auf die Ansicht (modelliert als die ICalculatorView-Schnittstelle), denn sobald feststeht, dass der Ansichtsstatus geändert hat, ist es die Funktion UpdateDisplay aufruft. Im MFC-Beispiel ist die Ansicht CCalcDlg selbst, denn das ist die Klasse, die sich direkt mit MFC.

CCalcDlg wird seine Präsentationsmodell in seinem Konstruktor erstellt:

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

Wie Sie sehen können, nutzte ich eine C ++ 11 intelligente Zeiger, die hier genannt Unique_ptr (siehe bit.ly/KswVGy für weitere Informationen). Intelligente Zeiger haben die Möglichkeit, das Objekt freizugeben, auf die, das Sie verweisen, wenn es nicht länger erforderlich ist. Ich habe hier den intelligenten Zeiger um sicherzustellen, dass das Presentation Model zerstört wird, wenn der View-Lifecycle endet. Die Ansicht hält Window-Ereignisse erfassen, delegieren an das Presentation Model mit oder ohne massieren die Eingabe wie in gezeigt Abbildung 6.

Abbildung 6 einige Funktionen anzeigen Delegation

// 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();
}

Diese überarbeitete Version der MFC-Beispiel finden Sie in den Ordner Mfccalc in der herunterladbaren Begleiter-Probe. Sie können feststellen, dass kein Modell. In diesem Beispiel die Domänenlogik wäre schon einer Klasse enthält Funktionen für die vier grundlegenden arithmetischen Operationen, so etwas wie was, in gezeigt wird Abbildung 7.

Abbildung 7 Pure-Implementierung, die einen Taschenrechner "Modell" enthält

// 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();
}

Ich beschloss, das Modell in diesem kleinen Szenario verlassen seiner Logik innerhalb das Presentation Model zu überspringen. Nicht in den meisten Fällen typisch sein wird, und die Modell-Logik in eine eigene Klasse oder Gruppe von Klassen implementiert werden müssen. Wenn Sie möchten, können Sie das Beispiel in Richtung einer puristischen Ansatz als Übung umgestalten. Wenn Sie dies tun, müssen Sie besondere Aufmerksamkeit beim die CalculatorModel::Divide-Funktion zu implementieren, da das Modell ist eine wieder verwendbare Komponenten, die durch einen automatisierten Prozess anstatt eine Benutzerinteraktion aufgerufen werden könnten. In diesem Fall wäre es keine Rolle, dass eine Division durch Null eine Ausnahme löst; an diesem Punkt wäre es eine unerwartete Situation. Aber Sie haben nicht die NULL Teiler Überprüfung aus das Presentation Model entfernen. Weiterleitung zu Innenlagen fehlerhafte Daten verhindert, ist immer gesund. Die Ausnahme, die ausgelöst durch das Modell ist eine Last Resort-Maßnahme, wenn nichts anderes verfügbar ist.

Gehen Sie voran und der Begleiter münden Sie der umgestalteten Lösung Mfccalc ein, Code und Hinweise, die das Verhalten immer noch wie vor, das ist genau das, was ich wollte: um den Code für einen Port einrichten ohne Verlust der Funktionalität. Ein ernsthafter Umgestaltung Prozess muss eine Reihe von automatisierten Tests bestätigen, dass das Verhalten der Anwendung als Folge betroffen war nicht berücksichtigen. Native-Unit-Tests steht in Visual Studio 2012 und alle seine Entwickler-Editionen, die kostenlose Express für Windows 8 (die mit MFC oder anderen Frameworks für desktop-Entwicklung, aber nicht kommt).

Werfen Sie einen Blick auf die Calculator\CalculatorTests-Lösung im begleitenden Code. Der Calculator::Testing-Namespace enthält eine Klasse, deren Methoden zu, die von CalculatorPresentationModel testen, PresentationModelTest, siehe Abbildung 8.

Abbildung 8 isolieren Störungen mit Native-Unit-Tests

// 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
};

Unit-Tests ist übrigens eine der am meisten geschätzten Folgen dieses Presentation Model-Muster: Die UI-Verhaltens-Testautomatisierung entspricht der für die Domänenlogik, dass in diesem Beispiel ich in das Presentation Model eingebettet. So können Sie neue Kanäle für Ihre Anwendung (z. B. Kakao, Qt, WxWidgets oder sogar nicht-Native, wie HTML5 oder Windows Presentation Foundation) mit der Gewissheit, dass Störungen öffnen wenn überhaupt, sind in die neuen Komponenten der Benutzerinteraktion, anstatt die vorhandenen geschieht. Nutzen Sie das Code-Coverage-Feature, um sicherzustellen, dass alle Codezeilen mindestens einmal getestet werden.

Eröffnung einer XAML-Fassade, um die Rechner-Anwendung

Mit dem Code umgestaltet bin ich bereit, eine neue Windows-Benutzeroberfläche für den MFC-Rechner zu erstellen. Es ist einfach eine Frage der Erstellen einer neuen Ansicht für das Presentation Model-Muster wie oben beschrieben.

Windows 8 bietet drei Technologien: XAML, HTML und DirectX.

  • XAML ist die XML-basierte Markup-Sprache, mit der Sie visuelle Elemente der Benutzeroberfläche Daten binden an UI-Steuerelemente und -Handler aufrufen, als Reaktion auf Ereignisse zu deklarieren. Diese Ereignisse werden in der Regel definiert in sogenannten Codebehind-Komponenten, die in eine erweiterte C++-Syntax bekannt als C++-Komponente-Erweiterungen für die Windows-Runtime erstellt werden können (C + + / CX), oder in anderen Programmiersprachen erstellt. Hier präsentiere ich ein XAML-basierten Rechner-Gesicht.
  • HTML können Sie einstellen, welche UI-Verhaltensweisen, die in Java definiert­Skript ausführen im Internet Explorer "Chakra" Motor. Es ist möglich — wie ich im nächsten Abschnitt zeigen werde — C++-basierte Komponenten aus dem JavaScript-Code aufrufen.
  • DirectX ist ideal für Multimedia-Intensive Anwendungen. Da der Rechner eine multimedia-app nicht, wird nicht ich dies hier besprechen. DirectX-Anwendungen können XAML über Interop, die Sie, mehr über am lesen können bit.ly/NeUhO4.

Um eine XAML-Ansicht zu erstellen, ich wählte die grundlegenden leere App aus der Liste der Visual C++ Windows 8 Vorlagen und erstellt ein Projekt namens XamlCalc.

Diese leere Vorlage enthält nur eine leere MainPage.xaml, die ich mit Steuerelementen in der MFC-Version das Windows 8-Gegenstück des ehemaligen CCalcDlg-Steuerelements zu füllen werde. Dies ist alles, die Was ist notwendig, um die Rechner-Anwendung zu portieren, denn es aus nur einem einzigen Fenster mit keine Navigation besteht. Bei der Portierung der Anwendung sollten Sie die anderen Vorlagen bieten Ihnen einen Seitennavigation Mechanismus, der bietet eine intuitive und vorhersehbare UX. Hier finden Sie Anleitungen dazu auf der Seite "Designing UX für Apps" (bit.ly/Izbxky).

Die leere Vorlage kommt auch mit einer Datei app.XAML, Zweck ähnlich der CCalcApp-Klasse in der MFC-Version (siehe Abbildung 2). Es ist ein Bootstrap-Loader, die den Rest der Komponenten initialisiert und übergibt die Steuerung über ihnen. Die CCalcApp::InitInstance-Funktion erstellt ein CCalcDlg-Fenster, das dann als eine Benutzeroberfläche dient. (Sie finden all diese in der XamlCalc-Lösung im herunterladbaren begleitenden Code.) In der XAML-Fall App::OnLaunched wird standardmäßig in die Codebehind-Quellcode-Datei, App.xaml.cpp, generiert und löst eine anfängliche Navigation zu der Hauptseite:

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");
  }
  ...
}

Ich benutze den eingebauten XAML-Editor von Visual Studio eine immersive Rechner-Seite zu erstellen, indem Sie Steuerelemente aus der Toolbox ziehen und einige manuelle Bearbeiten, z. B. benutzerdefinierte Stile, Datenbindung, zugeordneten Ereignisse, usw.. Das sich ergebende XAML sieht der Code in Abbildung 9. Der Rechner zeigt sich an Abbildung 10.

Abbildung 9 XAML-Version der MFC-Rechner

<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
Abbildung 10 das Look And Feel des XAML-Calculator

Ich definiert den Stil der Schaltflächen (für Zahlen und Operatoren) in App.xaml, so brauche ich nicht, Farben, Ausrichtungen, Schriftarten und andere Eigenschaften für jede Schaltfläche zu wiederholen. In ähnlicher Weise verbundenen ich Event-Handler der Click-Eigenschaft der einzelnen Schaltflächen; der Handler sind MainPage-Methoden, die in der Codebehind-Quellcode-Datei MainPage.xaml.cpp definiert. Hier sind ein paar Beispiele, einer für angeklickte Zahlen und einer für die Kluft-Operation:

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);
}

Wie Sie sehen können, diese C + + / CX-Methoden in der MainPage nur nehmen Ereignisinformationen und es zu einer Instanz von meinem C++-Standardklasse, CalculatorPresentationModel, um die tatsächliche UI-Aktivität durchzuführen. Dies zeigt, dass es möglich ist, standard C++-Logik von vorhandenen native Anwendungen zu nehmen und sie in einem brandneuen C + wiederverwenden + / CX-Windows-Speicher-Anwendung. Diese Wiederverwendbarkeit senkt die Kosten für beide Versionen pflegen, wie sie beide, jedes mögliches Update innerhalb der gemeinsamen Komponenten nutzen können — die CalculatorPresentationModel in diesem Fall.

Umsetzung-spezifische Komponenten können wieder durch die gemeinsamen Komponenten aufgerufen werden, solange sie klar definierte abstrakte Schnittstellen implementieren. In meinem Beispiel, beispielsweise Rechner­PresentationModel::UpdateDisplay delegiert die eigentliche Aufgabe in eine Instanz von ICalculatorView:

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

In der MFC-Version ist ICalculatorView von der MFC-basierten CCalcDlg-Klasse implementiert. Schauen Sie sich die umgestalteten Sequenzdiagramm in Abbildung 11 und vergleichen Sie sie mit dem Original in Abbildung 3.

The Sequence Diagram for the Equal Sign Button in the Decoupled MFC Version
Abbildung 11 das Sequenzdiagramm für die Gleichheitszeichen-Taste in der entkoppelten MFC-Version

Um die XAML-Version analog zu den MFC-Fall zu halten, sollte ich ICalculatorView in MainPage implementiert habe. Stattdessen musste ich ICalculatorView als eine andere Klasse zu implementieren, da MainPage a C + ist + / CX Klasse, und daher kann nicht von einer standard C++-Klasse ableiten. C++ und seine Projektion in die Windows-Runtime (C + + / CX) haben unterschiedlichen Typsystemen — die interoperieren schön auf jeden Fall. Eine reine C++ ICalculatorView implementiert, war keine große Sache:

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

Standard C++ und C + + / CX-Klassen können nicht voneinander abgeleitet werden, aber sie können noch Verweise auf einander halten — in diesem Fall die privaten Member Page_ die bezieht sich auf ein C + + / CX MainPage. Um die Display-Steuerung in der XAML-Version zu aktualisieren, ändere ich einfach die Text-Eigenschaft des Steuerelements MainPage.xaml TextBlock namens Display_:

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

Abbildung 12 zeigt die XAML-Rechner-Klassendiagramm.

The XAML-C++/CX Calculator Application Class Diagram
Abbildung 12 das XAML-C + + / CX-Rechner-Anwendung-Klassendiagramm

Abbildung 13 zeigt die Reihenfolge, die Aktion des die Gleichheitszeichen-Taste in der XAML-Version entspricht.

The Sequence Diagram for the Equal Sign Button in the XAML version.
Abbildung 13 das Sequenzdiagramm für die Gleichheitszeichen-Taste in der XAML-version

Ich sollte erwähnen, dass die WRL asynchrone Verarbeitung durch alle seine APIs für die Ereignisbehandlung stark fördert. Mein Code ist 100 Prozent synchron, aber. Ich könnte gemacht habe es asynchron mithilfe der aufgabenbasierten Parallel Patterns Library, die Aufgaben und Fortsetzungen, basierend auf Windows 8 asynchrone Konzepte implementiert. Dies war nicht gerechtfertigt, in meinem kleinen Beispiel, aber es ist lesenswert mehr darüber auf der Seite "Asynchrone Programmierung in C++" im Windows Developer Center (bit.ly/Mi84D1).

Drücken Sie F5, und führen Sie die Anwendung, die XAML-Version in Aktion zu sehen. Beim Migrieren oder Ihren Windows-Speicher-Anwendungen erstellen, ist es wichtig, dass Sie Ihre Anwendung Benutzeroberfläche auf das neue Windows Experience-Entwurfsmuster entwerfen basierend, beschrieben im Microsoft Dev Center (bit.ly/Oxo3S9). Befolgen Sie die empfohlenen Muster zum kommandierenden, touch, gekippten Orientierung, Charme und mehr um die UX-intuitive Anwendung für Erstbenutzer zu halten.

Ein weiteres Beispiel: Eine neue Windows-UI-HTML-Rechner

Das XAML-Beispiel ist genug, um das erste MFC-basierten Calculator-Beispiel, die parallel zu Windows 8 läuft. Jedoch unter bestimmten Umständen (z. B. das Know-How Ihres Teams oder vorhandene Ressourcen zu nutzen), sollten HTML und JavaScript statt XAML für die Benutzeroberfläche Sie.

Das Presentation Model-Entwurfsmuster, die in diesem Artikel beschriebenen ist noch nützlich, auch wenn die Benutzeroberfläche Logik in einer nicht-C++-Sprache wie JavaScript enthält. Dieses Wunder ist möglich, weil in der Umgebung von Windows 8 JavaScript-Projekte für die Windows-Runtime viel als C++ tut, macht es möglich, dass beide zusammenarbeiten, da sie beide das Typsystem von der WRL gegründet teilen.

Im begleitenden Code finden Sie eine Lösung namens HtmlCalc, die eine ähnlich MainPage.xaml default.html-Seite enthält. Abbildung 14 zeigt eine UI-Beschreibung auf die XAML-Version zeigte in vergleichbaren Abbildung 9.

Abbildung 14 das HTML-Markup für den UI-Rechner

    <!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>

Die Codebehind-Rolle in HTML-Seiten wird gespielt von JavaScript-Code. In der Tat finden Sie diesen Code in der Datei js\default.js. Mein Rechner­PresentationModel, weil es ein standard C++-Klasse ist kann nicht direkt aus dem JavaScript-Teil aufgerufen werden, aber Sie können tun, dass es indirekt über eine Überbrückung C + + / CX-Komponente — Calculator::View::CalcView.

Diese Komponente von JavaScript-Instanziierung ist so einfach wie in default.js folgende Deklaration:

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

Als Beispiel für diesen Ansatz löst die Gleichheitszeichen-Taste einen Aufruf an die folgende JavaScript-Funktion:

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

Dies wird den Aufruf von CalcView::equal_click, die "nativ mit meinem standard C++-CalculatorPresentationModel Gespräche" weitergegeben:

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();
}

In diesem speziellen Szenario, die C + + / CX-Komponente CalcView leitet nur jeder Anforderung an die Norm C++-PresentationModel (siehe das Sequenzdiagramm in Abbildung 15). Wir können nicht vermeiden es auf unserem Weg in die wiederverwendbare C++-Komponente, obwohl (siehe Abbildung 15).

The Sequence Diagram for the Equal Sign Button in the Hybrid HTML-C++ Version
Abbildung 15 das Sequenzdiagramm für die Gleichheitszeichen-Taste in der Hybrid-HTML-C++-Version

Da die C + + / CX-Proxy muss manuell erstellt werden, die damit verbundenen Kosten sollten nicht ignoriert werden. Dennoch können Sie es gegen den Nutzen der Wiederverwendung von Komponenten, ausgleichen, wie ich in meinem Szenario mit CalculatorPresentationModel.

Go ahead, und drücken Sie F5, um die HTML-Version in Aktion zu sehen. Ich habe gezeigt, wie Sie wiederverwenden vorhandenen C++-Code, um seine Reichweite zu neuartigen Framework in Windows 8 zu erweitern, ohne Löschen der ursprünglichen Kanäle (MFC in meinem Fall). Wir sind jetzt bereit für einige abschließenden Überlegungen.

Hallo (reale)!!

Meine Portierung Szenario ist ein Sonderfall, die Ihren speziellen Fall möglicherweise nicht die möglicherweise nicht ein fremdes Einzelfall. Viel von was ich hier gezeigt ist anwendbar auf das MFC-Rechner-Szenario, und ich könnte unterschiedliche Entscheidungen machen, wenn ich waren eine andere Anwendung auf die WRL portieren. Daher sind hier einige allgemeinen Schlussfolgerungen über das Portieren von Anwendungen:

  • Einfache Standardobjekte — diejenigen, die kein spezifisches Verhältnis mit Drittanbieter-APIs haben — haben maximale Wiederverwendbarkeit und damit Nein oder kostengünstige Portabilität. Im Gegensatz dazu wird Wiederverwendbarkeit eingeschränkt, wenn Objekte explizite Verbindungen zu nicht-Standard-APIs, wie MFC, Qt und die WRL haben. MFC steht beispielsweise nur auf dem Windows-Desktop zur Verfügung. Qt, ist auf der anderen Seite in anderen Umgebungen, obwohl nicht in allen. Vermeiden Sie in solchen Fällen Mischungen, die Wiederverwendbarkeit untergraben von Anwendungsobjekten "sprechen", um abstrakte Klassen zu machen. Dann leiten Sie von dieser Klassen dritte-Partei-fähige Implementierungen zu erstellen. Schau, was ich, mit ICalculatorView (die abstrakte Klasse) und ihrer Implementierungen, CCalcDlg und XamlCalc::CalcView Tat. Für Windows-8-Entwicklung, machen Sie sich mit der WRL-APIs und die Win32-APIs, die sie ersetzt sind. Finden Sie weitere Informationen unter bit.ly/IBKphR.
  • Ich wandte das Presentation Model-Muster, denn mein Ziel war es, in der Windows-Runtime zu imitieren, was hatte ich schon auf dem Desktop. Möglicherweise möchten Sie die Funktionen zu kürzen, wenn sie nicht viel Sinn — z. B. Anwenden von Stilen auf Text in einer mobilen E-mail-app. Oder Sie möglicherweise Funktionen, die Ihre neuen Zielplattform nutzen hinzufügen — denken, zum Beispiel Bild Strecken über Multi-Touch in einer Bild-Viewer-Anwendung. In solchen Fällen könnte eine weitere Entwurfsmuster besser geeignet sein.
  • Das Presentation Model habe ich eignet sich für die Aufrechterhaltung des Geschäftsbetriebs und für geringe Wartungskosten. Dies ermöglicht mir Windows Store Versionen der app ohne Schneiden Bindungen mit Kunden zu liefern, die die ursprüngliche MFC-Option zu bevorzugen. Pflege zwei Kanäle (XAML und MFC oder HTML und MFC) ist zweimal die Kosten nicht, solange ich wiederverwendbare Komponenten wie CalculatorPresentationModel haben.
  • Die gesamte Wiederverwendbarkeit einer Anwendung bestimmt das Verhältnis von Code-Zeilen, die in allen Versionen im Vergleich zu Code-Zeilen für die Aufrechterhaltung bestimmter Versionen (Komponenten, die von Dritten gepflegt werden nicht berücksichtigt). Es gibt Fälle, in denen Anwendungen hängen stark von nicht-Standard-APIs (z. B. eine augmented-Reality-app, die OpenGL und iOS Sensoren nutzt). Das Verhältnis der Wiederverwendbarkeit kann so niedrig, dass Sie schließlich entscheiden könnte, die Anwendung ohne Komponente Wiederverwendbarkeit als dem konzeptionell zu portieren.
  • Beginnen Sie nicht Fragen, wer so schlecht die vorhandene Anwendung entworfen haben könnte, so dass Ihr Portierung Job so schwierig. Starten sie stattdessen die Umgestaltung. Denken Sie daran, dass Agile Methoden sind nicht auf ausgereifte, robuste, hoch wiederverwendbare Architekturen; was sie betonen, ist Softwarelieferung. Software zu machen, generische und erweiterbar für zukünftige Wiederverwendbarkeit und Mobilität erfordert viel Erfahrung, da es nicht einfach, Design-Entscheidungen im Dunkeln zu machen.
  • Sie können Ihre Anwendung auf Windows 8, iOS oder Android mit der Absicht, es durch den Marktplätzen dieser Plattformen zu verkaufen portieren werden. In diesem Fall halten Sie im Verstand, die Ihre Anwendung einen Zertifizierungsprozess vergehen müssen, bevor Sie akzeptiert werden (bit.ly/L0sY9i). Dies könnte Sie zwingen, UI Verhaltensweisen zu unterstützen, die Sie nie in Ihrer ursprünglichen Version (z. B. Note-First, Charme und So weiter) erwogen. Bestimmte Standards nicht erfüllen könnte Ihre Bewerbung abgelehnt führen. Nicht solche "Einhaltung" übersehen, wenn die Schätzung der Kosten.

Die Herausforderung meistern

Die neue Windows-Runtime und den noch allgegenwärtigen Windows-Desktop stellen eine Herausforderung für Entwickler, die nicht, dass die Extra zahlen möchten Kosten für die Wartung einer separaten Anwendung pro Plattform. In diesem Artikel ich gezeigt, dass vorhandene CodeBase kann genutzt werden, nicht nur um neue Kanäle zu aktivieren, sondern auch zur Verbesserung der durch die Umgestaltung der Qualität der bestehenden CodeBase.

Diego Dagum ist ein Softwarearchitekt und Trainer mit mehr als 20 Jahre Erfahrung in der Branche. Er kann erreicht werden unter email@diegodagum.com.

Unser Dank gilt den folgenden technischen Experten für die Durchsicht dieses Artikels: Marius Bancila, Angel Jesus Hernandez und das Windows-8-team