Aracılığıyla paylaş


İzlenecek yol: Win32'de WPF Saati Barındırma

WPF'yi Win32 uygulamalarının içine yerleştirmek için, HwndSourceWPF içeriğinizi içeren HWND'yi sağlayan kullanın. İlk olarak oluşturarak CreateWindow'a benzer parametreler verirsiniz HwndSource. Ardından içinde olmasını istediğiniz WPF içeriği hakkında bilgi HwndSource edinebilirsiniz. Son olarak, HWND'yi içinden alırsınız HwndSource. Bu kılavuzda, win32 uygulaması içinde işletim sistemi Tarih ve Saat Özellikleri iletişim kutusunu yeniden tanımlayan karma BIR WPF'nin nasıl oluşturulacağı gösterilmektedir .

Ön koşullar

Bkz. WPF ve Win32 Birlikte Çalışma.

Bu Öğreticiyi Kullanma

Bu öğretici, birlikte çalışma uygulaması oluşturmanın önemli adımlarına odaklanır. Öğretici, Win32 Saat Birlikte Çalışma Örneği adlı bir örnek tarafından desteklenmiştir, ancak bu örnek son ürünün yansıtıcıdır. Bu öğreticide, kendi var olan bir Win32 projesiyle (belki de önceden var olan bir projeyle) başlıyor ve uygulamanıza barındırılan bir WPF ekliyorsunuz gibi adımlar belgelenmektedir. Son ürününüzü Win32 Saat Birlikte Çalışma Örneği ile karşılaştırabilirsiniz.

Win32 İçinde Windows Presentation Framework 'ün İzlenecek Yolu (HwndSource)

Aşağıdaki grafikte bu öğreticinin hedeflenen son ürünü gösterilmektedir:

Screenshot that shows the Date and Time Properties dialog box.

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:

Recreated Date and Time Properties dialog box

(kullanmak HwndSourceiç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:

  1. Visual Studio'da proje ayarlarını değiştirerek Win32 projenizin yönetilen kodu (/clr) çağırmasını etkinleştirin.

  2. Ayrı bir DLL'de WPFPage oluşturun.

  3. BU WPF'yiPage içine HwndSourceyerleştirin.

  4. özelliğini kullanarak Handle bunun Page için bir HWND alın.

  5. 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 kod kullanımını etkinleştirmek için: win32clock projesine sağ tıklayın ve Özellikler'i seçin. Genel özellik sayfasında (varsayılan), Ortak Dil Çalışma Zamanı desteğini olarak /clrdeğiştirin.

Ardından WPF için gereken DLL'lere başvurular ekleyin: PresentationCore.dll, PresentationFramework.dll, System.dll, WindowsBase.dll, UIAutomationProvider.dll ve UIAutomationTypes.dll. (Aşağıdaki yönergelerde işletim sisteminin C: sürücüsüne yüklendiği varsayılır.)

  1. win32clock projesine sağ tıklayın ve Başvurular... öğesini seçin ve bu iletişim kutusunun içinde:

  2. win32clock projesine sağ tıklayın ve Başvurular... seçeneğini belirleyin.

  3. Yeni Başvuru Ekle'ye tıklayın, Gözat sekmesine tıklayın, C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\PresentationCore.dll girin ve Tamam'a tıklayın.

  4. PresentationFramework.dll için yineleyin: C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\PresentationFramework.dll.

  5. WindowsBase.dll için yineleyin: C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\WindowsBase.dll.

  6. UIAutomationTypes.dll için tekrarlayın: C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\UIAutomationTypes.dll.

  7. UIAutomationProvider.dll için yineleyin: C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\UIAutomationProvider.dll.

  8. Yeni Başvuru Ekle'ye tıklayın, System.dll'yi seçin ve Tamam'a tıklayın.

  9. Başvuru eklemek için win32clock Özellik Sayfalarından çıkmak için Tamam'a tıklayın.

Son olarak, WPF ile kullanmak için yöntemine _tWinMain öğesini ekleyinSTAThreadAttribute:

[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 bir iş parçacıklı daire modeli (STA) kullanması gerektiğini bildirir.

Windows Presentation Framework Sayfası Oluşturma

Ardından, WPFPage tanımlayan bir DLL oluşturursunuz. WPF'yi tek başına bir uygulama olarak oluşturmak ve WPFPage bölümünü bu şekilde yazmak ve hatalarını ayıklamak genellikle en kolay yöntemdir. İşlem tamamlandıktan sonra, projeye sağ tıklayıp Özellikler'e tıklayarak, Uygulama'ya 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'yi seçin.

Win32 projesinden bu WPF dll dosyasını kullanmak için bir başvuru eklemeniz gerekir:

  1. win32clock projesine sağ tıklayın ve Başvurular... seçeneğini belirleyin.

  2. Yeni Başvuru Ekle'ye tıklayın.

  3. Projeler sekmesine tıklayın. WPFClock'u seçin, Tamam'a tıklayın.

  4. Başvuru eklemek için win32clock Özellik Sayfalarından çıkmak için Tamam'a tıklayın.

Hwndsource

Ardından, WPF'ninPage HWND gibi görünmesini sağlamak için kullanırsınızHwndSource. 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 nitelemeniz gerekmemesi için çeşitli yan tümceleridir:

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, içine bir ekleyen ve HWND döndüren bir HwndSource 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ı öğesine HwndSourcebağlarsınız:

source->RootVisual = page;

Son satırda, için HWND'yi HwndSourcedöndürü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 işleve GetHwnd 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 değerini almak için kullanırsınız GetDlgItem :

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

Ve bu konumda WPF saati HWND'sini 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 yalnızca birkaç olay işleyicisi arka planda olacak şekilde çoğunlukla işaretlemede yapabilirsiniz. Bu öğretici birlikte çalışmayla ilgili olduğundan ve denetim tasarımıyla ilgili olmadığından, WPF saati için tam kod burada bir kod bloğu olarak sağlanır; bunu oluşturmak için ayrı yönergeler 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.

İşaretlemeyi aşağıda bulabilirsiniz:

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

Final result Date and Time Properties dialog box

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.