Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Chcete-li umístit WPF do aplikací Win32, použijte HwndSource, který poskytuje HWND, který obsahuje váš obsah WPF. Nejprve vytvoříte parametr HwndSource, který bude vypadat podobně jako CreateWindow. Pak povězte HwndSource jaký obsah chcete mít uvnitř WPF. Konečně, dostaneš HWND ven z HwndSource. Tento názorný postup ukazuje, jak vytvořit smíšenou aplikaci WPF uvnitř aplikace Win32, která znovu vytvoří dialogové okno Vlastnosti data a času operačního systému.
Požadavky
Podívejte se na WPF a Win32 interoperabilitu.
Jak používat tento kurz
Tento kurz se zaměřuje na důležité kroky při vytváření interoperační aplikace. Tento tutoriál je podpořen ukázkou Win32 Clock Interoperation Sample, ale tato ukázka odráží koncový produkt. Tento kurz popisuje kroky, jako kdybyste začali s existujícím projektem Win32, možná už existujícím projektem, a přidali jste do své aplikace hostovaný WPF. Koncový produkt můžete porovnat s ukázkou spolupráce Win32 Clock.
Návod k rozhraní Windows Presentation Framework v systému Win32 (HwndSource)
Následující obrázek ukazuje zamýšlený koncový produkt tohoto kurzu:
Toto dialogové okno můžete znovu vytvořit tak, že vytvoříte projekt C++ Win32 v sadě Visual Studio a pomocí editoru dialogů vytvoříte následující:
(K použití HwndSourcenemusíte používat Visual Studio a nemusíte používat C++ k psaní programů Win32, ale je to poměrně typický způsob, jak to udělat, a hodí se pro vysvětlení krokových kurzů).
Abyste mohli vložit hodiny WPF do dialogového okna, musíte provést pět konkrétních dílčích kroků:
Povolte svůj Win32 projekt volat spravovaný kód (/clr) změnou nastavení projektu ve Visual Studiu.
Vytvořte WPF aplikaciPage v samostatné knihovně DLL.
Vložte WPFPage dovnitř HwndSource.
Používejte Win32 k rozhodování, kam umístit identifikátor HWND v rámci větší Win32 aplikace.
/clr
Prvním krokem je převést tento nespravovaný projekt Win32 na projekt, který může volat spravovaný kód. Použijete možnost kompilátoru /clr, která propojuje s potřebnými knihovnami DLL, které chcete použít, a upravíte metodu Main pro použití s WPF.
Pokud chcete povolit použití spravovaného kódu v projektu C++: Klikněte pravým tlačítkem myši na projekt win32clock a vyberte Vlastnosti. Na stránce vlastností Obecné (výchozí) změňte podporu modulu ClR (Common Language Runtime) na /clr.
Dále přidejte odkazy na knihovny DLL potřebné pro WPF: PresentationCore.dll, PresentationFramework.dll, System.dll, WindowsBase.dll, UIAutomationProvider.dlla UIAutomationTypes.dll. (Následující pokyny předpokládají, že operační systém je nainstalován na jednotce C: .)
Klikněte pravým tlačítkem myši na projekt win32clock a vyberte Odkazy... a v dialogovém okně:
Klikněte pravým tlačítkem na projekt win32clock a vyberte Odkazy....
Klikněte na Přidat nový odkaz, klikněte na Procházet kartu, zadejte C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\PresentationCore.dlla klikněte na OK.
Opakujte pro PresentationFramework.dll: C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\PresentationFramework.dll.
Opakujte pro WindowsBase.dll: C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\WindowsBase.dll.
Opakujte pro UIAutomationTypes.dll: C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\UIAutomationTypes.dll.
Opakujte pro UIAutomationProvider.dll: C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\UIAutomationProvider.dll.
Klepněte na tlačítko Přidat nový odkaz, vyberte System.dlla klepněte na tlačítko OK.
Kliknutím na tlačítko OK ukončete stránky vlastností win32clock pro přidání odkazů.
Nakonec přidejte STAThreadAttribute do metody _tWinMain pro použití s WPF.
[System::STAThreadAttribute]
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
Tento atribut říká modulu CLR (Common Language Runtime), že při inicializaci modelu COM (Component Object Model) by měl používat jeden model bytu s vlákny (STA), který je nezbytný pro WPF (a Windows Forms).
Vytvořte stránku Windows Presentation Framework
Dále vytvoříte knihovnu DLL, která definuje komponenty WPFPage. Často je nejjednodušší vytvořit WPFPage jako samostatnou aplikaci a napsat a ladit část WPF tímto způsobem. Po dokončení lze tento projekt převést na knihovnu DLL tak, že kliknete pravým tlačítkem myši na projekt, kliknete na Vlastnosti, přejdete do aplikace a změníte typ výstupu na knihovnu tříd systému Windows.
Projekt knihovny DLL WPF lze poté kombinovat s projektem Win32 (jedno řešení, které obsahuje dva projekty) – klikněte pravým tlačítkem myši na řešení a vyberte Přidat\Existující projekt.
Chcete-li použít tuto knihovnu DLL WPF z projektu Win32, musíte přidat odkaz:
Klikněte pravým tlačítkem na projekt win32clock a vyberte Odkazy....
Klikněte na Přidat nový odkaz.
Klikněte na kartu Projekty . Vyberte WPFClock, klepněte na tlačítko OK.
Kliknutím na tlačítko OK ukončete stránky vlastností win32clock pro přidání odkazů.
HwndSource
Dále použijete HwndSource k tomu, aby WPFPage vypadal jako HWND. Tento blok kódu přidáte do souboru C++:
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();
}
}
}
Toto je dlouhá část kódu, která by mohla použít nějaké vysvětlení. První část je různé klauzule, takže nemusíte plně kvalifikovat všechna volání.
namespace ManagedCode
{
using namespace System;
using namespace System::Windows;
using namespace System::Windows::Interop;
using namespace System::Windows::Media;
Pak definujete funkci, která vytvoří obsah WPF, vloží HwndSource kolem něj a vrátí HWND:
HWND GetHwnd(HWND parent, int x, int y, int width, int height) {
Nejprve vytvoříte objekt HwndSource, jehož parametry jsou podobné CreateWindow:
HwndSource^ source = gcnew HwndSource(
0, // class style
WS_VISIBLE | WS_CHILD, // style
0, // exstyle
x, y, width, height,
"hi", // NAME
IntPtr(parent) // parent window
);
Pak vytvoříte třídu obsahu WPF voláním jeho konstruktoru:
UIElement^ page = gcnew WPFClock::Clock();
Pak připojíte stránku k HwndSource:
source->RootVisual = page;
A v posledním řádku vraťte HWND pro HwndSource:
return (HWND) source->Handle.ToPointer();
Umístění hwndu
Teď, když máte HWND, který obsahuje WPF hodiny, musíte tento HWND dát do dialogového okna Win32. Kdybyste věděli, kam umístit HWND, stačí předat tuto velikost a umístění do GetHwnd funkce, kterou jste definovali dříve. K definování dialogového okna jste ale použili soubor prostředků, takže si nejste úplně jistí, kde jsou umístěné jednotlivé HWND (window handle). Editor dialogových oken sady Visual Studio můžete použít k umístění ovládacího prvku Win32 STATIC na místo, kam mají hodiny jít ("Sem vložte hodiny") a použijte ho k umístění hodin WPF.
Když zpracováváte WM_INITDIALOG, použijte GetDlgItem k získání HWND pro zástupný prvek STATIC:
HWND placeholder = GetDlgItem(hDlg, IDC_CLOCK);
Potom vypočítáte velikost a umístění tohoto zástupného symbolu STATIC, abyste mohli umístit hodiny WPF na toto místo:
RECT obdélník;
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);
Zástupný symbol STATIC pak skryjete:
ShowWindow(placeholder, SW_HIDE);
A vytvořte HWND hodin WPF na daném místě.
HWND clock = ManagedCode::GetHwnd(hDlg, point.x, point.y, width, height);
Aby byl kurz zajímavý, a abyste vytvořili skutečné WPF hodiny, budete muset vytvořit ovládací prvek hodin WPF v tomto okamžiku. Můžete to provést většinou ve značkovacím jazyce, s několika obslužnými rutinami událostí v zákulisí kódu. Vzhledem k tomu, že tento kurz se týká spolupráce a ne návrhu řízení, celý kód hodin WPF je zde poskytnut jako kódový blok, bez podrobných instrukcí pro jeho vytvoření nebo vysvětlení jednotlivých částí. Experimentujte s tímto kódem a změňte vzhled a chování nebo funkčnost ovládacího prvku.
Tady je kód:
<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>
A tady je doprovodný kód:
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);
}
}
}
Konečný výsledek vypadá takto:
Pokud chcete porovnat svůj konečný výsledek s kódem, který vytvořil tento snímek obrazovky, podívejte se na Win32 Clock Interoperation Sample.
Viz také
.NET Desktop feedback