İzlenecek yol: Win32'de WPF Saati Barındırma
WPF'yi Win32 uygulamalarının içine yerleştirmek için, WPF içeriğinizi içeren HWND'yi sağlayan HwndSourcekullanın. İlk olarak HwndSourceoluşturup CreateWindow'a benzer parametreler verirsiniz. Ardından, HwndSource'a içinde olmasını istediğiniz WPF içeriği hakkında bilgi verirsiniz. Son olarak, HWND'yi HwndSource'dan çıkarın. Bu kılavuz, Win32 uygulaması içinde işletim sistemi Tarih ve Saat Özellikleri diyalogunu yeniden oluşturan kapsamlı bir WPF oluşturmayı gösterir.
Önkoşullar
Bkz. WPF ve Win32 Birlikte Çalışma
Bu Eğitimi Nasıl Kullanırsınız?
Bu öğretici, birlikte çalışma uygulaması oluşturmanın önemli adımlarına odaklanır. Öğretici, Win32 Saat Birlikte Çalışma Örneği ile desteklenmiştir, ancak bu örnek, son ürünü yansıtmaktadır. Bu kılavuzda, önceden var olan bir Win32 projenizden başlayarak uygulamanıza gömülü bir WPF eklediğinizde atmanız gereken adımlar açıklanmaktadır. Son ürününüzü Win32 Saat Entegrasyon Örneğiile karşılaştırabilirsiniz.
Win32 Üzerinde Windows Presentation Framework İçin Bir Kılavuz (HwndSource)
Aşağıdaki grafikte bu öğreticinin hedeflenen son ürünü gösterilmektedir:
Visual Studio'da bir C++ Win32 projesi oluşturarak ve aşağıdakileri oluşturmak için iletişim kutusu düzenleyicisini kullanarak bu iletişim kutusunu yeniden oluşturabilirsiniz:
(HwndSourcekullanmak için Visual Studio kullanmanız gerekmez ve Win32 programları yazmak için C++ kullanmanız gerekmez, ancak bunu yapmak için oldukça tipik bir yoldur ve adım adım öğretici açıklamasına kendini iyi verir).
bir WPF saatini iletişim kutusuna yerleştirmek için beş belirli alt adım gerçekleştirmeniz gerekir:
Visual Studio'da proje ayarlarını değiştirerek Win32 projenizin yönetilen kodu (/clr) çağırmasını etkinleştirin.
Ayrı bir DLL'de WPFPage oluşturun.
WPFPage bir HwndSourceiçine yerleştirin.
Daha büyük Win32 uygulaması içinde HWND'nin nereye yerleştirileceğine karar vermek için Win32'yi kullanın
/Clr
İlk adım, yönetilmeyen bu Win32 projesini yönetilen kodu çağırabilen bir projeye dönüştürmektir. Kullanmak istediğiniz gerekli DLL'lere bağlanacak olan /clr derleyicisi seçeneğini kullanırsınız ve WPF ile kullanılacak Main yöntemini ayarlarsınız.
C++ projesinde yönetilen kodun kullanımını etkinleştirmek için: win32clock projesine sağ tıklayın ve özellikler/clr
olarak değiştirin.
Ardından, WPF için gerekli DLL'lere başvurular ekleyin: PresentationCore.dll, PresentationFramework.dll, System.dll, WindowsBase.dll, UIAutomationProvider.dllve UIAutomationTypes.dll. (Aşağıdaki yönergelerde işletim sisteminin C: sürücüsüne yüklendiği varsayılır.)
win32clock projesine sağ tıklayın ve Başvurular...öğesini seçin ve bu iletişim kutusunun içinde:
win32clock projesine sağ tıklayın ve Başvurular...öğesini seçin.
Yeni BaşvuruEkle'ye tıklayın, Gözat sekmesine gidin, C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\PresentationCore.dllgirin ve Tamam'a tıklayın.
PresentationFramework.dlliçin tekrarlayın: C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\PresentationFramework.dll.
WindowsBase.dlliçin tekrarlayın: C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\WindowsBase.dll.
UIAutomationTypes.dlliçin tekrarlayın: C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\UIAutomationTypes.dll.
UIAutomationProvider.dlliçin tekrarlayın: C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\UIAutomationProvider.dll.
Yeni BaşvuruEkle'yi tıklayın, System.dllöğesini seçin ve Tamamöğesine tıklayın.
win32clock Özellik Sayfalarından çıkmak için başvuru eklerken Tamam'a tıklayın.
Son olarak, WPF ile kullanmak üzere _tWinMain
yöntemine STAThreadAttribute
ekleyin:
[System::STAThreadAttribute]
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
Bu öznitelik, ortak dil çalışma zamanına (CLR), Bileşen Nesne Modeli'ni (COM) başlatırken, WPF (ve Windows Forms) için gerekli olan tek iş parçacıklı daire modelini (STA) kullanması gerektiğini bildirir.
Windows Presentation Framework Sayfası Oluşturma
Ardından WPFPagetanımlayan bir DLL oluşturacaksınız. WPFPage'ı genellikle bağımsız bir uygulama olarak oluşturmak ve bu şekilde WPF bölümünü yazıp hatalarını ayıklamak en kolay yöntemdir. İşlem tamamlandıktan sonra, projeye sağ tıklayarak, Özelliklerseçeneğine tıklayarak, Uygulamaya gidip Çıkış türünü Windows Sınıf Kitaplığı olarak değiştirerek bu proje DLL'ye dönüştürülebilir.
WPF dll projesi daha sonra Win32 projesiyle (iki proje içeren bir çözüm) birleştirilebilir; çözüme sağ tıklayın, Ekle\Mevcut Proje
Win32 projesinden WPF DLL dosyasını kullanmak için bir referans eklemeniz gerekir:
win32clock projesine sağ tıklayın ve Referanslar...öğesini seçin.
Yeni Başvuru Eklebutonuna tıklayın.
Projeler sekmesine tıklayın. WPFClock'u seçin, Tamam'a tıklayın.
Başvuru eklemek üzere win32clock Özellik Sayfalarından çıkmak için Tamam'e tıklayın.
HwndSource
Ardından, WPFPage bir HWND gibi görünmesini sağlamak için HwndSource kullanırsınız. Bu kod bloğunu bir C++ dosyasına eklersiniz:
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();
}
}
}
Bu, bazı açıklamalar kullanabilecek uzun bir kod parçasıdır. İlk bölüm, tüm çağrıları tam olarak nitelemenize gerek kalmaması için çeşitli yan tümcelerden oluşur.
namespace ManagedCode
{
using namespace System;
using namespace System::Windows;
using namespace System::Windows::Interop;
using namespace System::Windows::Media;
Ardından WPF içeriğini oluşturan, çevresinde bir HwndSource yerleştiren ve HWND döndüren bir işlev tanımlarsınız:
HWND GetHwnd(HWND parent, int x, int y, int width, int height) {
İlk olarak, parametreleri CreateWindow'a benzeyen bir HwndSourceoluşturursunuz:
HwndSource^ source = gcnew HwndSource(
0, // class style
WS_VISIBLE | WS_CHILD, // style
0, // exstyle
x, y, width, height,
"hi", // NAME
IntPtr(parent) // parent window
);
Ardından oluşturucusunu çağırarak WPF içerik sınıfını oluşturursunuz:
UIElement^ page = gcnew WPFClock::Clock();
Ardından sayfayı HwndSource'a bağlarsınız:
source->RootVisual = page;
Son satırda HwndSourceiçin HWND değerini döndür:
return (HWND) source->Handle.ToPointer();
Hwnd'i konumlandırma
WPF saatini içeren bir HWND'niz olduğuna göre, bu HWND'yi Win32 iletişim kutusuna yerleştirmeniz gerekir. HWND'yi tam olarak nereye yerleştirebileceğinizi biliyorsanız, bu boyutu ve konumu daha önce tanımladığınız GetHwnd
işlevine geçirmeniz gerekir. Ancak iletişim kutusunu tanımlamak için bir kaynak dosyası kullandınız, bu nedenle HWND'lerin nerede konumlandırıldığından tam olarak emin değilsiniz. Saatin gitmesini istediğiniz yere bir Win32 STATIC denetimi yerleştirmek için Visual Studio iletişim kutusu düzenleyicisini kullanabilir ("Buraya saat ekle") ve WPF saatini konumlandırmak için bunu kullanabilirsiniz.
WM_INITDIALOG işlediğiniz yerde, static yer tutucusunun HWND'sini almak için GetDlgItem
kullanırsınız:
HWND placeholder = GetDlgItem(hDlg, IDC_CLOCK);
Ardından static yer tutucusunun boyutunu ve konumunu hesaplarsınız, böylece WPF saatini bu konuma yerleştirebilirsiniz:
RECT dikdörtgeni;
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);
Ardından STATIC yer tutucusunu gizlersiniz:
ShowWindow(placeholder, SW_HIDE);
Bu konumda WPF saatini HWND ile oluşturun.
HWND clock = ManagedCode::GetHwnd(hDlg, point.x, point.y, width, height);
Öğreticiyi ilginç hale getirmek ve gerçek bir WPF saati oluşturmak için bu noktada bir WPF saat denetimi oluşturmanız gerekir. Bunu, arka planda birkaç olay işleyicisi ile birlikte, çoğunlukla işaretleme dilinde yapabilirsiniz. Bu öğretici birlikte çalışmayı kapsadığından ve kontrol tasarımına odaklanmadığından, WPF saati için tüm kod burada bir kod bloğu olarak sağlanır; bunu oluşturmak için ayrıntılı talimatlar veya her bölümün anlamı olmadan. Denetimin genel görünümünü veya işlevselliğini değiştirmek için bu kodla denemeler yapmaktan çekinmeyin.
İşte işaretleme:
<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>
Arka planda eşlik eden kod da şu şekildedir:
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);
}
}
}
Nihai sonuç şöyle görünür:
Sonucunuzu bu ekran görüntüsünü oluşturan kodla karşılaştırmak için bkz. Win32 Saat Birlikte Çalışma Örneği.
Ayrıca bkz.
.NET Desktop feedback