Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Um WPF in Win32-Anwendungen zu integrieren, verwenden Sie HwndSource, das das HWND bereitstellt, das Ihren WPF-Inhalt enthält. Dazu erstellen Sie zunächst die HwndSource mit Parametern, die denen von „CreateWindow“ ähneln. Danach informieren Sie die HwndSource über den Inhalt, der darin enthalten sein soll. Schließlich rufen Sie das HWND aus der HwndSource ab. In dieser exemplarischen Vorgehensweise wird veranschaulicht, wie Sie eine gemischte WPF-in-Win32-Anwendung erstellen, die das Betriebssystem-Dialogfeld Datums- und Uhrzeiteigenschaften neu implementiert.
Voraussetzungen
Weitere Informationen finden Sie unter Interaktion zwischen WPF und Win32.
Wie man dieses Lernprogramm verwendet
Dieses Tutorial konzentriert sich auf die wichtigsten Schritte zur Erstellung einer Interoperationsanwendung. Das Tutorial stützt sich zwar auf ein Beispiel (Beispiel für Interaktion mit der Win32-Uhr), aber dieses Beispiel spiegelt das gewünschte Endprodukt wider. In diesem Lernprogramm werden die Schritte so dokumentiert, als würden Sie mit einem vorhandenen Win32-Projekt selbst beginnen, vielleicht ein bereits vorhandenes Projekt, und Sie haben ihrer Anwendung ein gehostetes WPF hinzugefügt. Sie können Ihr Endprodukt mit dem Beispiel für Interaktion mit der Win32-Uhr vergleichen.
Exemplarische Vorgehensweise für Windows Presentation Framework in Win32 (HwndSource)
Die folgende Abbildung zeigt das beabsichtigte Endprodukt dieses Lernprogramms:
Sie können dieses Dialogfeld neu erstellen, indem Sie ein C++-Win32-Projekt in Visual Studio erstellen und den Dialog-Editor verwenden, um Folgendes zu erstellen:
dialogfeld
(Sie müssen Visual Studio nicht verwenden, um HwndSourcezu verwenden, und Sie müssen C++ nicht zum Schreiben von Win32-Programmen verwenden, aber dies ist eine ziemlich typische Möglichkeit, dies zu tun, und eignet sich gut für eine schrittweise Lernprogramm-Erklärung).
Sie müssen fünf bestimmte Unterschritte ausführen, um eine WPF-Uhr in das Dialogfeld einzufügen:
Aktivieren Sie Ihr Win32-Projekt, verwalteten Code (/clr) aufzurufen, indem Sie die Projekteinstellungen in Visual Studio ändern.
Erstellen eines WPF-Page-Elements in einer separaten DLL.
Setzen Sie diese WPF-Page in eine HwndSource.
Rufen Sie für diese Page mithilfe der Handle-Eigenschaft ein HWND ab.
Verwenden Sie Win32, um zu entscheiden, wo der HWND in der größeren Win32-Anwendung platziert werden soll.
/clr
Der erste Schritt besteht darin, dieses nicht verwaltete Win32-Projekt in ein Projekt umzuwandeln, das verwalteten Code aufrufen kann. Verwenden Sie die Compileroption „/clr“. Auf diese Weise werden die erforderlichen DLLs verknüpft und die Main-Methode für die Verwendung mit WPF angepasst.
So aktivieren Sie die Verwendung von verwaltetem Code im C++-Projekt: Klicken Sie mit der rechten Maustaste auf das Win32clock-Projekt, und wählen Sie Eigenschaftenaus. Ändern Sie auf der Eigenschaftenseite Allgemein (Standardseite) die Eigenschaft „Common Language Runtime-Unterstützung” in /clr
.
Fügen Sie als Nächstes Verweise auf DLLs hinzu, die für WPF erforderlich sind: PresentationCore.dll, PresentationFramework.dll, System.dll, WindowsBase.dll, UIAutomationProvider.dllund UIAutomationTypes.dll. (In den folgenden Anweisungen wird davon ausgegangen, dass das Betriebssystem auf C: Laufwerk installiert ist.)
Klicken Sie mit der rechten Maustaste auf das win32clock-Projekt, wählen Sie Verweise... aus, und führen Sie in diesem Dialogfeld folgende Schritte aus:
Führen Sie einen rechten Mausklick auf das win32clock-Projekt aus und wählen Sie Verweise... .
Klicken Sie auf neuen Verweis hinzufügen, klicken Sie auf die Registerkarte Durchsuchen, geben Sie dort C:\Programme\Reference Assemblies\Microsoft\Framework\v3.0\PresentationCore.dll ein, und klicken Sie auf OK.
Wiederholen Sie diesen Schritt für „PresentationFramework.dll: C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\PresentationFramework.dll“.
Wiederholen Sie diesen Schritt für WindowsBase.dll: C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\WindowsBase.dll.
Wiederholen Sie diesen Schritt für UIAutomationTypes.dll: C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\UIAutomationTypes.dll.
Wiederholen Sie diesen Schritt für „UIAutomationProvider.dll: C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\UIAutomationProvider.dll“.
Klicken Sie auf neuen Verweis hinzufügen, wählen Sie System.dll, und klicken Sie auf OK.
Klicken Sie auf OK, um die win32clock-Eigenschaftenseiten zum Hinzufügen von Verweisen zu beenden.
Fügen Sie nun abschließend mit WPF das Attribut STAThreadAttribute
zur _tWinMain
-Methode hinzu:
[System::STAThreadAttribute]
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
Dieses Attribut teilt der Common Language Runtime (CLR) mit, dass es beim Initialisieren des Component Object Model (COM) ein STA-Modell (Single Threaded Apartment) verwenden soll. Dies ist für WPF (und Windows Forms) erforderlich.
Erstellen einer Windows Presentation Framework-Seite
Als Nächstes erstellen Sie eine DLL, die eine WPF-Page definiert. Häufig ist es am einfachsten, die WPF-Page als eigenständige Anwendung zu erstellen und den WPF-Teil auf diese Weise zu schreiben und zu debuggen. Danach kann das Projekt in eine DLL-Datei umgewandelt werden. Klicken Sie hierfür mit der rechten Maustaste auf das Projekt, wählen Sie Eigenschaften aus, navigieren Sie zur Anwendung, und ändern Sie den Ausgabetyp in „Windows-Klassenbibliothek”.
Das WPF-DLL-Projekt kann dann wie folgt mit dem Win32-Projekt kombiniert werden (eine Projektmappe, die zwei Projekte enthält): Klicken Sie mit der rechten Maustaste auf die Projektmappe, und wählen Sie Hinzufügen\Vorhandenes Projekt aus.
Um diese WPF-DLL aus dem Win32-Projekt zu verwenden, müssen Sie einen Verweis hinzufügen:
Führen Sie einen rechten Mausklick auf das win32clock-Projekt aus und wählen Sie Verweise... .
Klicken Sie auf Neuen Verweis hinzufügen.
Klicken Sie auf die Registerkarte Projekte. Wählen Sie WPFClock aus und klicken Sie auf „OK“.
Klicken Sie auf OK, um die win32clock-Eigenschaftenseiten zum Hinzufügen von Verweisen zu beenden.
HwndSource
Verwenden Sie als Nächstes HwndSource, um die WPF-Page wie ein HWND aussehen zu lassen. Sie fügen diesen Codeblock zu einer C++-Datei hinzu:
namespace ManagedCode
{
using namespace System;
using namespace System::Windows;
using namespace System::Windows::Interop;
using namespace System::Windows::Media;
HWND GetHwnd(HWND parent, int x, int y, int width, int height) {
HwndSource^ source = gcnew HwndSource(
0, // class style
WS_VISIBLE | WS_CHILD, // style
0, // exstyle
x, y, width, height,
"hi", // NAME
IntPtr(parent) // parent window
);
UIElement^ page = gcnew WPFClock::Clock();
source->RootVisual = page;
return (HWND) source->Handle.ToPointer();
}
}
}
Dies ist ein langer Codeabschnitt, der einige Erläuterungen verwenden könnte. Der erste Teil besteht aus verschiedenen Klauseln, die dafür sorgen, dass nicht alle Aufrufe vollständig qualifiziert werden müssen:
namespace ManagedCode
{
using namespace System;
using namespace System::Windows;
using namespace System::Windows::Interop;
using namespace System::Windows::Media;
Anschließend definieren Sie eine Funktion, die den WPF-Inhalt erstellt, mit HwndSource umschließt und das HWND zurückgibt:
HWND GetHwnd(HWND parent, int x, int y, int width, int height) {
Dazu erstellen Sie zunächst eine HwndSource mit Parametern, die denen von „CreateWindow“ ähneln:
HwndSource^ source = gcnew HwndSource(
0, // class style
WS_VISIBLE | WS_CHILD, // style
0, // exstyle
x, y, width, height,
"hi", // NAME
IntPtr(parent) // parent window
);
Anschließend erstellen Sie die WPF-Inhaltsklasse durch Aufrufen des Konstruktors:
UIElement^ page = gcnew WPFClock::Clock();
Anschließend verbinden Sie die Seite mit der HwndSource:
source->RootVisual = page;
In der letzten Zeile geben Sie das HWND für die HwndSource zurück:
return (HWND) source->Handle.ToPointer();
Positionieren des HWND
Da Sie nun über ein HWND mit der WPF-Uhr verfügen, müssen Sie dieses HWND im Win32-Dialog platzieren. Wenn Sie nur wussten, wo der HWND platziert werden soll, würden Sie diese Größe und Position einfach an die zuvor definierte GetHwnd
Funktion übergeben. Sie haben für die Definition des Dialogfelds jedoch eine Ressourcendatei verwendet, sodass Sie nicht sicher wissen, wo ein HWND jeweils platziert sein wird. Sie können allerdings mithilfe des Visual Studio-Dialog-Editors ein STATISCHES Win32-Steuerelement dort platzieren, wo Sie die Uhr haben möchten („Uhr hier einfügen“), und dieses verwenden, um die WPF-Uhr zu positionieren.
An der Stelle, an der Sie „WM_INITDIALOG“ behandeln, können Sie GetDlgItem
verwenden, um das HWND für den Platzhalter „STATIC” abzurufen:
HWND placeholder = GetDlgItem(hDlg, IDC_CLOCK);
Anschließend berechnen Sie die Größe und Position dieses PlatzhalterS STATIC, sodass Sie die WPF-Uhr an dieser Stelle platzieren können:
RECHT Rechteck;
GetWindowRect(placeholder, &rectangle);
int width = rectangle.right - rectangle.left;
int height = rectangle.bottom - rectangle.top;
POINT point;
point.x = rectangle.left;
point.y = rectangle.top;
result = MapWindowPoints(NULL, hDlg, &point, 1);
Blenden Sie anschließen den Platzhalter „STATIC” aus:
ShowWindow(placeholder, SW_HIDE);
Erstellen Sie nun das HWND der WPF-Uhr an dieser Position:
HWND clock = ManagedCode::GetHwnd(hDlg, point.x, point.y, width, height);
Um das Tutorial interessant zu gestalten und eine echte WPF-Uhr zu erstellen, müssen Sie nun ein WPF-Uhr-Steuerelement erstellen. Den größten Teil davon können Sie mit nur einigen wenigen Ereignishandlern in der CodeBehind-Datei in Markup erledigen. Da es sich bei diesem Lernprogramm um die Interoperabilität und nicht um den Entwurf von Steuerelementen handelt, wird der vollständige Code für die WPF-Uhr hier als Codeblock bereitgestellt, ohne diskrete Anweisungen zum Aufbau oder was jeder Teil bedeutet. Sie können mit diesem Code experimentieren, um das Aussehen und Verhalten oder die Funktionalität des Steuerelements zu ändern.
Dies ist das Markup:
<Page x:Class="WPFClock.Clock"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<Grid>
<Grid.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#fcfcfe" Offset="0" />
<GradientStop Color="#f6f4f0" Offset="1.0" />
</LinearGradientBrush>
</Grid.Background>
<Grid Name="PodClock" HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid.Resources>
<Storyboard x:Key="sb">
<DoubleAnimation From="0" To="360" Duration="12:00:00" RepeatBehavior="Forever"
Storyboard.TargetName="HourHand"
Storyboard.TargetProperty="(Rectangle.RenderTransform).(RotateTransform.Angle)"
/>
<DoubleAnimation From="0" To="360" Duration="01:00:00" RepeatBehavior="Forever"
Storyboard.TargetName="MinuteHand"
Storyboard.TargetProperty="(Rectangle.RenderTransform).(RotateTransform.Angle)"
/>
<DoubleAnimation From="0" To="360" Duration="0:1:00" RepeatBehavior="Forever"
Storyboard.TargetName="SecondHand"
Storyboard.TargetProperty="(Rectangle.RenderTransform).(RotateTransform.Angle)"
/>
</Storyboard>
</Grid.Resources>
<Ellipse Width="108" Height="108" StrokeThickness="3">
<Ellipse.Stroke>
<LinearGradientBrush>
<GradientStop Color="LightBlue" Offset="0" />
<GradientStop Color="DarkBlue" Offset="1" />
</LinearGradientBrush>
</Ellipse.Stroke>
</Ellipse>
<Ellipse VerticalAlignment="Center" HorizontalAlignment="Center" Width="104" Height="104" Fill="LightBlue" StrokeThickness="3">
<Ellipse.Stroke>
<LinearGradientBrush>
<GradientStop Color="DarkBlue" Offset="0" />
<GradientStop Color="LightBlue" Offset="1" />
</LinearGradientBrush>
</Ellipse.Stroke>
</Ellipse>
<Border BorderThickness="1" BorderBrush="Black" Background="White" Margin="20" HorizontalAlignment="Right" VerticalAlignment="Center">
<TextBlock Name="MonthDay" Text="{Binding}"/>
</Border>
<Canvas Width="102" Height="102">
<Ellipse Width="8" Height="8" Fill="Black" Canvas.Top="46" Canvas.Left="46" />
<Rectangle Canvas.Top="5" Canvas.Left="48" Fill="Black" Width="4" Height="8">
<Rectangle.RenderTransform>
<RotateTransform CenterX="2" CenterY="46" Angle="0" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Canvas.Top="5" Canvas.Left="49" Fill="Black" Width="2" Height="6">
<Rectangle.RenderTransform>
<RotateTransform CenterX="2" CenterY="46" Angle="30" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Canvas.Top="5" Canvas.Left="49" Fill="Black" Width="2" Height="6">
<Rectangle.RenderTransform>
<RotateTransform CenterX="2" CenterY="46" Angle="60" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Canvas.Top="5" Canvas.Left="48" Fill="Black" Width="4" Height="8">
<Rectangle.RenderTransform>
<RotateTransform CenterX="2" CenterY="46" Angle="90" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Canvas.Top="5" Canvas.Left="49" Fill="Black" Width="2" Height="6">
<Rectangle.RenderTransform>
<RotateTransform CenterX="2" CenterY="46" Angle="120" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Canvas.Top="5" Canvas.Left="49" Fill="Black" Width="2" Height="6">
<Rectangle.RenderTransform>
<RotateTransform CenterX="2" CenterY="46" Angle="150" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Canvas.Top="5" Canvas.Left="48" Fill="Black" Width="4" Height="8">
<Rectangle.RenderTransform>
<RotateTransform CenterX="2" CenterY="46" Angle="180" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Canvas.Top="5" Canvas.Left="49" Fill="Black" Width="2" Height="6">
<Rectangle.RenderTransform>
<RotateTransform CenterX="2" CenterY="46" Angle="210" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Canvas.Top="5" Canvas.Left="49" Fill="Black" Width="2" Height="6">
<Rectangle.RenderTransform>
<RotateTransform CenterX="2" CenterY="46" Angle="240" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Canvas.Top="5" Canvas.Left="48" Fill="Black" Width="4" Height="8">
<Rectangle.RenderTransform>
<RotateTransform CenterX="2" CenterY="46" Angle="270" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Canvas.Top="5" Canvas.Left="49" Fill="Black" Width="2" Height="6">
<Rectangle.RenderTransform>
<RotateTransform CenterX="2" CenterY="46" Angle="300" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Canvas.Top="5" Canvas.Left="49" Fill="Black" Width="2" Height="6">
<Rectangle.RenderTransform>
<RotateTransform CenterX="2" CenterY="46" Angle="330" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle x:Name="HourHand" Canvas.Top="21" Canvas.Left="48"
Fill="Black" Width="4" Height="30">
<Rectangle.RenderTransform>
<RotateTransform x:Name="HourHand2" CenterX="2" CenterY="30" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle x:Name="MinuteHand" Canvas.Top="6" Canvas.Left="49"
Fill="Black" Width="2" Height="45">
<Rectangle.RenderTransform>
<RotateTransform CenterX="1" CenterY="45" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle x:Name="SecondHand" Canvas.Top="4" Canvas.Left="49"
Fill="Red" Width="1" Height="47">
<Rectangle.RenderTransform>
<RotateTransform CenterX="0.5" CenterY="47" />
</Rectangle.RenderTransform>
</Rectangle>
</Canvas>
</Grid>
</Grid>
</Page>
Zugehörige CodeBehind-Datei:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Threading;
namespace WPFClock
{
/// <summary>
/// Interaction logic for Clock.xaml
/// </summary>
public partial class Clock : Page
{
private DispatcherTimer _dayTimer;
public Clock()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(Clock_Loaded);
}
void Clock_Loaded(object sender, RoutedEventArgs e) {
// set the datacontext to be today's date
DateTime now = DateTime.Now;
DataContext = now.Day.ToString();
// then set up a timer to fire at the start of tomorrow, so that we can update
// the datacontext
_dayTimer = new DispatcherTimer();
_dayTimer.Interval = new TimeSpan(1, 0, 0, 0) - now.TimeOfDay;
_dayTimer.Tick += new EventHandler(OnDayChange);
_dayTimer.Start();
// finally, seek the timeline, which assumes a beginning at midnight, to the appropriate
// offset
Storyboard sb = (Storyboard)PodClock.FindResource("sb");
sb.Begin(PodClock, HandoffBehavior.SnapshotAndReplace, true);
sb.Seek(PodClock, now.TimeOfDay, TimeSeekOrigin.BeginTime);
}
private void OnDayChange(object sender, EventArgs e)
{
// date has changed, update the datacontext to reflect today's date
DateTime now = DateTime.Now;
DataContext = now.Day.ToString();
_dayTimer.Interval = new TimeSpan(1, 0, 0, 0);
}
}
}
Das Endergebnis sieht wie folgt aus:
Sehen Sie sich das Beispiel für Interaktion mit der Win32-Uhr an, um Ihr Ergebnis mit dem Code zu vergleichen, dessen Ergebnis in diesen Screenshot abgebildet ist.
Siehe auch
.NET Desktop feedback