برنامج تعليمي إنشاء تطبيق Win32 يستضيف محتوى WPF
لوضع WPF داخل تطبيقات Win32 قم باستخدام HwndSource ، والتي توفر HWND الذي يحتوي على محتوى WPF الخاص بك. أولاً قم بإنشاء HwndSource ، بإعطائها معلمات مشابهة لـ CreateWindow. ثم قم بتعريف HwndSource بمحتوىWPF الذي تريده داخله. وأخيراً، يمكنك الحصول على HWND خارج HwndSource. توضح هذه الإرشادات التفصيلية كيفية إنشاء WPF مختلط داخل تطبيق Win32 الذي يقوم باعادة تنفيذ مربع حوار خصائص الوقت والتاريخ الخاص بنظام التشغيل.
المتطلبات الأساسية
انظر نظرة عامة حول التشغيل التفاعلي ل Win32 و WPF
كيفية استخدام هذا البرنامج التعليمى
هذا البرنامج التعليمي يركز على الخطوات الهامة لإنتاج تطبيق متداخل التشغيل. يتم نسخ البرنامج التعليمي بواسطة عينة Win32 Clock Interoperation SampleWin 32 Clock Interoperation Sample ، ولكن ذلك النموذج يعكس المنتج النهائى. هذا البرنامج التعليمي يوثق الخطوات كما لو تم بدء تشغيل مشروع Win32موجود خاص بك ، ربما موجود مسبقاً ، ثم قمت باضافةWPF مستضاف إلى التطبيق الخاص بك. يمكنك مقارنة المنتج النهائى الخاص بك مع Win32 Clock Interoperation Sample.
شرح تفصيلى لـ Windows Presentation Framework Inside Win32 (HwndSource)
يوضح الرسم التالي المنتج النهائي المقصود من هذا البرنامج التعليمي:
يمكنك إعادة إنشاء مربع الحوار هذا بإنشاء مشروع C++ Win32 في Microsoft Visual Studio ، ثم استخدام محرر الحوار لإنشاء التالي:
(لا تحتاج إلى استخدام Microsoft Visual Studio لاستخدام HwndSource ، و لا تحتاج إلى استخدام C++ لكتابة برامج Win32 ولكن هذه طريقة نموذجية للقيام بذلك ويميل بنفسه بشكل جيد إلى تفسير البرنامج التعليمي كخطوات).
تحتاج إلى إنجاز خمس خطوات فرعية معينة لوضع ساعة WPF داخل مربع الحوار:
قم بتمكين مشروع Win32 الخاص بك من استدعاء تعليمات برمجية مدارة ( /clr) عن طريق تغيير إعدادات المشروع في Microsoft Visual Studio.
قم بإنشاء WPFPage في DLL منفصل.
وضع هذا WPFPage داخل HwndSource.
قم باستخدام Win32 لتحديد مكان وضع HWND داخل أكبر تطبيق Win32
CLR
الخطوة الأولى هى تحويل هذا المشروع غير المدار Win32 إلى مشروع يمكنه استدعاء التعليمات البرمجية المدارة. يمكنك استخدام خيار برنامج التحويل البرمجي /clr الذى سيرتبط بـالـ DLLs الضرورية التي تريد استخدامها وضبط الأسلوب الرئيسي للاستخدام بـ WPF.
لتمكين استخدام تعليمات برمجية يمكن إدارتها داخل مشروع C++: انقر بالزر الأيمن للماوس فوق المشروع win32clock واختر Properties . على صفحة الخاصية General (الافتراضية) ، قم بتغيير دعم اللغة العامة لوقت التشغيل إلى /clr.
بعد ذلك ، قم باضافة مراجع DLLs الضرورية من أجل WPF: PresentationCore.dll, PresentationFramework.dll, النظام.dll, WindowsBase.dll, UIAutomationProvider.dll, و UIAutomationTypes.dll. (Following instructions assume the operating النظام هو installed تشغيل C: محرك الأقراص
انقر بالزر الأيمن فوق المشروع win32clock وحدد References... ، وداخل مربع الحوار هذا:
انقر بالزر الأيمن فوق المشروع win32clock وحدد المراجع....
انقر فوق إضافة مرجع جديد وانقر فوق تبويب الاستعراض وقم بادخال Assemblies\Microsoft\Framework\v3.0\PresentationCore.dll Files\Reference C:\صواب ثم انقر فوق موافق.
قم بالمثل لـ PresentationFramework.dll: Assemblies\Microsoft\Framework\v3.0\PresentationFramework.dll Files\Reference C:\.
قم بالمثل لـ WindowsBase.dll: C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\WindowsBase.dll.
قم بالمثل لـ UIAutomationTypes.dll: C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\UIAutomationTypes.dll.
قم بالمثل لـ UIAutomationProvider. : C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\UIAutomationProvider.dll.
انقر فوق اضافة مرجع جديد وحدد System.dll ثم انقر فوق موافق .
انقر فوق موافق لإنهاء "صفحات الخصائص" الخاصة بـ win32clock لاضافة المراجع.
وأخيراً، قم بإضافة STAThreadAttribute إلى الأسلوب _tWinMain للاستخدام مع WPF:
[System::STAThreadAttribute]
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
تقوم هذه السمة بإعلام وقت تشغيل اللغة العامة (CLR) عندما تقوم بتهيئة طراز كائن المكون (COM) ، فإنه يجب استخدام طراز ترابط واحد (STA) ، وهو ضروري من أجل WPF (و Windows Forms).
قم بانشاء صفحة Windows Presentation Framework
بعد ذلك ، قم بانشاء DLL التى تعرّف WPFPage. غالباً يكون من الأسهل إنشاء WPFPage كتطبيق مستقل وكتابة و تصحيح جزء WPF بهذه الطريقة. بمجرد أن يتم هذا ، يمكن أن يتم تحويل هذا المشروع إلى DLL بواسطة النقر بالزر الأيمن فوق المشروع ثم النقر فوق Properties ، الانتقال إلى "تطبيق" ، وتغيير نوع الإخراج إلى مكتبة فئات الـ Windows.
مشروع الـ dll WPF يمكن ان يتم ضمه مع مشروع Win32 (حل واحد يحتوي على مشروعين) – انقر بزر الماوس الأيمن على الحل، حدد Add\Existing Project.
لاستخدام هذا الـ dll WPF من المشروع Win32 ، تحتاج إلى اضافة مرجع:
انقر بالزر الأيمن فوق المشروع win32clock وحدد المراجع....
انقر فوق اضافة مرجع جديد .
انقر فوق علامة التبويب المشاريع. حدد WPFClock, انقر فوق موافق.
انقر فوق موافق لإنهاء "صفحات الخصائص" الخاصة بـ win32clock لاضافة المراجع.
HwndSource
بعد ذلك، استخدم HwndSource لجعل WPFPage تشبه HWND. إضافة هذه الكتلة من التعليمات البرمجية الى ملف 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();
}
}
}
هذه قطعة طويلة من التعليمات البرمجية التي قد تستخدم بعض الشرح. الجزء الأول عبارات متعددة بحيث لا تحتاج الى تأهيل كافة الاستدعاءات بشكل كامل:
namespace ManagedCode
{
using namespace System;
using namespace System::Windows;
using namespace System::Windows::Interop;
using namespace System::Windows::Media;
ثم تقوم بتعريف وظيفة تنشئ محتوى WPF ويضع HwndSource حولها ويقوم بإرجاع HWND:
HWND GetHwnd(HWND parent, int x, int y, int width, int height) {
أولاً قم بإنشاء HwndSource ، الذى له معلمات مشابهة لـ 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
);
ثم قم بانشاء محتوى فئة WPF عن طريق استدعاء المُنشئ الخاص بها:
UIElement^ page = gcnew WPFClock::Clock();
قم بربط الصفحة الى HwndSource:
source->RootVisual = page;
في السطر الأخير قم بارجاع HWND من أجل HwndSource:
return (HWND) source->Handle.ToPointer();
تموضع Hwnd
والآن لديك HWND الذي يحتوي على ساعة WPF ، تحتاج الى وضع هذا الـ HWND داخل مربع الحوار Win32. إذا علمت فقط أين تضع الـ HWND ، ستقوم فقط بتمرير الحجم والموقع الى الوظيفة GetHwnd المعرفة سابقاً. ولكنك استخدمت ملف مورد لتعريف مربع حوار ، بحيث تكون غير متأكد تماماً من حيث يتم وضع أي HWNDs. يمكنك استخدام مربع الحوار Microsoft Visual Studio لوضع تحكم ثابت Win32 حيث تريد الساعة أن تنتقل (“ إدراج الساعة هنا ”) ، واستخدم هذا لوضع الساعة WPF.
حيث تقوم بمعالجة WM_INITDIALOG قم باستخدام GetDlgItem لاسترداد الـ HWND في العنصر النائب ثابت:
HWND placeholder = GetDlgItem(hDlg, IDC_CLOCK);
قم بعد ذلك بحساب حجم وموضع العنصر النائب ثابت ، بحيث يمكنك وضع الساعة WPF في هذا المكان:
مستطيلات
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);
ثم قم بإخفاء العنصر النائب ثابت:
ShowWindow(placeholder, SW_HIDE);
وتقوم بإنشاء ساعة WPF HWND في هذا الموقع:
HWND clock = ManagedCode::GetHwnd(hDlg, point.x, point.y, width, height);
لجعل البرنامج التعليمي شيق ولإنتاج ساعة WPF حقيقية ، ستحتاج إلى إنشاء عنصر تحكم ساعة WPF عند هذه النقطة. يمكنك القيام بذلك غالباً في العلامات ، بكمية قليلة من معالجات الأحداث فقط فى التعليمات البرمجية الخلفية. حيث هذا البرنامج التعليمي حول التشغيل المتداخل وليس حول تصميم التحكم التعليمات البرمجية الكاملة لساعة WPF تتوفر هنا ككتلة من التعليمات البرمجية ، بدون إرشادات منفصلة لإنشائها أو ما يعني كل جزء. لا تتردد فى تجربة هذه التعليمات البرمجية لتغيير مظهر وأسلوب عرض أو وظيفة التحكم.
وفيما يلي العلامات:
<Page x:Class="WPFClock.Clock"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://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>
التعليمات البرمجية الخلفية المصاحبة
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);
}
}
}
النتيجة النهائية تبدو:
لمقارنة النتيجة النهائية الخاصة بك إلى التعليمات البرمجية التي تم إنتاجها من هذه اللقطة ، راجع Win32 Clock Interoperation Sample.
راجع أيضًا:
المرجع
المبادئ
نظرة عامة حول التشغيل التفاعلي ل Win32 و WPF