Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Windows Çalışma Zamanı Oluşturma API'lerini (Visual layer olarak da adlandırılır) Windows Presentation Foundation (WPF) uygulamalarınızda kullanarak Windows kullanıcılarınıza yönelik modern deneyimler oluşturabilirsiniz.
Bu öğreticinin tüm kodu GitHub: WPF HelloComposition sample adresinde bulunabilir.
Prerequisites
UWP XAML barındırma API'sinin bu önkoşulları vardır.
- WPF ve UWP kullanarak uygulama geliştirme hakkında bilgi sahibi olduğunuzu varsayıyoruz. Daha fazla bilgi için bkz:
- .NET Framework 4.7.2 veya sonraki bir sürümü
- Windows 10 sürüm 1803 veya üzeri
- Windows 10 SDK 17134 veya üzeri
WPF'de Oluşturma API'lerini kullanma
Bu öğreticide basit bir WPF uygulaması kullanıcı arabirimi oluşturacak ve ona animasyonlu Composition öğeleri ekleyeceksiniz. Hem WPF hem de Oluşturma bileşenleri basit tutulur, ancak bileşenlerin karmaşıklığından bağımsız olarak gösterilen birlikte çalışma kodu aynıdır. Tamamlanmış uygulama şöyle görünür.
WPF projesi oluşturun
İlk adım, kullanıcı arabirimi için bir uygulama tanımı ve XAML sayfası içeren WPF uygulama projesi oluşturmaktır.
Visual C# uygulamasında
Visual Studio'yu açın ve Dosya>Yeni>Projeöğesini seçin.
Yeni Proje iletişim kutusu açılır.
Yüklü kategorisinin altında Visual C# düğümünü genişletin ve Windows Masaüstü öğesini seçin.
WPF Uygulaması (.NET Framework) şablonunu seçin.
HelloComposition adını girin, Framework .NET Framework 4.7.2'i seçin ve OK'ye tıklayın.
Visual Studio projeyi oluşturur ve MainWindow.xaml adlı varsayılan uygulama penceresi için tasarımcıyı açar.
Projeyi Windows Çalışma Zamanı API'lerini kullanacak şekilde yapılandırma
WPF uygulamanızda Windows Çalışma Zamanı (WinRT) API'lerini kullanmak için Visual Studio projenizi Windows Çalışma Zamanı erişecek şekilde yapılandırmanız gerekir. Buna ek olarak, vektörler Oluşturma API'leri tarafından yaygın olarak kullanılır, bu nedenle vektörleri kullanmak için gereken başvuruları eklemeniz gerekir.
NuGet paketleri, bu gereksinimlerin her ikisini de karşılamak için kullanılabilir. Projenize gerekli başvuruları eklemek için bu paketlerin en son sürümlerini yükleyin.
- Microsoft.Windows. SDK. Contracts (Varsayılan paket yönetimi biçiminin PackageReference olarak ayarlanmasını gerektirir.)
- System.Numerics.Vectors
Note
Projenizi yapılandırmak için NuGet paketlerini kullanmanızı öneririz ancak gerekli başvuruları el ile ekleyebilirsiniz. Daha fazla bilgi için bkz. Windows için masaüstü uygulamanızı kullanma. Aşağıdaki tabloda referans eklemeniz gereken dosyalar gösterilmektedir.
| File | Konum |
|---|---|
| System.Runtime.WindowsRuntime | C:\Windows\Microsoft.NET\Framework\v4.0.30319 |
| Windows.Foundation.UniversalApiContract.winmd | C:\Program Files (x86)\Windows Kits\10\References<sdk version>\Windows.Foundation.UniversalApiContract<version> |
| Windows.Foundation.FoundationContract.winmd | C:\Program Files (x86)\Windows Kits\10\References<sdk version>\Windows. Foundation.FoundationContract<version> |
| System.Numerics.Vectors.dll | C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Numerics.Vectors\v4.0_4.0.0.0__b03f5f7f11d50a3a |
| System.Numerics.dll | C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETFramework\v4.7.2 |
Projeyi monitör başına DPI algılamalı olacak şekilde yapılandırma
Uygulamanıza eklediğiniz görsel katman içeriği, gösterildiği ekranın DPI ayarlarıyla eşleşecek şekilde otomatik olarak ölçeklendirilmiyor. Uygulamanız için monitör başına DPI farkındalığını etkinleştirmeniz ve ardından görsel katmanı içeriğinizi oluşturmak için kullandığınız kodun uygulama çalıştırıldığında geçerli DPI ölçeğini dikkate aldığından emin olmanız gerekir. Burada projeyi DPI'ye duyarlı olacak şekilde yapılandıracağız. Sonraki bölümlerde görsel katmanı içeriğini ölçeklendirmek için DPI bilgilerinin nasıl kullanılacağını göstereceğiz.
WPF uygulamaları varsayılan olarak Sistem DPI'sine duyarlıdır, ancak bir app.manifest dosyasında kendilerini monitör başına DPI algılayan olarak belirtmeleri gerekir. Uygulama bildirim dosyasında Windows düzey monitör başına DPI farkındalığını açmak için:
Çözüm Gezginipenceresinde, HelloComposition projesine sağ tıklayın.
Bağlam menüsündeYeni Öğe Ekle...'> seçin.
Yeni Öğe Ekle iletişim kutusunda 'Uygulama Bildirim Dosyası'nı seçin ve ekle'ye tıklayın. (Varsayılan adı bırakabilirsiniz.)
app.manifest dosyasında bu xml dosyasını bulun ve açıklamayı kaldırın:
<application xmlns="urn:schemas-microsoft-com:asm.v3"> <windowsSettings> <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware> </windowsSettings> </application>Bu ayarı açılış
<windowsSettings>etiketinden sonra ekleyin:<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitor</dpiAwareness>Ayrıca App.config dosyasında DoNotScaleForDpiChanges ayarını da ayarlamanız gerekir.
App.Config dosyasını açın ve şu xml dosyasını öğesinin
<configuration>içine ekleyin:<runtime> <AppContextSwitchOverrides value="Switch.System.Windows.DoNotScaleForDpiChanges=false"/> </runtime>
Note
AppContextSwitchOverrides yalnızca bir kez ayarlanabilir. Uygulamanızın zaten bir kümesi varsa, bu anahtarı value özniteliği içinde noktalı virgülle sınırlandırmalısınız.
(Daha fazla bilgi için bkz. Per monitor DPI Geliştirici Kılavuzu ve örnekler GitHub.)
Bileşen öğelerini barındırmak için HwndHost'tan türetilmiş bir sınıf oluşturun.
Görsel katmanıyla oluşturduğunuz içeriği barındırmak için HwndHost'tan türetilen bir sınıf oluşturmanız gerekir. Burası, Oluşturma API'lerini barındırmak için yapılandırmanın çoğunu yaptığınız yerdir. Bu sınıfta, oluşturma API'lerini WPF uygulamanıza getirmek için Platform Çağırma Hizmetleri (PInvoke) ve COM Birlikte Çalışma kullanırsınız. PInvoke ve COM Birlikte Çalışma hakkında daha fazla bilgi için bkz. Yönetilmeyen kodla birlikte çalışma.
Tavsiye
Gerekirse, öğreticiyi takip ederken tüm kodun doğru yerlerde olduğundan emin olmak için, öğreticinin sonundaki tüm kodu kontrol edin.
Projenize HwndHost'tan türetilen yeni bir sınıf dosyası ekleyin.
- Çözüm Gezginipenceresinde, HelloComposition projesine sağ tıklayın.
- Bağlam menüsündeSınıf Ekle...'> seçin.
- Yeni Öğe Ekle iletişim kutusunda sınıfı CompositionHost.cs adlandırın ve Ekle'ye tıklayın.
CompositionHost.cs'da, HwndHost'tan türetmek için sınıf tanımını düzenleyin.
// Add // using System.Windows.Interop; namespace HelloComposition { class CompositionHost : HwndHost { } }Sınıfına aşağıdaki kodu ve oluşturucuyu ekleyin.
// Add // using Windows.UI.Composition; IntPtr hwndHost; int hostHeight, hostWidth; object dispatcherQueue; ICompositionTarget compositionTarget; public Compositor Compositor { get; private set; } public Visual Child { set { if (Compositor == null) { InitComposition(hwndHost); } compositionTarget.Root = value; } } internal const int WS_CHILD = 0x40000000, WS_VISIBLE = 0x10000000, LBS_NOTIFY = 0x00000001, HOST_ID = 0x00000002, LISTBOX_ID = 0x00000001, WS_VSCROLL = 0x00200000, WS_BORDER = 0x00800000; public CompositionHost(double height, double width) { hostHeight = (int)height; hostWidth = (int)width; }BuildWindowCore ve DestroyWindowCore yöntemlerini geçersiz kılın.
Note
BuildWindowCore'da InitializeCoreDispatcher ve InitComposition yöntemlerini çağırırsınız. Bu yöntemleri sonraki adımlarda oluşturacaksınız.
// Add // using System.Runtime.InteropServices; protected override HandleRef BuildWindowCore(HandleRef hwndParent) { // Create Window hwndHost = IntPtr.Zero; hwndHost = CreateWindowEx(0, "static", "", WS_CHILD | WS_VISIBLE, 0, 0, hostWidth, hostHeight, hwndParent.Handle, (IntPtr)HOST_ID, IntPtr.Zero, 0); // Create Dispatcher Queue dispatcherQueue = InitializeCoreDispatcher(); // Build Composition tree of content InitComposition(hwndHost); return new HandleRef(this, hwndHost); } protected override void DestroyWindowCore(HandleRef hwnd) { if (compositionTarget.Root != null) { compositionTarget.Root.Dispose(); } DestroyWindow(hwnd.Handle); }- CreateWindowEx ve DestroyWindow için PInvoke bildirimi gerekir. Bu bildirimi sınıfı için kodun sonuna yerleştirin.
#region PInvoke declarations [DllImport("user32.dll", EntryPoint = "CreateWindowEx", CharSet = CharSet.Unicode)] internal static extern IntPtr CreateWindowEx(int dwExStyle, string lpszClassName, string lpszWindowName, int style, int x, int y, int width, int height, IntPtr hwndParent, IntPtr hMenu, IntPtr hInst, [MarshalAs(UnmanagedType.AsAny)] object pvParam); [DllImport("user32.dll", EntryPoint = "DestroyWindow", CharSet = CharSet.Unicode)] internal static extern bool DestroyWindow(IntPtr hwnd); #endregion PInvoke declarationsCoreDispatcher ile bir iş parçacığını başlatın. Çekirdek dağıtıcı, WinRT API'leri için pencere iletilerini işlemek ve olayları göndermekle sorumludur. CoreDispatcher'ın yeni örnekleri, CoreDispatcher içeren bir iş parçacığında oluşturulmalıdır.
- InitializeCoreDispatcher adlı bir yöntem oluşturun ve dağıtıcı kuyruğunun ayarlanması için kod ekleyin.
private object InitializeCoreDispatcher() { DispatcherQueueOptions options = new DispatcherQueueOptions(); options.apartmentType = DISPATCHERQUEUE_THREAD_APARTMENTTYPE.DQTAT_COM_STA; options.threadType = DISPATCHERQUEUE_THREAD_TYPE.DQTYPE_THREAD_CURRENT; options.dwSize = Marshal.SizeOf(typeof(DispatcherQueueOptions)); object queue = null; CreateDispatcherQueueController(options, out queue); return queue; }- Dağıtıcı kuyruğu bir PInvoke bildirimi de gerektirir. Bu bildirimi, önceki adımda oluşturduğunuz PInvoke bildirimleri bölgesinin içine yerleştirin.
//typedef enum DISPATCHERQUEUE_THREAD_APARTMENTTYPE //{ // DQTAT_COM_NONE, // DQTAT_COM_ASTA, // DQTAT_COM_STA //}; internal enum DISPATCHERQUEUE_THREAD_APARTMENTTYPE { DQTAT_COM_NONE = 0, DQTAT_COM_ASTA = 1, DQTAT_COM_STA = 2 }; //typedef enum DISPATCHERQUEUE_THREAD_TYPE //{ // DQTYPE_THREAD_DEDICATED, // DQTYPE_THREAD_CURRENT //}; internal enum DISPATCHERQUEUE_THREAD_TYPE { DQTYPE_THREAD_DEDICATED = 1, DQTYPE_THREAD_CURRENT = 2, }; //struct DispatcherQueueOptions //{ // DWORD dwSize; // DISPATCHERQUEUE_THREAD_TYPE threadType; // DISPATCHERQUEUE_THREAD_APARTMENTTYPE apartmentType; //}; [StructLayout(LayoutKind.Sequential)] internal struct DispatcherQueueOptions { public int dwSize; [MarshalAs(UnmanagedType.I4)] public DISPATCHERQUEUE_THREAD_TYPE threadType; [MarshalAs(UnmanagedType.I4)] public DISPATCHERQUEUE_THREAD_APARTMENTTYPE apartmentType; }; //HRESULT CreateDispatcherQueueController( // DispatcherQueueOptions options, // ABI::Windows::System::IDispatcherQueueController** dispatcherQueueController //); [DllImport("coremessaging.dll", EntryPoint = "CreateDispatcherQueueController", CharSet = CharSet.Unicode)] internal static extern IntPtr CreateDispatcherQueueController(DispatcherQueueOptions options, [MarshalAs(UnmanagedType.IUnknown)] out object dispatcherQueueController);Artık dağıtıcı kuyruğunuz hazır ve Oluşturma içeriğini oluşturmaya başlayabilirsiniz.
Compositor'ı başlatın. Compositor, Windows.UI.Composition ad alanındaki görselleri, efekt sistemini ve animasyon sistemini kapsayan çeşitli türler oluşturan bir fabrikadır. Compositor sınıfı, fabrikadan oluşturulan nesnelerin yaşam ömrünü de yönetir.
private void InitComposition(IntPtr hwndHost) { ICompositorDesktopInterop interop; compositor = new Compositor(); object iunknown = compositor as object; interop = (ICompositorDesktopInterop)iunknown; IntPtr raw; interop.CreateDesktopWindowTarget(hwndHost, true, out raw); object rawObject = Marshal.GetObjectForIUnknown(raw); ICompositionTarget target = (ICompositionTarget)rawObject; if (raw == null) { throw new Exception("QI Failed"); } }- ICompositorDesktopInterop ve ICompositionTarget , COM içeri aktarmaları gerektirir. Bu kodu CompositionHost sınıfından sonra ancak ad alanı bildiriminin içine yerleştirin.
#region COM Interop /* #undef INTERFACE #define INTERFACE ICompositorDesktopInterop DECLARE_INTERFACE_IID_(ICompositorDesktopInterop, IUnknown, "29E691FA-4567-4DCA-B319-D0F207EB6807") { IFACEMETHOD(CreateDesktopWindowTarget)( _In_ HWND hwndTarget, _In_ BOOL isTopmost, _COM_Outptr_ IDesktopWindowTarget * *result ) PURE; }; */ [ComImport] [Guid("29E691FA-4567-4DCA-B319-D0F207EB6807")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface ICompositorDesktopInterop { void CreateDesktopWindowTarget(IntPtr hwndTarget, bool isTopmost, out IntPtr test); } //[contract(Windows.Foundation.UniversalApiContract, 2.0)] //[exclusiveto(Windows.UI.Composition.CompositionTarget)] //[uuid(A1BEA8BA - D726 - 4663 - 8129 - 6B5E7927FFA6)] //interface ICompositionTarget : IInspectable //{ // [propget] HRESULT Root([out] [retval] Windows.UI.Composition.Visual** value); // [propput] HRESULT Root([in] Windows.UI.Composition.Visual* value); //} [ComImport] [Guid("A1BEA8BA-D726-4663-8129-6B5E7927FFA6")] [InterfaceType(ComInterfaceType.InterfaceIsIInspectable)] public interface ICompositionTarget { Windows.UI.Composition.Visual Root { get; set; } } #endregion COM Interop
İçeriğinizi WPF görsel ağacına eklemek için UserControl oluşturma
Oluşturma içeriğini barındırmak için gereken altyapıyı ayarlamanın son adımı, HwndHost'u WPF görsel ağacına eklemektir.
UserControl Oluşturma
UserControl, Oluşturma içeriğini oluşturan ve yöneten kodunuzu paketlemenin ve içeriği XAML'nize kolayca eklemenin kullanışlı bir yoludur.
Projenize yeni bir kullanıcı denetimi dosyası ekleyin.
- Çözüm Gezginipenceresinde, HelloComposition projesine sağ tıklayın.
- Bağlam menüsündeKullanıcı Denetimi Ekle...'> seçin.
- Yeni Öğe Ekle iletişim kutusunda kullanıcı denetimini CompositionHostControl.xaml olarak adlandırın ve Ekle'ye tıklayın.
Hem CompositionHostControl.xaml hem de CompositionHostControl.xaml.cs dosyaları oluşturulur ve projenize eklenir.
CompositionHostControl.xaml dosyasında, HwndHost'unuzun yer alacağı XAML kapsayıcısı olan bu Border öğesiyle
<Grid> </Grid>etiketlerini değiştirin.<Border Name="CompositionHostElement"/>
Kullanıcı denetiminin kodunda, önceki adımda oluşturduğunuz CompositionHost sınıfının bir örneğini oluşturur ve bunu XAML sayfasında oluşturduğunuz Kenarlık olan CompositionHostElement öğesinin alt öğesi olarak eklersiniz.
CompositionHostControl.xaml.cs'da, Oluşturma kodunuzda kullanacağınız nesneler için özel değişkenler ekleyin. Bunları sınıf tanımından sonra ekleyin.
CompositionHost compositionHost; Compositor compositor; Windows.UI.Composition.ContainerVisual containerVisual; DpiScale currentDpi;Kullanıcı denetiminin Loaded olayı için bir işleyici ekleyin. Burası, CompositionHost örneğinizi ayarladığınız yerdir.
- Oluşturucuda, burada
Loaded += CompositionHostControl_Loaded;() gösterildiği gibi olay işleyicisini bağlayın.
public CompositionHostControl() { InitializeComponent(); Loaded += CompositionHostControl_Loaded; }- CompositionHostControl_Loaded adlı olay işleyici yöntemini ekleyin.
private void CompositionHostControl_Loaded(object sender, RoutedEventArgs e) { // If the user changes the DPI scale setting for the screen the app is on, // the CompositionHostControl is reloaded. Don't redo this set up if it's // already been done. if (compositionHost is null) { currentDpi = VisualTreeHelper.GetDpi(this); compositionHost = new CompositionHost(ControlHostElement.ActualHeight, ControlHostElement.ActualWidth); ControlHostElement.Child = compositionHost; compositor = compositionHost.Compositor; containerVisual = compositor.CreateContainerVisual(); compositionHost.Child = containerVisual; } }Bu yöntemde, Oluşturma kodunuzda kullanacağınız nesneleri ayarlarsınız. Burada neler olduğuna hızlıca göz atabilirsiniz.
- İlk olarak, Bir CompositionHost örneğinin zaten var olup olmadığını denetleyerek kurulumun yalnızca bir kez yapıldığından emin olun.
// If the user changes the DPI scale setting for the screen the app is on, // the CompositionHostControl is reloaded. Don't redo this set up if it's // already been done. if (compositionHost is null) { }- Geçerli DPI'yi alın. Bu, Oluşturma öğelerinizi düzgün bir şekilde ölçeklendirmek için kullanılır.
currentDpi = VisualTreeHelper.GetDpi(this);- Bir CompositionHost örneği oluşturun ve bunu Border, CompositionHostElement öğesinin Alt Öğesi olarak atayın.
compositionHost = new CompositionHost(ControlHostElement.ActualHeight, ControlHostElement.ActualWidth); ControlHostElement.Child = compositionHost;- Compositor'ı CompositionHost'tan alın.
compositor = compositionHost.Compositor;- Kapsayıcı görseli oluşturmak için Compositor'ı kullanın. Bu, Oluşturma öğelerinizi eklediğiniz Oluşturma kapsayıcısıdır.
containerVisual = compositor.CreateContainerVisual(); compositionHost.Child = containerVisual;- Oluşturucuda, burada
Oluşturma öğeleri ekleme
Altyapı yerleşik olarak, artık göstermek istediğiniz Oluşturma içeriğini oluşturabilirsiniz.
Bu örnekte, basit bir kare SpriteVisual oluşturan ve onu canlandıran kodu eklersiniz.
Bir bileşim öğesi ekleyin. CompositionHostControl.xaml.cs dosyasında, bu yöntemleri CompositionHostControl sınıfına ekleyin.
// Add // using System.Numerics; public void AddElement(float size, float offsetX, float offsetY) { var visual = compositor.CreateSpriteVisual(); visual.Size = new Vector2(size, size); visual.Scale = new Vector3((float)currentDpi.DpiScaleX, (float)currentDpi.DpiScaleY, 1); visual.Brush = compositor.CreateColorBrush(GetRandomColor()); visual.Offset = new Vector3(offsetX * (float)currentDpi.DpiScaleX, offsetY * (float)currentDpi.DpiScaleY, 0); containerVisual.Children.InsertAtTop(visual); AnimateSquare(visual, 3); } private void AnimateSquare(SpriteVisual visual, int delay) { float offsetX = (float)(visual.Offset.X); // Already adjusted for DPI. // Adjust values for DPI scale, then find the Y offset that aligns the bottom of the square // with the bottom of the host container. This is the value to animate to. var hostHeightAdj = CompositionHostElement.ActualHeight * currentDpi.DpiScaleY; var squareSizeAdj = visual.Size.Y * currentDpi.DpiScaleY; float bottom = (float)(hostHeightAdj - squareSizeAdj); // Create the animation only if it's needed. if (visual.Offset.Y != bottom) { Vector3KeyFrameAnimation animation = compositor.CreateVector3KeyFrameAnimation(); animation.InsertKeyFrame(1f, new Vector3(offsetX, bottom, 0f)); animation.Duration = TimeSpan.FromSeconds(2); animation.DelayTime = TimeSpan.FromSeconds(delay); visual.StartAnimation("Offset", animation); } } private Windows.UI.Color GetRandomColor() { Random random = new Random(); byte r = (byte)random.Next(0, 255); byte g = (byte)random.Next(0, 255); byte b = (byte)random.Next(0, 255); return Windows.UI.Color.FromArgb(255, r, g, b); }
DPI değişikliklerini işleme
Öğe ekleme ve öğeye animasyon ekleme kodu, öğeler oluşturulduğunda geçerli DPI ölçeğini dikkate alır, ancak uygulama çalışırken DPI değişikliklerini de dikkate almanız gerekir. Değişikliklerden haberdar olmak için HwndHost.DpiChanged olayını işleyebilir ve hesaplamalarınızı yeni DPI'ye göre ayarlayabilirsiniz.
CompositionHostControl_Loaded yönteminde, son satırdan sonra DpiChanged olay işleyicisini bağlamak için bunu ekleyin.
compositionHost.DpiChanged += CompositionHost_DpiChanged;CompositionHostDpiChanged adlı olay işleyici yöntemini ekleyin. Bu kod, her öğenin ölçeğini ve uzaklığını ayarlar ve tamamlanmamış animasyonları yeniden hesaplar.
private void CompositionHost_DpiChanged(object sender, DpiChangedEventArgs e) { currentDpi = e.NewDpi; Vector3 newScale = new Vector3((float)e.NewDpi.DpiScaleX, (float)e.NewDpi.DpiScaleY, 1); foreach (SpriteVisual child in containerVisual.Children) { child.Scale = newScale; var newOffsetX = child.Offset.X * ((float)e.NewDpi.DpiScaleX / (float)e.OldDpi.DpiScaleX); var newOffsetY = child.Offset.Y * ((float)e.NewDpi.DpiScaleY / (float)e.OldDpi.DpiScaleY); child.Offset = new Vector3(newOffsetX, newOffsetY, 1); // Adjust animations for DPI change. AnimateSquare(child, 0); } }
Kullanıcı denetimini XAML sayfanıza ekleme
Artık kullanıcı denetimini XAML kullanıcı arabiriminize ekleyebilirsiniz.
MainWindow.xaml dosyasında Pencere Yüksekliği'ni 600, Width değerini 840 olarak ayarlayın.
Kullanıcı arabirimi için XAML'yi ekleyin. MainWindow.xaml'de bu XAML'yi kök
<Grid> </Grid>etiketleri arasına ekleyin.<Grid.ColumnDefinitions> <ColumnDefinition Width="210"/> <ColumnDefinition Width="600"/> <ColumnDefinition/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="46"/> <RowDefinition/> </Grid.RowDefinitions> <Button Content="Add composition element" Click="Button_Click" Grid.Row="1" Margin="12,0" VerticalAlignment="Top" Height="40"/> <TextBlock Text="Composition content" FontSize="20" Grid.Column="1" Margin="0,12,0,4" HorizontalAlignment="Center"/> <local:CompositionHostControl x:Name="CompositionHostControl1" Grid.Row="1" Grid.Column="1" VerticalAlignment="Top" Width="600" Height="500" BorderBrush="LightGray" BorderThickness="3"/>Yeni öğeler oluşturmak için düğmeye tıklamayı işle. (Click olayı XAML'de zaten tanımlı.)
MainWindow.xaml.cs bu Button_Click olay işleyici yöntemini ekleyin. Bu kod, rastgele oluşturulan boyut ve uzaklık ile yeni bir öğe oluşturmak için CompositionHost.AddElement öğesini çağırır.
// Add // using System; private void Button_Click(object sender, RoutedEventArgs e) { Random random = new Random(); float size = random.Next(50, 150); float offsetX = random.Next(0, (int)(CompositionHostControl1.ActualWidth - size)); float offsetY = random.Next(0, (int)(CompositionHostControl1.ActualHeight/2 - size)); CompositionHostControl1.AddElement(size, offsetX, offsetY); }
Artık WPF uygulamanızı derleyebilir ve çalıştırabilirsiniz. Gerekirse, tüm kodun doğru yerlerde olduğundan emin olmak için öğreticinin sonundaki tüm kodu denetleyin.
Uygulamayı çalıştırıp düğmeye tıkladığınızda, kullanıcı arabirimine eklenmiş animasyonlu kareler görmeniz gerekir.
Sonraki Adımlar
Aynı altyapıyı oluşturan daha eksiksiz bir örnek için GitHub WPF Görsel katmanı tümleştirme örneğine bakın.
Ek kaynaklar
- Getting Started (WPF) (.NET)
- Yönetilmeyen kodla çalışma (.NET)
- Windows uygulamalarını (UWP) kullanmaya başlama
- Windows için masaüstü uygulamanızı geliştirme (UWP)
- Windows.UI.Composition ad alanı (UWP)
Kodu tamamlama
İşte bu eğitimin tam kodu.
MainWindow.xaml
<Window x:Class="HelloComposition.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:HelloComposition"
mc:Ignorable="d"
Title="MainWindow" Height="600" Width="840">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="210"/>
<ColumnDefinition Width="600"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="46"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Content="Add composition element" Click="Button_Click"
Grid.Row="1" Margin="12,0"
VerticalAlignment="Top" Height="40"/>
<TextBlock Text="Composition content" FontSize="20"
Grid.Column="1" Margin="0,12,0,4"
HorizontalAlignment="Center"/>
<local:CompositionHostControl x:Name="CompositionHostControl1"
Grid.Row="1" Grid.Column="1"
VerticalAlignment="Top"
Width="600" Height="500"
BorderBrush="LightGray" BorderThickness="3"/>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Windows;
namespace HelloComposition
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Random random = new Random();
float size = random.Next(50, 150);
float offsetX = random.Next(0, (int)(CompositionHostControl1.ActualWidth - size));
float offsetY = random.Next(0, (int)(CompositionHostControl1.ActualHeight/2 - size));
CompositionHostControl1.AddElement(size, offsetX, offsetY);
}
}
}
CompositionHostControl.xaml
<UserControl x:Class="HelloComposition.CompositionHostControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:HelloComposition"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Border Name="CompositionHostElement"/>
</UserControl>
CompositionHostControl.xaml.cs
using System;
using System.Numerics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using Windows.UI.Composition;
namespace HelloComposition
{
/// <summary>
/// Interaction logic for CompositionHostControl.xaml
/// </summary>
public partial class CompositionHostControl : UserControl
{
CompositionHost compositionHost;
Compositor compositor;
Windows.UI.Composition.ContainerVisual containerVisual;
DpiScale currentDpi;
public CompositionHostControl()
{
InitializeComponent();
Loaded += CompositionHostControl_Loaded;
}
private void CompositionHostControl_Loaded(object sender, RoutedEventArgs e)
{
// If the user changes the DPI scale setting for the screen the app is on,
// the CompositionHostControl is reloaded. Don't redo this set up if it's
// already been done.
if (compositionHost is null)
{
currentDpi = VisualTreeHelper.GetDpi(this);
compositionHost = new CompositionHost(CompositionHostElement.ActualHeight, CompositionHostElement.ActualWidth);
CompositionHostElement.Child = compositionHost;
compositor = compositionHost.Compositor;
containerVisual = compositor.CreateContainerVisual();
compositionHost.Child = containerVisual;
}
}
protected override void OnDpiChanged(DpiScale oldDpi, DpiScale newDpi)
{
base.OnDpiChanged(oldDpi, newDpi);
currentDpi = newDpi;
Vector3 newScale = new Vector3((float)newDpi.DpiScaleX, (float)newDpi.DpiScaleY, 1);
foreach (SpriteVisual child in containerVisual.Children)
{
child.Scale = newScale;
var newOffsetX = child.Offset.X * ((float)newDpi.DpiScaleX / (float)oldDpi.DpiScaleX);
var newOffsetY = child.Offset.Y * ((float)newDpi.DpiScaleY / (float)oldDpi.DpiScaleY);
child.Offset = new Vector3(newOffsetX, newOffsetY, 1);
// Adjust animations for DPI change.
AnimateSquare(child, 0);
}
}
public void AddElement(float size, float offsetX, float offsetY)
{
var visual = compositor.CreateSpriteVisual();
visual.Size = new Vector2(size, size);
visual.Scale = new Vector3((float)currentDpi.DpiScaleX, (float)currentDpi.DpiScaleY, 1);
visual.Brush = compositor.CreateColorBrush(GetRandomColor());
visual.Offset = new Vector3(offsetX * (float)currentDpi.DpiScaleX, offsetY * (float)currentDpi.DpiScaleY, 0);
containerVisual.Children.InsertAtTop(visual);
AnimateSquare(visual, 3);
}
private void AnimateSquare(SpriteVisual visual, int delay)
{
float offsetX = (float)(visual.Offset.X); // Already adjusted for DPI.
// Adjust values for DPI scale, then find the Y offset that aligns the bottom of the square
// with the bottom of the host container. This is the value to animate to.
var hostHeightAdj = CompositionHostElement.ActualHeight * currentDpi.DpiScaleY;
var squareSizeAdj = visual.Size.Y * currentDpi.DpiScaleY;
float bottom = (float)(hostHeightAdj - squareSizeAdj);
// Create the animation only if it's needed.
if (visual.Offset.Y != bottom)
{
Vector3KeyFrameAnimation animation = compositor.CreateVector3KeyFrameAnimation();
animation.InsertKeyFrame(1f, new Vector3(offsetX, bottom, 0f));
animation.Duration = TimeSpan.FromSeconds(2);
animation.DelayTime = TimeSpan.FromSeconds(delay);
visual.StartAnimation("Offset", animation);
}
}
private Windows.UI.Color GetRandomColor()
{
Random random = new Random();
byte r = (byte)random.Next(0, 255);
byte g = (byte)random.Next(0, 255);
byte b = (byte)random.Next(0, 255);
return Windows.UI.Color.FromArgb(255, r, g, b);
}
}
}
CompositionHost.cs
using System;
using System.Runtime.InteropServices;
using System.Windows.Interop;
using Windows.UI.Composition;
namespace HelloComposition
{
class CompositionHost : HwndHost
{
IntPtr hwndHost;
int hostHeight, hostWidth;
object dispatcherQueue;
ICompositionTarget compositionTarget;
public Compositor Compositor { get; private set; }
public Visual Child
{
set
{
if (Compositor == null)
{
InitComposition(hwndHost);
}
compositionTarget.Root = value;
}
}
internal const int
WS_CHILD = 0x40000000,
WS_VISIBLE = 0x10000000,
LBS_NOTIFY = 0x00000001,
HOST_ID = 0x00000002,
LISTBOX_ID = 0x00000001,
WS_VSCROLL = 0x00200000,
WS_BORDER = 0x00800000;
public CompositionHost(double height, double width)
{
hostHeight = (int)height;
hostWidth = (int)width;
}
protected override HandleRef BuildWindowCore(HandleRef hwndParent)
{
// Create Window
hwndHost = IntPtr.Zero;
hwndHost = CreateWindowEx(0, "static", "",
WS_CHILD | WS_VISIBLE,
0, 0,
hostWidth, hostHeight,
hwndParent.Handle,
(IntPtr)HOST_ID,
IntPtr.Zero,
0);
// Create Dispatcher Queue
dispatcherQueue = InitializeCoreDispatcher();
// Build Composition Tree of content
InitComposition(hwndHost);
return new HandleRef(this, hwndHost);
}
protected override void DestroyWindowCore(HandleRef hwnd)
{
if (compositionTarget.Root != null)
{
compositionTarget.Root.Dispose();
}
DestroyWindow(hwnd.Handle);
}
private object InitializeCoreDispatcher()
{
DispatcherQueueOptions options = new DispatcherQueueOptions();
options.apartmentType = DISPATCHERQUEUE_THREAD_APARTMENTTYPE.DQTAT_COM_STA;
options.threadType = DISPATCHERQUEUE_THREAD_TYPE.DQTYPE_THREAD_CURRENT;
options.dwSize = Marshal.SizeOf(typeof(DispatcherQueueOptions));
object queue = null;
CreateDispatcherQueueController(options, out queue);
return queue;
}
private void InitComposition(IntPtr hwndHost)
{
ICompositorDesktopInterop interop;
Compositor = new Compositor();
object iunknown = Compositor as object;
interop = (ICompositorDesktopInterop)iunknown;
IntPtr raw;
interop.CreateDesktopWindowTarget(hwndHost, true, out raw);
object rawObject = Marshal.GetObjectForIUnknown(raw);
compositionTarget = (ICompositionTarget)rawObject;
if (raw == null) { throw new Exception("QI Failed"); }
}
#region PInvoke declarations
//typedef enum DISPATCHERQUEUE_THREAD_APARTMENTTYPE
//{
// DQTAT_COM_NONE,
// DQTAT_COM_ASTA,
// DQTAT_COM_STA
//};
internal enum DISPATCHERQUEUE_THREAD_APARTMENTTYPE
{
DQTAT_COM_NONE = 0,
DQTAT_COM_ASTA = 1,
DQTAT_COM_STA = 2
};
//typedef enum DISPATCHERQUEUE_THREAD_TYPE
//{
// DQTYPE_THREAD_DEDICATED,
// DQTYPE_THREAD_CURRENT
//};
internal enum DISPATCHERQUEUE_THREAD_TYPE
{
DQTYPE_THREAD_DEDICATED = 1,
DQTYPE_THREAD_CURRENT = 2,
};
//struct DispatcherQueueOptions
//{
// DWORD dwSize;
// DISPATCHERQUEUE_THREAD_TYPE threadType;
// DISPATCHERQUEUE_THREAD_APARTMENTTYPE apartmentType;
//};
[StructLayout(LayoutKind.Sequential)]
internal struct DispatcherQueueOptions
{
public int dwSize;
[MarshalAs(UnmanagedType.I4)]
public DISPATCHERQUEUE_THREAD_TYPE threadType;
[MarshalAs(UnmanagedType.I4)]
public DISPATCHERQUEUE_THREAD_APARTMENTTYPE apartmentType;
};
//HRESULT CreateDispatcherQueueController(
// DispatcherQueueOptions options,
// ABI::Windows::System::IDispatcherQueueController** dispatcherQueueController
//);
[DllImport("coremessaging.dll", EntryPoint = "CreateDispatcherQueueController", CharSet = CharSet.Unicode)]
internal static extern IntPtr CreateDispatcherQueueController(DispatcherQueueOptions options,
[MarshalAs(UnmanagedType.IUnknown)]
out object dispatcherQueueController);
[DllImport("user32.dll", EntryPoint = "CreateWindowEx", CharSet = CharSet.Unicode)]
internal static extern IntPtr CreateWindowEx(int dwExStyle,
string lpszClassName,
string lpszWindowName,
int style,
int x, int y,
int width, int height,
IntPtr hwndParent,
IntPtr hMenu,
IntPtr hInst,
[MarshalAs(UnmanagedType.AsAny)] object pvParam);
[DllImport("user32.dll", EntryPoint = "DestroyWindow", CharSet = CharSet.Unicode)]
internal static extern bool DestroyWindow(IntPtr hwnd);
#endregion PInvoke declarations
}
#region COM Interop
/*
#undef INTERFACE
#define INTERFACE ICompositorDesktopInterop
DECLARE_INTERFACE_IID_(ICompositorDesktopInterop, IUnknown, "29E691FA-4567-4DCA-B319-D0F207EB6807")
{
IFACEMETHOD(CreateDesktopWindowTarget)(
_In_ HWND hwndTarget,
_In_ BOOL isTopmost,
_COM_Outptr_ IDesktopWindowTarget * *result
) PURE;
};
*/
[ComImport]
[Guid("29E691FA-4567-4DCA-B319-D0F207EB6807")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ICompositorDesktopInterop
{
void CreateDesktopWindowTarget(IntPtr hwndTarget, bool isTopmost, out IntPtr test);
}
//[contract(Windows.Foundation.UniversalApiContract, 2.0)]
//[exclusiveto(Windows.UI.Composition.CompositionTarget)]
//[uuid(A1BEA8BA - D726 - 4663 - 8129 - 6B5E7927FFA6)]
//interface ICompositionTarget : IInspectable
//{
// [propget] HRESULT Root([out] [retval] Windows.UI.Composition.Visual** value);
// [propput] HRESULT Root([in] Windows.UI.Composition.Visual* value);
//}
[ComImport]
[Guid("A1BEA8BA-D726-4663-8129-6B5E7927FFA6")]
[InterfaceType(ComInterfaceType.InterfaceIsIInspectable)]
public interface ICompositionTarget
{
Windows.UI.Composition.Visual Root
{
get;
set;
}
}
#endregion COM Interop
}