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.
Bu konu, Evrensel Windows Platformu (UWP) uygulama örneklerinden birini C# dilinden C++/WinRT'ye taşımaya yönelik bir örnek olay incelemesi sunar. Adım adım kılavuzu takip ederek ve ilerledikçe örneği kendiniz uyarlayarak uyarlama pratiği ve deneyimi kazanabilirsiniz.
Note
Taşıma yapılan kaynak kodu bir UWP C# uygulamasıdır. Bu makaledeki hedef C++/WinRT kodu WinUI 3 (Windows Uygulama SDK'sı) için yazılmıştır. UWP kaynağının WinUI 3'te farklılık gösteren API'leri kullandığı her yerde (örneğin, Windows.UI.Core.CoreDispatcher vs. Microsoft.UI.Dispatching.DispatcherQueue), bu makalede C++/WinRT çıkışında doğru WinUI 3 eşdeğeri açıkça gösterilir. C++/WinRT sütunundaki kod desenlerini doğrudan WinUI 3 uygulamanızda kullanabilirsiniz.
C#'den C++/WinRT'ye taşımayla ilgili teknik ayrıntıların kapsamlı bir kataloğu için C#' den C++/WinRT'ye taşıma konusuna bakın.
C# ve C++ kaynak kodu dosyaları hakkında kısa bir önsöz
C# projesinde kaynak kod dosyalarınız öncelikli olarak .cs dosyalardır. C++'a geçtiğiniz zaman alışabileceğiniz daha fazla tür kaynak kodu dosyası olduğunu fark edeceksiniz. Bunun nedeni derleyiciler arasındaki fark, C++ kaynak kodunun yeniden kullanım şekli ve bir türü ve işlevlerini (yöntemleri) bildirme ve tanımlama kavramlarıyla ilgilidir.
İşlev bildirimi yalnızca işlevin imzasını (dönüş türü, adı ve parametre türleri ve adları) açıklar. İşlev tanımı , işlevin gövdesini (uygulaması) içerir.
Türler söz konusu olduğunda biraz farklıdır. Bir türü, adını vererek ve (en azından) tüm üye fonksiyonlarını (ve diğer üyelerini) yalnızca bildirerektanımlarsınız. Doğru, üye işlevlerini tanımlamasanız bile bir tür tanımlayabilirsiniz.
- Yaygın C++ kaynak kodu dosyaları
.h(dot h) ve.cppdosyalarıdır. Dosya.hbir üst bilgi dosyasıdır ve bir veya daha fazla tür tanımlar. Bir başlık dosyasında üye fonksiyonları tanımlayabilirsiniz, ancak genellikle.cppdosyası bunun içindir. Bu nedenle, varsayımsal bir C++ türü için MyClass yazın, içinde MyClass'ıMyClass.htanımlarsınız ve içindeki üye işlevleriniMyClass.cpptanımlarsınız. Diğer geliştiricilerin sınıflarınızı yeniden kullanması için yalnızca.hdosyaları ve nesne kodunu paylaşırsınız..cppdosyalarınızı gizli tutardınız, çünkü uygulamanın kendisi fikri mülkiyetinizdir. - Önceden derlenmiş üst bilgi (
pch.h). Genellikle uygulamanıza eklediğiniz bir dizi üst bilgi dosyası vardır ve bu dosyaları çok sık değiştirmezsiniz. Bu nedenle, her derlediğinizde bu üst bilgi kümesinin içeriğini işlemek yerine, bu üst bilgileri tek bir dosyada toplayabilir, bir kez derleyebilir ve her derlemede bu ön derleme adımının çıktısını kullanabilirsiniz. Bunu, önceden derlenmiş başlık dosyası aracılığıyla yaparsınız (genelliklepch.holarak adlandırılır). -
.idlDosyaları. Bu dosyalar Arabirim Tanım Dili (IDL) içerir. IDL'yi Windows Çalışma Zamanı türleri için üst bilgi dosyaları olarak düşünebilirsiniz. MainPage türü için IDL bölümünde IDL hakkında daha fazla bilgi vereceğiz.
Pano örneğini indirin ve test edin
Clipboard örneği web sayfasını ziyaret edin ve ZIP İndir’e tıklayın. İndirilen dosyanın sıkıştırmasını açın ve klasör yapısına bakın.
- Örnek kaynak kodunun C# sürümü adlı
csklasörde yer alır. - Örnek kaynak kodunun C++/WinRT sürümü adlı
cppwinrtklasörde yer alır. - Hem C# sürümü hem de C++/WinRT sürümü tarafından kullanılan diğer dosyalar,
sharedveSharedContentklasörlerinde bulunabilir.
Bu konudaki izlenecek yol, Pano örneğinin C# kaynak kodundan taşıma yoluyla C++/WinRT sürümünü nasıl yeniden oluşturabileceğinizi gösterir. Bu şekilde, kendi C# projelerinizi C++/WinRT'ye nasıl aktarabileceğinizi görebilirsiniz.
Örneğin ne yaptığını görmek için C# çözümünü ()\Clipboard_sample\cs\Clipboard.sln açın, yapılandırmayı uygun şekilde değiştirin ( x64 olabilir), derleyin ve çalıştırın. Örneğin kendi kullanıcı arabirimi (UI), çeşitli özelliklerinde size adım adım yol gösterir.
Tavsiye
İndirdiğiniz örneğin kök klasörü yerine Clipboardolarak adlandırılabilirClipboard_sample. Ancak daha sonraki bir adımda oluşturacağımız C++/WinRT sürümünden ayırt etmek için bu klasöre Clipboard_sample başvurmaya devam edeceğiz.
Pano adlı boş bir uygulama oluşturma
Note
C++/WinRT Visual Studio Uzantısını (VSIX) ve NuGet paketini (birlikte proje şablonu ve derleme desteği sağlayan) yükleme ve kullanma hakkında bilgi için bkz. C++/WinRT için Visual Studio desteği.
Microsoft Visual Studio'de yeni bir C++/WinRT projesi oluşturarak taşıma işlemine başlayın. C++ proje şablonu için Paketlenmiş Boş Uygulama (Masaüstünde WinUI 3) kullanarak yeni bir proje oluşturun. Adını Pano olarak ayarlayın ve (klasör yapınızın izlenecek yolla eşleşmesi için) Çözümü ve projeyi aynı dizine yerleştir seçeneğinin işaretinin kaldırıldığından emin olun.
Yalnızca bir temel almak için bu yeni, boş projenin derlenip çalıştığından emin olun.
Package.appxmanifest ve öğe dosyaları
Örneğin C# ve C++/WinRT sürümlerinin aynı makineye yan yana yüklenmesi gerekmiyorsa, iki projenin uygulama paketi bildirim kaynak dosyaları (Package.appxmanifest) aynı olabilir. Bu durumda, yalnızca C# projesinden C++/WinRT projesine kopyalayabilirsiniz Package.appxmanifest ve işiniz biter.
Örneğin iki sürümünün bir arada bulunabilmesi için farklı tanımlayıcılara ihtiyaç vardır. Bu durumda, C++/WinRT projesinde dosyayı bir XML düzenleyicisinde açın Package.appxmanifest ve bu üç değeri not edin.
- /Package/Identity öğesinin içinde Name özniteliğinin değerini not edin. Bu , paket adıdır. Yeni oluşturulan bir proje için, proje ona benzersiz bir GUID'nin başlangıç değerini verir.
- /Package/Applications/Application öğesinin içinde Id özniteliğinin değerini not edin. Bu , uygulama kimliğidir.
- /Package/mp:PhoneIdentity öğesinin içinde PhoneProductId özniteliğinin değerini not edin. Yine, yeni oluşturulmuş bir proje için bu değer, paket adı için ayarlanan GUID ile aynı olarak ayarlanır.
Ardından C# projesinden C++/WinRT projesine kopyalayın Package.appxmanifest . Son olarak, not ettiğiniz üç değeri geri yükleyebilirsiniz. Ya da kopyalanan değerleri, uygulama ve kuruluşunuz için (normalde yeni bir proje için yaptığınız gibi) benzersiz ve/veya uygun olacak şekilde düzenleyebilirsiniz. Örneğin, bu durumda paket adının değerini geri yüklemek yerine, kopyalanan değeri Microsoft.SDKSamples.Clipboard.CS yerine Microsoft.SDKSamples.Clipboard.CppWinRT olarak değiştirebiliriz. Ayrıca uygulama kimliğini App olarak ayarlayabiliriz. Paket adı veya uygulama kimliği farklı olduğu sürece, iki uygulama farklı Uygulama Kullanıcı Modeli kimliklerine (AUMID) sahip olur. İki uygulamanın aynı makineye yan yana yüklenmesi için de bu gereklidir.
Bu anlatımda, Package.appxmanifest içinde birkaç ek değişiklik yapmak mantıklıdır.
Clipboard C# Sample dizesi üç kez geçmektedir. Bunu Clipboard C++/WinRT Örneği olarak değiştirin.
C++/WinRT projesinde, Package.appxmanifest dosyası ile proje artık başvurdukları varlık dosyaları açısından eşzamanlı değil. Bunu gidermek için önce, Visual Studio'daki Çözüm Gezgini'nde Assets klasöründeki tüm dosyaları seçip bunları kaldırarak varlıkları C++/WinRT projesinden kaldırın (iletişim kutusunda Delete seçeneğini belirleyin).
C# projesi paylaşılan bir klasörden varlık dosyalarına başvurur. C++/WinRT projesinde de aynısını yapabilir veya bu kılavuzda yaptığımız gibi dosyaları kopyalayabilirsiniz.
\Clipboard_sample\SharedContent\media klasörüne gidin. C# projesinin içerdiği yedi dosyayı seçin (, , , , , , microsoft-sdk.pngve smalltile-sdk.png), bunları kopyalayın ve yeni projedeki klasöre yapıştırınsplash-sdk.png. squaretile-sdk.pngstorelogo-sdk.pngtile-sdk.pngwindows-sdk.png\Clipboard\Clipboard\Assets
Assets klasörüne sağ tıklayın (C++/WinRT projesindeki Çözüm Gezgini'da) >Ekle>Varolan Öğe...'yi seçin ve \Clipboard\Clipboard\Assets konumuna gidin. Dosya seçicide yedi dosyayı seçin ve Ekle'ye tıklayın.
Package.appxmanifest artık yeniden projenin varlık dosyalarıyla senkronize edildi.
Örneği yapılandıran işlevsellik de dahil olmak üzere MainPage
Tüm Evrensel Windows Platformu (UWP) uygulama örnekleri gibi Pano örneği de kullanıcının birer birer adım ilerleyebileceği bir senaryo koleksiyonundan oluşur. Belirli bir örnekteki senaryoların koleksiyonu, örneğin kaynak kodunda yapılandırılır. Koleksiyondaki her senaryo, bir başlığın yanı sıra senaryoyu uygulayan projedeki sınıfın türünü depolayan bir veri öğesidir.
Örneğin C# sürümünde, kaynak kod dosyasına bakarsanız SampleConfiguration.cs iki sınıf görürsünüz. Yapılandırma mantığının çoğu kısmi bir sınıf olan MainPage sınıfındadır (içindeki işaretleme MainPage.xaml ve içindeki kesinlik temelli kod MainPage.xaml.csile birleştirildiğinde tam bir sınıf oluşturur). Bu kaynak kod dosyasındaki diğer sınıf, Title ve ClassType özelliklerine sahip Senaryo'dur.
Sonraki birkaç alt bölümde MainPage ve Scenario'nun nasıl taşınabilir olduğunu inceleyeceğiz.
MainPage türü için IDL
Bu bölüme, Arabirim Tanım Dili (IDL) ve C++/WinRT ile programlama yaparken bize nasıl yardımcı olduğu hakkında kısaca konuşarak başlayalım. IDL, bir Windows Çalışma Zamanı türünün çağrılabilen yüzeyini açıklayan bir tür kaynak kodudur. Bir türün çağrılabilir (veya genel kullanıma açık) yüzeyi, türün kullanılabilmesi için dış dünyaya yansıtılır. Türün yansıtılan kısmı, türün gerçek iç uygulamasıyla tezat oluşturur; bu iç uygulama ise elbette çağrılabilir değildir ve public değildir. Yalnızca IDL'de tanımladığımız öngörülen bölümdür.
IDL kaynak kodunu (bir .idl dosya içinde) yazdığınızda, IDL'yi makine tarafından okunabilir meta veri dosyalarına (Windows Meta Veriler olarak da bilinir) derleyebilirsiniz. Bu meta veri dosyaları uzantısına .winmdsahiptir ve kullanımlarından bazıları aşağıdadır.
- bir
.winmdbileşendeki Windows Çalışma Zamanı türlerini açıklayabilir. Uygulama projesinden bir Windows Çalışma Zamanı Bileşenine (WRC) başvurduğunuzda, uygulama projesi WRC'ye ait Windows Meta Verilerini okur (bu meta veriler ayrı bir dosyada olabilir veya WRC'nin kendisiyle aynı dosyaya paketlenebilir) böylece WRC'nin türlerini uygulama içinden kullanabilirsiniz. - ,
.winmduygulamanızın bir bölümündeki Windows Çalışma Zamanı türlerini açıklayarak aynı uygulamanın farklı bir bölümü tarafından tüketilebilmelerini sağlayabilir. Örneğin, aynı uygulamadaki bir XAML sayfası tarafından kullanılan bir Windows Çalışma Zamanı türü. - Windows Çalışma Zamanı türlerini (yerleşik veya üçüncü taraf) kullanmanızı kolaylaştırmak için C++/WinRT derleme sistemi, bu Windows Çalışma Zamanı türlerinin yansıtılan bölümlerini temsil eden sarmalayıcı türler oluşturmak üzere
.winmddosyalarını kullanır. - Kendi Windows Çalışma Zamanı türlerinizi uygulamanızı kolaylaştırmak için C++/WinRT derleme sistemi IDL'nizi bir
.winmddosyaya dönüştürür ve bunu kullanarak projeksiyonunuz için sarmalayıcılar ve uygulamanızı temel alan saptamalar oluşturur (bu saplamalar hakkında bu konunun ilerleyen bölümlerinde daha fazla konuşacağız).
C++/WinRT ile kullandığımız belirli IDL sürümü, Microsoft Interface Definition Language 3.0'dır. Konunun bu bölümünün geri kalanında C# MainPage türünü ayrıntılı olarak inceleyeceğiz. Bunun hangi bölümlerinin C++/WinRT MainPage türünün projeksiyonunda (yani çağrılabilir veya genel kullanıma açık yüzeyinde) yer alması gerektiğine ve hangilerinin yalnızca uygulamasının bir parçası olabileceğine karar vereceğiz. Bu ayrım önemlidir, çünkü IDL'mizi yazmaya başladığımızda (bunu bundan sonraki bölümde yapacağız), burada yalnızca çağrılabilen bölümleri tanımlayacağız.
MainPage türünü birlikte uygulayan C# kaynak kodu dosyaları şunlardır: MainPage.xaml (yakında bunu kopyalayarak bağlantı noktası sağlayacağız), MainPage.xaml.csve SampleConfiguration.cs.
C++/WinRT sürümünde MainPage türümüzü benzer şekilde kaynak kod dosyalarına hesaba katacağız.
MainPage.xaml.cs içindeki mantığı büyük ölçüde MainPage.h ve MainPage.cpp'ye çevireceğiz. Ve SampleConfiguration.cs içindeki mantığı SampleConfiguration.h ve SampleConfiguration.cpp olarak çevireceğiz.
C# Evrensel Windows Platformu (UWP) uygulamasındaki sınıflar elbette Windows Çalışma Zamanı türlerdir. Ancak bir C++/WinRT uygulamasında bir tür yazdığınızda, bu türün Windows Çalışma Zamanı bir tür mü yoksa normal bir C++ sınıfı/yapısı/numaralandırması mı olduğunu seçebilirsiniz.
Projemizdeki herhangi bir XAML sayfasının Windows Çalışma Zamanı bir tür olması gerektiğinden MainPage'in Windows Çalışma Zamanı bir tür olması gerekir. C++/WinRT projesinde MainPage zaten Windows Çalışma Zamanı bir tür olduğundan bu yönünü değiştirmemiz gerekmez. Özellikle, bu bir çalışma zamanı sınıfı.
- Belirli bir tür için çalışma zamanı sınıfı yazmanız gerekip gerekmediği hakkında daha fazla bilgi için C++/WinRT ile API yazma konusuna bakın.
- C++/WinRT'de, bir çalışma zamanı sınıfının iç uygulaması ve bunun öngörülen (genel) bölümleri iki farklı sınıf biçiminde bulunur. Bunlar uygulama türü ve öngörülen tür olarak bilinir. Bunlar hakkında daha fazla bilgiyi önceki madde işaretinde belirtilen konu başlığında ve ayrıca API'leri C++/WinRT ile kullanma bölümünden öğrenebilirsiniz.
- Çalışma zamanı sınıfları ile IDL (
.idldosyalar) arasındaki bağlantı hakkında daha fazla bilgi için XAML denetimleri konusunu okuyup izleyebilirsiniz; C++/WinRT özelliğine bağlanın. Bu konu başlığında, ilk adımı projeye yeni bir Midl Dosyası (.idl) öğesi eklemek olan yeni bir çalışma zamanı sınıfı yazma işleminde yol gösterilir.
MainPage için C++/WinRT projesinde gerekli MainPage.idl dosya zaten var. Çünkü bunu bizim için proje şablonu oluşturdu. Ancak bu kılavuzda daha sonra projeye başka .idl dosyalar ekleyeceğiz.
Kısa süre içinde var olan MainPage.idl dosyaya tam olarak hangi IDL'yi eklememiz gerektiğinin listesini göreceğiz. Bundan önce, IDL'de nelerin yer alması gerekip nelerin gerekmediği konusunda biraz değerlendirme yapmamız gerekiyor.
MainPage'in hangi üyelerini bildirmemiz MainPage.idl gerektiğini (böylece MainPage çalışma zamanı sınıfının bir parçası olmaları için) ve yalnızca MainPage uygulama türünün üyesi olabileceklerini belirlemek için C# MainPage sınıfının üyelerinin listesini oluşturalım. Bu üyeleri, MainPage.xaml.cs ve SampleConfiguration.cs içine bakarak buluyoruz.
Toplam on iki protectedprivate ve alanları ve yöntemleri buluyoruz. Ve aşağıdaki public üyeleri buluruz.
- Varsayılan oluşturucu.
MainPage() - Current ve FEATURE_NAME statik alanları.
- IsClipboardContentChangedEnabled ve Scenarios özellikleri.
- BuildClipboardFormatsOutputString, DisplayToast, EnableClipboardContentChangedNotifications ve NotifyUser yöntemleri.
MainPage.idl içinde bildirimde bulunmaya aday olanlar, public üyeleridir. Şimdi her birini inceleyelim ve MainPage çalışma zamanı sınıfının parçası olması gerekip gerekmediğini veya yalnızca uygulamanın bir parçası olması gerekip gerekmediğini görelim.
- Varsayılan oluşturucu.
MainPage()XAML Sayfası için IDL'sinde varsayılan bir oluşturucu bildirmek normaldir. Bu şekilde, XAML UI çerçevesi türü etkinleştirebilir. - Uygulamanın MainPage örneğine erişmek için bağımsız senaryo XAML sayfalarının içinden Current statik alanı kullanılır. Current, XAML çerçevesiyle birlikte işlerlik sağlamak için kullanılmadığından (ve derleme birimleri arasında da kullanılmadığından), onu yalnızca uygulama türünün bir üyesi olarak ayırabiliriz. Bu gibi durumlarda kendi projelerinizde bunu yapmayı seçebilirsiniz. Ancak alan, yansıtılan türün bir örneği olduğundan, onu IDL'de tanımlamak mantıklı görünür. Bu nedenle burada bunu yapacağız (ve bunu yapmak kodu biraz daha temiz hale getirir).
- MainPage türü içinde erişilen statik FEATURE_NAME alanı için benzer bir durumdur. Yine IDL'de bildirmeyi seçmek kodumuzu biraz daha temiz hale getirir.
- IsClipboardContentChangedEnabled özelliği yalnızca OtherScenarios sınıfında kullanılır. Bu yüzden taşıma sırasında işleri biraz basitleştireceğiz ve bunu OtherScenarios çalışma zamanı sınıfının özel bir alanı olarak tanımlayacağız. O, IDL'ye girmeyecek.
- Scenarios özelliği, Scenario türünde bir nesne koleksiyonudur (daha önce bahsettiğimiz bir tür). Sonraki alt bölümde Senaryo'dan bahsedeceğiz, bu nedenle o zamana kadar Scenarios özelliğinden de ayrılalım.
- BuildClipboardFormatsOutputString, DisplayToast ve EnableClipboardContentChangedNotifications yöntemleri, ana sayfadan ziyade örneğin genel durumuyla daha çok ilgili görünen yardımcı işlevlerdir. Bu nedenle, bağlantı noktası sırasında bu üç yöntemi SampleState adlı yeni bir yardımcı program türünde yeniden düzenleyeceğiz (Windows Çalışma Zamanı türü olması gerekmez). Bu nedenle, bu üç yöntem IDL'de yer almayacak.
- NotifyUser yöntemi, statik Current alanından döndürülen MainPage örneğindeki tek tek senaryo XAML sayfalarının içinden çağrılır. Current (daha önce belirtildiği gibi) öngörülen türün bir örneği olduğundan, IDL'de NotifyUser'ı bildirmemiz gerekir. NotifyUser, NotifyType türünde bir parametre alır. Bunu bir sonraki bölümde konuşacağız.
Veri bağlamak istediğiniz herhangi bir üyenin de IDL'de bildirilmesi gerekir ({x:Bind} veya {Binding} kullanıyor olmanız fark etmeksizin). Daha fazla bilgi için bkz. Veri bağlama.
İlerleme kaydediyoruz: Dosyaya eklenecek ve eklenmemesi gereken üyelerin MainPage.idl listesini geliştiriyoruz. Ancak yine de Scenarios özelliğini ve NotifyType türünü tartışmamız gerekir. Şimdi bunu yapalım.
Senaryo ve NotifyType türleri için IDL
Senaryo sınıfı içinde SampleConfiguration.cstanımlanır. Bu sınıfı C++/WinRT'ye taşıma konusunda karar verme kararımız var. Varsayılan olarak, bunu muhtemelen sıradan bir C++ structyaparız. Ancak Senaryo ikili dosyalar arasında kullanılıyorsa veya XAML çerçevesiyle birlikte çalışırken IDL'de Windows Çalışma Zamanı türü olarak bildirilmesi gerekir.
C# kaynak kodunu incelediğimizde Senaryo'nun bu bağlamda kullanıldığını tespit ediyoruz.
<ListBox x:Name="ScenarioControl" ... >
var itemCollection = new List<Scenario>();
int i = 1;
foreach (Scenario s in scenarios)
{
itemCollection.Add(new Scenario { Title = $"{i++}) {s.Title}", ClassType = s.ClassType });
}
ScenarioControl.ItemsSource = itemCollection;
Bir ListBox'ınItemsSource özelliğine (bir öğe denetimidir) Bir Senaryo nesneleri koleksiyonu atanıyor. Scenariogerçekten XAML ile birlikte çalışması gerektiğinden, bir Windows Çalışma Zamanı türü olması gerekir. Bu nedenle IDL'de tanımlanması gerekir. IDL'de Senaryo türünü tanımlamak, C++/WinRT derleme sisteminin sahne arkası üst bilgi dosyasında (adı ve konumu bu kılavuz için önemli olmayan) senaryonun kaynak kod tanımını oluşturmasına neden olur.
MainPage.Scenarios'un az önce IDL'de olması gerektiğini söylediğimiz bir Senaryo nesneleri koleksiyonu olduğunu hatırlayacaksınız. Bu nedenle MainPage.Scenarios'un da IDL'de bildirilmesi gerekir.
NotifyType, C#'ın MainPage.xaml.cs içinde bildirilen bir enum'dir.
NotifyType'ıMainPage çalışma zamanı sınıfına ait bir yönteme geçirdiğimiz için NotifyType'ın da Windows Çalışma Zamanı bir tür olması ve içinde MainPage.idltanımlanması gerekir.
Şimdi dosyaya IDL'de bildirmeye MainPage.idl karar vermiş olduğumuz yeni türleri ve Mainpage'in yeni üyesini ekleyelim. Aynı zamanda, Visual Studio proje şablonunun bize verdiği Mainpage yer tutucu üyelerini IDL'den kaldıracağız.
Dolayısıyla, C++/WinRT projenizde MainPage.idl öğesini açın ve aşağıdaki kod listesiyle aynı olacak şekilde düzenleyin. Düzenlemelerden birinin ad alanı adını Clipboard'dan SDKTemplate'e değiştirmek olduğunu unutmayın. İsterseniz içeriğinin tamamını MainPage.idl aşağıdaki kodla değiştirebilirsiniz. Dikkate almak istediğiniz bir diğer ayar da Scenario::ClassType adını Scenario::ClassName olarak değiştirdiğimizdir.
// MainPage.idl
namespace SDKTemplate
{
struct Scenario
{
String Title;
Microsoft.UI.Xaml.Interop.TypeName ClassName;
};
enum NotifyType
{
StatusMessage,
ErrorMessage
};
[default_interface]
runtimeclass MainPage : Microsoft.UI.Xaml.Controls.Page
{
MainPage();
static MainPage Current{ get; };
static String FEATURE_NAME{ get; };
static Windows.Foundation.Collections.IVector<Scenario> scenarios{ get; };
void NotifyUser(String strMessage, NotifyType type);
};
}
Note
C++/WinRT projesindeki bir .idl dosyanın içeriği hakkında daha fazla bilgi için bkz. Arabirim Tanımı Dili 3.0 Microsoft.
Kendi taşıma çalışmanızda, yukarıda yaptığımız gibi ad alanı adını değiştirmek istemeyebilir veya bunu değiştirmenize gerek olmayabilir. Bunu burada yalnızca, taşıdığımız C# projesinin varsayılan ad alanı SDKTemplate olduğu için yapıyoruz; oysa projenin ve derlemenin adı Clipboard.
Ancak, bu kılavuzda uyarlama işlemine devam ederken, kaynak kodda geçen Clipboard ad alanı adının her örneğini SDKTemplate olarak değiştireceğiz. C++/WinRT proje özelliklerinde Pano ad alanı adının göründüğü bir yer de vardır, bu nedenle bunu şimdi değiştirme fırsatına sahip olacağız.
Visual Studio'de, C++/WinRT projesi için Ortak Özellikler>C++/WinRT>Kök Ad Alanı proje özelliğini SDKTemplate değerine ayarlayın.
IDL'yi kaydedin ve taslak dosyalarını yeniden oluşturun
XAML denetimleri; C++/WinRT özelliğine bağlama konusu saplama dosyalarına ilişkin bir fikri tanıtır ve bunların nasıl çalıştığını gösteren bir kılavuz gösterir. Bu konuda daha önce, C++/WinRT derleme sisteminin .idl dosyalarınızın içeriğini Windows Meta Verileri’ne dönüştürdüğünü ve ardından cppwinrt.exe adlı bir aracın bu meta verilerden, uygulamanızı temel alabileceğiniz saplamalar oluşturduğunu belirtirken saplamalardan da söz etmiştik.
IDL’nizde bir şey eklediğinizde, kaldırdığınızda veya değiştirdiğinizde ve derleme yaptığınızda, derleme sistemi bu stub dosyalarındaki stub uygulamalarını günceller. Bu nedenle IDL'nizi ve derlemenizi her değiştirdiğinizde, bu saplama dosyalarını görüntülemenizi, değiştirilen imzaları kopyalamanızı ve bunları projenize yapıştırmanızı öneririz. Bunu hemen nasıl yapacağımıza dair daha fazla bilgi ve örnek vereceğiz. Ancak bunu yapmanın avantajı, uygulama türünüzün şeklinin ne olması gerektiğini ve yöntemlerinin imzasının ne olması gerektiğini her zaman bilmeniz için hatasız bir yol sunmaktır.
Kılavuzun bu noktasında, MainPage.idl dosyasını şimdilik düzenlemeyi bitirdik, bu yüzden şimdi kaydetmelisiniz. Proje şu anda derlemeyi başarıyla tamamlamayacak, ancak şimdi bir derleme yapmak yine de yararlıdır çünkü bu işlem MainPage için saplama dosyalarını yeniden oluşturur. Bu nedenle projeyi şimdi derleyin ve derleme hatalarını göz ardı edin.
Bu C++/WinRT projesi için saplama dosyaları \Clipboard\Clipboard\Generated Files\sources klasöründe oluşturulur. Kısmi derleme sona erdikten sonra bunları orada bulacaksınız (beklendiği gibi derleme tam olarak başarılı olmayacaktır. Ancak ilgilendiğimiz adım (saplama oluşturma) başarılı olacaktır . İlgilendiğimiz dosyalar MainPage.h ve MainPage.cpp.
Bu iki saplama dosyasında, IDL'ye eklediğimiz MainPage üyelerinin yeni saplama uygulamalarını görürsünüz (örneğin, Geçerli ve FEATURE_NAME). Projede zaten bulunan MainPage.h ve MainPage.cpp dosyalarına bu taslak uygulamaları kopyalamanız gerekecek. Aynı zamanda, IDL'de yaptığımız gibi, Visual Studio proje şablonunun bize verdiği Mainpage yer tutucu üyelerini (MyProperty adlı sahte özellik ve ClickHandler adlı olay işleyicisi) mevcut dosyalardan kaldıracağız.
Aslında, MainPage'in korumak istediğimiz geçerli sürümünün tek üyesi oluşturucudur.
Saplama dosyalarından yeni üyeleri kopyaladıktan, istemediğimiz üyeleri sildikten ve ad alanını güncelleştirdikten sonra projenizdeki MainPage.h ve MainPage.cpp dosyaları aşağıdaki kod listelerine benzemelidir. İki MainPage türü olduğuna dikkat edin. Biri implementation ad alanında, ikincisi de factory_implementation ad alanında.
factory_implementation üzerinde yaptığımız tek değişiklik SDKTemplate'ı ad alanına eklemektir.
// MainPage.h
#pragma once
#include "MainPage.g.h"
namespace winrt::SDKTemplate::implementation
{
struct MainPage : MainPageT<MainPage>
{
MainPage();
static SDKTemplate::MainPage Current();
static hstring FEATURE_NAME();
static Windows::Foundation::Collections::IVector<SDKTemplate::Scenario> scenarios();
void NotifyUser(hstring const& strMessage, SDKTemplate::NotifyType const& type);
};
}
namespace winrt::SDKTemplate::factory_implementation
{
struct MainPage : MainPageT<MainPage, implementation::MainPage>
{
};
}
// MainPage.cpp
#include "pch.h"
#include "MainPage.h"
#include "MainPage.g.cpp"
namespace winrt::SDKTemplate::implementation
{
MainPage::MainPage()
{
InitializeComponent();
}
SDKTemplate::MainPage MainPage::Current()
{
throw hresult_not_implemented();
}
hstring MainPage::FEATURE_NAME()
{
throw hresult_not_implemented();
}
Windows::Foundation::Collections::IVector<SDKTemplate::Scenario> MainPage::scenarios()
{
throw hresult_not_implemented();
}
void MainPage::NotifyUser(hstring const& strMessage, SDKTemplate::NotifyType const& type)
{
throw hresult_not_implemented();
}
}
C# dizeleri için System.String kullanır. Bir örnek için MainPage.NotifyUser yöntemine bakın. IDL'mizde String ile bir dize bildiririz ve cppwinrt.exe araç bizim için C++/WinRT kodu oluşturduğunda winrt::hstring türünü kullanır. C# kodunda bir dizeyle her karşılaşışımızda, bunu winrt::hstring'e taşımamız gerekir. Daha fazla bilgi için bkz. C++/WinRT'de dize işleme.
Yöntem imzalarındaki parametrelerin const& açıklaması için bkz. Parametre geçirme.
Kalan tüm ad alanı bildirimlerini ve başvurularını güncelle ve derle
C++/WinRT projesini derlemeden önce, Clipboard ad alanına ait tüm bildirimleri (ve başvuruları) bulun ve bunları SDKTemplate ile değiştirin.
-
MainPage.xamlveApp.xaml. Ad alanı,x:Classvexmlns:localözniteliklerinin değerlerinde görünür. -
App.idl. -
App.h. -
App.cpp. İkiusing namespaceyönergesi (alt dizeusing namespace Clipboardiçin arayın) ve MainPage türüne ait iki niteleme vardır (Clipboard::MainPageifadesini arayın). Bunların değişmesi gerekiyor.
MainPage içinden olay işleyicisini kaldırdığımıza göre, MainPage.xaml içine de gidin ve Button öğesini işaretlemeden silin.
Tüm dosyaları kaydedin. Çözümü temizleyin (Derleme>Çözümü Temizle) ve ardından derleyin. Şimdiye kadar yapılan tüm değişiklikler tam olarak belirtildiği gibi uygulandıysa, derlemenin başarılı olması beklenir.
IDL'de bildirdiğimiz MainPage üyelerini uygulama
Oluşturucu, Current ve FEATURE_NAME
Taşımamız gereken ilgili kod (C# projesinden) aşağıdadır.
<!-- MainPage.xaml -->
...
<TextBlock x:Name="SampleTitle" ... />
...
// MainPage.xaml.cs
...
public sealed partial class MainPage : Page
{
public static MainPage Current;
public MainPage()
{
InitializeComponent();
Current = this;
SampleTitle.Text = FEATURE_NAME;
}
...
}
...
// SampleConfiguration.cs
...
public partial class MainPage : Page
{
public const string FEATURE_NAME = "Clipboard C# sample";
...
}
...
Yakında MainPage.xaml öğesinin tamamını yeniden kullanacağız (onu kopyalayarak). Şimdilik (aşağıda), C++/WinRT projesine uygun ada sahip bir MainPage.xaml öğesini geçici olarak ekleyeceğiz.
FEATURE_NAMEMainPage'in statik bir alanıdır (C# const alanı temelde davranışında statiktir), içinde SampleConfiguration.cstanımlanır. C++/WinRT için (statik) bir alan yerine bunu bir (statik) salt okunur özelliğin C++/WinRT ifadesi yapacağız. Bir özellik alıcısını ifade etmenin C++/WinRT yolu, özellik değerini döndüren ve parametre (erişimci) almayan bir işlevdir. Böylece C# FEATURE_NAME statik alanı, C++/WinRT’de FEATURE_NAME statik erişimci fonksiyonuna dönüşür (bu durumda dize sabitini döndüren).
Bu arada, bir C# salt okunur özelliğini aktarıyor olsaydık da aynı şeyi yapardık. C# yazılabilir özelliği için, bir özellik ayarlayıcısını ifade etmenin C++/WinRT yolu, void özellik değerini parametre (mutator) olarak alan bir işlevdir. Her iki durumda da, C# alanı veya özelliği statikse, C++/WinRT erişimcisi ve/veya mutator da öyledir.
Current , MainPage'in statik (sabit olmayan) bir alanıdır. Yeniden, bunu (C++/WinRT ifadesi) salt okunur bir özellik haline getirecek ve yeniden statik hale getireceğiz.
FEATURE_NAME sabitken, Current sabit değildir. Bu nedenle C++/WinRT'de bir destekleyici alana ihtiyacımız olacak ve erişimcimiz de bunu döndürecek. Dolayısıyla, C++/WinRT projesinde MainPage.h içinde current adlı özel bir statik alan bildireceğiz, MainPage.cpp içinde current'ı tanımlayıp başlatacağız (çünkü statik depolama süresine sahiptir) ve buna Current adlı genel statik erişimci işlevi aracılığıyla erişeceğiz.
Oluşturucunun kendisi, aktarılması kolay birkaç atama yapar.
C++/WinRT projesinde, adlı yeni bir >>SampleConfiguration.cpp öğesi ekleyin.
Aşağıdaki listelerle eşleşecek şekilde MainPage.xaml, MainPage.h, MainPage.cpp ve SampleConfiguration.cpp öğelerini düzenleyin.
<!-- MainPage.xaml -->
...
<StackPanel ...>
<TextBlock x:Name="SampleTitle" />
</StackPanel>
...
// MainPage.h
...
namespace winrt::SDKTemplate::implementation
{
struct MainPage : MainPageT<MainPage>
{
...
static SDKTemplate::MainPage Current() { return current; }
...
private:
static SDKTemplate::MainPage current;
...
};
...
}
// MainPage.cpp
...
namespace winrt::SDKTemplate::implementation
{
SDKTemplate::MainPage MainPage::current{ nullptr };
MainPage::MainPage()
{
InitializeComponent();
MainPage::current = *this;
SampleTitle().Text(FEATURE_NAME());
}
...
}
// SampleConfiguration.cpp
#include "pch.h"
#include "MainPage.h"
using namespace winrt;
using namespace SDKTemplate;
hstring implementation::MainPage::FEATURE_NAME()
{
return L"Clipboard C++/WinRT Sample";
}
Ayrıca, MainPage.cppFEATURE_NAME() için mevcut işlev gövdelerini sildiğinizden emin olun çünkü bu yöntemleri başka bir yerde tanımlıyoruz.
Gördüğünüz gibi MainPage::current , öngörülen tür olan SDKTemplate::MainPage türünde olarak bildirilir. Uygulama türü olan SDKTemplate::implementation::MainPage türünde değildir. Öngörülen tür, XAML birlikte çalışma projesi içinde veya ikili dosyalar arasında kullanılmak üzere tasarlanmış türdür. Uygulama türü, öngörülen türünüzde kullanıma sunduğunuz tesisleri uygulamak için kullandığınız türdür.
MainPage.h içindeki MainPage::current bildirimi uygulama ad alanı (winrt::SDKTemplate::implementation) içinde yer aldığından, nitelenmemiş bir MainPage uygulama türüne karşılık gelecekti. Bu nedenle, SDKTemplate:: ile niteliyoruz; böylece MainPage::current öğesinin, öngörülen tür winrt::SDKTemplate::MainPage örneği olduğunu açıkça belirtmiş oluyoruz.
Oluşturucuda, MainPage::current = *this; ile ilgili açıklanması gereken bazı noktalar vardır.
- İşaretçiyi
thisuygulama türünün bir üyesi içinde kullandığınızda,thisişaretçi elbette uygulama türüne yönelik bir işaretçidir. -
thisişaretçisini karşılık gelen projeksiyonlu türe dönüştürmek için başvurusunu kaldırın. Uygulama türünüzü IDL'den oluşturmanız koşuluyla (burada olduğu gibi), uygulama türünün yansıtılan türüne dönüştüren bir dönüştürme işleci vardır. Bu yüzden buradaki ödev işe yarıyor.
Bu ayrıntılar hakkında daha fazla bilgi için bkz. Uygulama türlerini ve arabirimlerini örneklendirme ve döndürme.
Yapıcıda da SampleTitle().Text(FEATURE_NAME()); bulunur. bölümüSampleTitle(), XAML'ye eklediğimiz TextBlock değerini döndüren SampleTitle adlı basit bir erişimci işlevine yapılan çağrıdır. Bir XAML öğesini x:Name yaptığınızda, XAML derleyicisi sizin için öğenin adını taşıyan bir erişimci oluşturur.
.Text(...) bölümü, SampleTitle erişimcisinin döndürdüğü TextBlock nesnesi üzerinde Text değiştirici işlevini çağırır. Ve FEATURE_NAME() dize sabitini döndürmek için statik MainPage::FEATURE_NAME erişimci fonksiyonunu çağırır. Bu kod satırı, TextBlock'unSampleTitle adlı Text özelliğini ayarlar.
Windows Çalışma Zamanı'ta dizeler geniş karakterli olduğundan, bir dize sabitini taşımak için başına L geniş karakter kodlama önekini getirdiğimizi unutmayın. Bu nedenle (örneğin) "bir dize sabiti" ifadesini L"bir dize sabiti" olarak değiştiririz. Ayrıca bkz. Geniş karakter dizisi sabitleri.
Senaryo
İşte port etmemiz gereken ilgili C# kodu.
// MainPage.xaml.cs
...
public sealed partial class MainPage : Page
{
...
public List<Scenario> Scenarios
{
get { return this.scenarios; }
}
...
}
...
// SampleConfiguration.cs
...
public partial class MainPage : Page
{
...
List<Scenario> scenarios = new List<Scenario>
{
new Scenario() { Title = "Copy and paste text", ClassType = typeof(CopyText) },
new Scenario() { Title = "Copy and paste an image", ClassType = typeof(CopyImage) },
new Scenario() { Title = "Copy and paste files", ClassType = typeof(CopyFiles) },
new Scenario() { Title = "Other Clipboard operations", ClassType = typeof(OtherScenarios) }
};
...
}
...
Önceki araştırmamızda bu Senaryo nesneleri koleksiyonunun ListBox'ta görüntülendiğini biliyoruz. C++/WinRT'de, bir öğe denetiminin ItemsSource özelliğine atayabileceğiniz koleksiyon türünün sınırları vardır. Koleksiyon bir vektör veya gözlemlenebilir bir vektör olmalı ve öğeleri aşağıdakilerden biri olmalıdır:
- Çalışma zamanı sınıfları veya,
- IInspectable.
IInspectable durumunda, öğelerin kendileri çalışma zamanı sınıfları değilse, bu öğelerin IInspectable'a kutulanıp IInspectable'dan kutudan çıkarılabilecek bir türden olması gerekir. Bu da onların Windows Çalışma Zamanı türleri olması gerektiği anlamına gelir (bkz. Değerleri IInspectable'a kutulama ve kutudan çıkarma).
Bu örnek olay incelemesi için Senaryo'nun çalışma zamanı sınıfı olmasını sağlamadık. Yine de mantıklı bir seçenek. Kendi taşıma çalışmanızda da bir çalışma zamanı sınıfının kesinlikle en uygun seçenek olacağı durumlar olacaktır. Örneğin, öğe türünü gözlemlenebilir hale getirmeniz gerekiyorsa (bkz . XAML denetimleri; C++/WinRT özelliğine bağlama) veya öğenin başka bir nedenle yöntemleri olması gerekiyorsa ve bu yalnızca bir veri üyeleri kümesinden fazlasıdır.
Bu kılavuzda Senaryo türü için bir çalışma zamanı sınıfı kullanmadığımız için, kutulama hakkında düşünmemiz gerekir.
Scenario'yu normal bir C++ struct haline getirseydik, onu kutulayamazdık. Ancak Scenario'yu IDL'de bir struct olarak tanımladık ve bu yüzden onu kutulayabiliriz.
Geriye, Scenario nesnelerini ya önceden kutulamak ya da bunları ItemsSource’a atamadan hemen öncesine kadar bekleyip tam gerektiği anda kutulamak seçeneği kalıyor. Bu iki seçenekle ilgili dikkat edilmesi gereken bazı noktalar aşağıdadır.
- Önceden boks. Bu seçenek için veri üyemiz kullanıcı arabirimine atanmaya hazır bir IInspectable koleksiyonudur. Başlatma işleminde Senaryo nesnelerini bu veri üyesine kutulayacağız. Bu koleksiyonun yalnızca bir kopyasına ihtiyacımız var, ancak alanlarını okumamız gerektiğinde her seferinde bir öğeyi kutudan çıkarmamız gerekiyor.
- Tam zamanında boks. Bu seçenek için veri üyemiz bir Senaryo koleksiyonudur. Kullanıcı arabirimine atama zamanı geldiğinde, veri üyesindeki Scenario nesnelerini yeni bir IInspectable koleksiyonuna kutularız. Veri üyesindeki öğelerin alanlarını kutulamadan okuyabiliriz, ancak koleksiyonun iki kopyasına ihtiyacımız vardır.
Gördüğünüz gibi, bunun gibi küçük bir koleksiyon söz konusu olduğunda, artılar ve eksiler aşağı yukarı birbirini dengeliyor. Bu örnek olay incelemesi için tam zamanında seçeneğini kullanacağız.
senaryolar üyesi, SampleConfiguration.cs içinde tanımlanıp başlatılan MainPage sınıfının bir alanıdır.
Senaryolar, MainPage'inMainPage.xaml.cs içinde tanımlanan (ve senaryolar alanını döndürmek için) uygulanan salt okunur bir özelliğidir. C++/WinRT projesinde benzer bir şey yapacağız; ancak iki üyeyi statik hale getireceğiz (uygulama genelinde tek bir örneğe ihtiyacımız olduğundan ve sınıf örneğine gerek kalmadan bunlara erişebildiğimizden). Bunlara sırasıyla senaryolarInner ve senaryolar adını vereceğiz.
MainPage.h içinde scenariosInner tanımlayacağız. Statik depolama süresi olduğundan bunu bir .cpp dosyada (SampleConfiguration.cppbu örnekte) tanımlayacağız/başlatacağız.
MainPage.h ve SampleConfiguration.cpp öğelerini aşağıdaki listelerle eşleşecek şekilde düzenleyin.
// MainPage.h
...
struct MainPage : MainPageT<MainPage>
{
...
static Windows::Foundation::Collections::IVector<Scenario> scenarios() { return scenariosInner; }
...
private:
static winrt::Windows::Foundation::Collections::IVector<Scenario> scenariosInner;
...
};
// SampleConfiguration.cpp
...
using namespace Windows::Foundation::Collections;
...
IVector<Scenario> implementation::MainPage::scenariosInner = winrt::single_threaded_observable_vector<Scenario>(
{
Scenario{ L"Copy and paste text", xaml_typename<SDKTemplate::CopyText>() },
Scenario{ L"Copy and paste an image", xaml_typename<SDKTemplate::CopyImage>() },
Scenario{ L"Copy and paste files", xaml_typename<SDKTemplate::CopyFiles>() },
Scenario{ L"History and roaming", xaml_typename<SDKTemplate::HistoryAndRoaming>() },
Scenario{ L"Other Clipboard operations", xaml_typename<SDKTemplate::OtherScenarios>() },
});
Ayrıca, MainPage.cpp için mevcut işlev gövdesini sildiğinizden emin olun çünkü bu yöntemi artık üst bilgi dosyasında tanımlıyoruz.
Gördüğünüz gibi, SampleConfiguration.cpp içinde, statik veri üyesi scenariosInner'ı winrt::single_threaded_observable_vector adlı bir C++/WinRT yardımcı işlevini çağırarak başlatırız. Bu işlev bizim için yeni bir Windows Çalışma Zamanı koleksiyonu nesnesi oluşturur ve bunu IObservableVector arabirimi olarak döndürür. Bu örnekte koleksiyon gözlemlenebilir olmadığından (başlatma işleminden sonra öğe eklemediğinden veya kaldırmadığından olması gerekmez), bunun yerine winrt::single_threaded_vector çağırmayı tercih edebilirdik. Bu işlev, koleksiyonu bir IVector arabirimi olarak döndürür.
Koleksiyonlar ve bunlara bağlama hakkında daha fazla bilgi için bkz. XAML öğeleri denetimleri; C++/WinRT koleksiyonuna bağlama ve C++/WinRT ile Koleksiyonlar.
Az önce eklediğiniz başlatma kodu, henüz projede olmayan türlere başvurur (örneğin, winrt::SDKTemplate::CopyText). Bunu düzeltmek için devam edelim ve projeye beş yeni boş XAML sayfası ekleyelim.
Beş yeni boş XAML sayfası ekleme
Projeye yeni bir Visual C++>Boş Sayfa (C++/WinRT) öğesi ekleyin (Bunun Boş Sayfa (C++/WinRT) öğe şablonu olduğundan ve Boş Sayfa şablonu olmadığından emin olun).
CopyText olarak adlandırın. Yeni XAML sayfası, istediğimiz gibi, SDKTemplate ad alanı içinde tanımlanır.
Yukarıdaki işlemi dört kez daha tekrarlayın ve XAML sayfalarını CopyImage, , CopyFilesHistoryAndRoamingve OtherScenariosolarak adlandırın.
İsterseniz artık yeniden oluşturabilirsiniz.
NotifyUser
C# projesinde MainPage.NotifyUser yönteminin uygulamasını içinde MainPage.xaml.csbulacaksınız.
MainPage.NotifyUser'ınMainPage.UpdateStatus'a bağımlılığı vardır ve bu yöntemin de henüz taşımadığımız XAML öğelerine bağımlılıkları vardır. Şimdilik C++/WinRT projesinde UpdateStatus adlı yer tutucu bir yöntem oluşturacağız, bunu daha sonra taşıyacağız.
İşte port etmemiz gereken ilgili C# kodu.
// MainPage.xaml.cs
...
public void NotifyUser(string strMessage, NotifyType type)
if (Dispatcher.HasThreadAccess)
{
UpdateStatus(strMessage, type);
}
else
{
var task = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => UpdateStatus(strMessage, type));
}
private void UpdateStatus(string strMessage, NotifyType type) { ... }{
...
NotifyUser kullanıcı arabirimi güncelleştirmelerini ana iş parçacığına gönderir. WinUI 3'te bu, eski CoreDispatcher yerine Microsoft.UI.Dispatching.DispatcherQueue kullanır. C++/WinRT'de, bir Windows veya Microsoft ad alanından tür kullanmak istediğinizde, ilgili C++/WinRT ad alanı üst bilgi dosyasını eklemeniz gerekir (bu konuda daha fazla bilgi için bkz. C++/WinRT ile çalışmaya başlama). Bu durumda, aşağıdaki kod listesinde göreceğiniz gibi, üst bilgi winrt/Microsoft.UI.Dispatching.h'dır ve bunu pch.h içine dahil edeceğiz.
UpdateStatus özeldir. Bu nedenle bunu MainPage uygulama türümüzde özel bir yöntem yapacağız. UpdateStatus çalışma zamanı sınıfında çağrılmak üzere tasarlanmamıştır, bu nedenle IDL'de bildirmeyeceğiz.
MainPage.NotifyUser aktarıldıktan ve MainPage.UpdateStatus için taslak bir uygulama oluşturulduktan sonra, C++/WinRT projesinde elde ettiğimiz sonuç budur. Bu kod listesinden sonra bazı ayrıntıları inceleyeceğiz.
// pch.h
...
#include <winrt/Microsoft.UI.Dispatching.h>
...
// MainPage.h
...
struct MainPage : MainPageT<MainPage>
{
...
void NotifyUser(hstring const& strMessage, SDKTemplate::NotifyType const& type);
...
private:
void UpdateStatus(hstring const& strMessage, SDKTemplate::NotifyType const& type);
...
};
// MainPage.cpp
...
void MainPage::NotifyUser(hstring const& strMessage, SDKTemplate::NotifyType const& type)
{
if (DispatcherQueue().HasThreadAccess())
{
UpdateStatus(strMessage, type);
}
else
{
DispatcherQueue().TryEnqueue([strMessage, type, this]()
{
UpdateStatus(strMessage, type);
});
}
}
void MainPage::UpdateStatus(hstring const& strMessage, SDKTemplate::NotifyType const& type)
{
throw hresult_not_implemented();
}
...
C# dilinde, iç içe geçmiş özelliklere erişmek için nokta gösterimini kullanabilirsiniz. Bu nedenle, C# MainPage türü söz dizimi ile kendi Dispatcher özelliğine erişebilir. C# söz dizimi ile bu Dispatcher.HasThreadAccess daha fazla nokta ekleyebilir. C++/WinRT'de özellikler erişimci işlevleri olarak uygulanır, bu nedenle söz dizimi yalnızca her işlev çağrısı için parantez eklemenizden farklıdır.
| C# | C++/WinRT |
|---|---|
Dispatcher.HasThreadAccess |
DispatcherQueue().HasThreadAccess() |
NotifyUser'ın C# sürümü Dispatcher.RunAsync'i çağırdığında WinUI 3 eşdeğeri DispatcherQueue.TryEnqueue kullanır. C++/WinRT sürümü, geri çağırma temsilcisini lambda işlevi olarak uygular. C++/WinRT'de, kullanacağımız iki parametreyi ve ayrıca this işaretçiyi de yakalarız (çünkü bir üye fonksiyonu çağıracağız).
C++/WinRT'de temsilcileri kullanarak olayları işleme konusunda temsilcileri lambda olarak uygulama ve kod örnekleri hakkında daha fazla bilgi bulabilirsiniz.
Kalan MainPage üyelerini uygulama
Şimdiye kadar hangilerini taşıdığımızı ve hangilerinin hâlâ yapılması gerektiğini görebilmek için, MainPage.xaml.cs ve SampleConfiguration.cs genelinde uygulanan MainPage üyelerinin tam bir listesini çıkaralım.
| Üye | Access | Status |
|---|---|---|
| MainPage oluşturucu | public |
Uyarlandı |
| Geçerli özellik | public |
Portlandı |
| FEATURE_NAME özelliği | public |
Uyarlanmış |
| IsClipboardContentChangedEnabled özelliği | public |
Başlatılmadi |
| Senaryolar özelliği | public |
Uyarlanmış |
| BuildClipboardFormatsOutputString yöntemi | public |
Başlatılmadi |
| DisplayToast yöntemi | public |
Başlatılmadi |
| EnableClipboardContentChangedNotifications yöntemi | public |
Başlatılmadi |
| NotifyUser yöntemi | public |
Uyarlanmış |
| OnNavigatedTo yöntemi | protected |
Başlatılmadi |
| isApplicationWindowActive alanı | private |
Başlatılmadi |
| needToPrintClipboardFormat alanı | private |
Başlatılmadi |
| senaryolar alanı | private |
Uyarlanmış |
| Button_Click yöntemi | private |
Başlatılmadi |
| DisplayChangedFormats yöntemi | private |
Başlatılmadi |
| Footer_Click yöntemi | private |
Başlatılmadi |
| HandleClipboardChanged yöntemi | private |
Başlatılmadi |
| OnClipboardChanged yöntemi | private |
Başlatılmadi |
| OnWindowActivated yöntemi | private |
Başlatılmadi |
| ScenarioControl_SelectionChanged yöntemi | private |
Başlatılmadi |
| UpdateStatus yöntemi | private |
Taslak olarak oluşturuldu |
Sonraki birkaç alt bölümde henüz raporlanmamış üyeler hakkında konuşacağız.
Note
Zaman zaman kaynak kodunda XAML işaretlemesindeki (içinde MainPage.xaml) kullanıcı arabirimi öğelerine başvurularla karşılaşacağız. Bu referanslara geldiğimizde, XAML'e basit yer tutucu öğeler ekleyerek bunları geçici olarak aşacağız. Bu şekilde, proje her alt bölümden sonra derlenmeye devam eder. Alternatif olarak, referansları çözmenin yolu, MainPage.xaml tüm içeriğini C# projesinden C++/WinRT projesine şimdi kopyalamaktır. Ancak bunu yaparsak, soluklanıp yeniden derleme yapabileceğimiz bir noktaya gelmemiz uzun zaman alır (böylece yol boyunca yaptığımız yazım hataları ya da başka hatalar gözden kaçabilir).
MainPage sınıfı için buyruksal kodu taşımayı tamamladıktan sonra, XAML dosyasının içeriğini kopyalayacağız ve projenin yine de derlenmeye devam edeceğinden emin olacağız.
IsClipboardContentChangedEnabled
Bu, varsayılan olarak olarak ayarlanmış bir get-set C# özelliğidir false.
MainPage'in bir üyesidir ve içinde SampleConfiguration.cstanımlanır.
C++/WinRT için bir erişimci işlevine, bir mutator işlevine ve alan olarak bir destek veri üyesine ihtiyacımız vardır.
IsClipboardContentChangedEnabled, MainPage'in durumu yerine örnekteki senaryolardan birinin durumunu temsil ettiğinden, yeni üyeleri SampleState adlı yeni bir yardımcı program türünde oluşturacağız. Bunu kaynak kod dosyamızda SampleConfiguration.cpp uygulayacağız ve üyeleri static oluşturacağız (uygulama genelinde yalnızca bir örneğe ihtiyacımız olduğundan ve sınıf örneğine gerek kalmadan bunlara erişebildiğimizden).
C++/WinRT projesinde, SampleConfiguration.cpp öğemize eşlik etmesi için SampleConfiguration.h adlı yeni bir Visual C++>Code>Üst bilgi dosyası (.h) öğesi ekleyin.
SampleConfiguration.h ve SampleConfiguration.cpp öğelerini aşağıdaki listelerle eşleşecek şekilde düzenleyin.
// SampleConfiguration.h
#pragma once
#include "pch.h"
namespace winrt::SDKTemplate
{
struct SampleState
{
static bool IsClipboardContentChangedEnabled();
static void IsClipboardContentChangedEnabled(bool checked);
private:
static bool isClipboardContentChangedEnabled;
};
}
// SampleConfiguration.cpp
...
#include "SampleConfiguration.h"
...
bool SampleState::isClipboardContentChangedEnabled = false;
...
bool SampleState::IsClipboardContentChangedEnabled()
{
return isClipboardContentChangedEnabled;
}
void SampleState::IsClipboardContentChangedEnabled(bool checked)
{
if (isClipboardContentChangedEnabled != checked)
{
isClipboardContentChangedEnabled = checked;
}
}
Yine, static depolamalı bir alanın (SampleState::isClipboardContentChangedEnabled gibi) uygulama içinde bir kez tanımlanması gerekir ve .cpp dosyası bunun için uygun bir yerdir (SampleConfiguration.cpp bu durumda).
BuildClipboardFormatsOutputString
Bu yöntem MainPage'in genel bir üyesidir ve içinde SampleConfiguration.cstanımlanır.
// SampleConfiguration.cs
...
public string BuildClipboardFormatsOutputString()
{
DataPackageView clipboardContent = Windows.ApplicationModel.DataTransfer.Clipboard.GetContent();
StringBuilder output = new StringBuilder();
if (clipboardContent != null && clipboardContent.AvailableFormats.Count > 0)
{
output.Append("Available formats in the clipboard:");
foreach (var format in clipboardContent.AvailableFormats)
{
output.Append(Environment.NewLine + " * " + format);
}
}
else
{
output.Append("The clipboard is empty");
}
return output.ToString();
}
...
C++/WinRT'de BuildClipboardFormatsOutputString'iSampleState'in genel statik yöntemi yapacağız. Herhangi bir örnek üyesine erişmediği için onu static yapabiliriz.
C++/WinRT'de Pano ve DataPackageView türlerini kullanmak için C++/WinRT Windows ad alanı üst bilgi dosyasını winrt/Windows.ApplicationModel.DataTransfer.heklememiz gerekir.
C# dilinde DataPackageView.AvailableFormats özelliği bir IReadOnlyList olduğundan bunun Count özelliğine erişebiliriz. C++/WinRT'de DataPackageView::AvailableFormats erişimci işlevi, çağırabildiğimiz bir Boyut erişimcisi işlevine sahip bir IVectorView döndürür.
C# System.Text.StringBuilder türünün kullanımını taşımak için, standart C++ türü std::wostringstream'ten yararlanacağız. Bu tür, geniş dizeler için bir çıkış akışıdır (ve bunu kullanmak için üst bilgi dosyasını eklememiz sstream gerekir).
StringBuilder ile yaptığınız gibi bir Append yöntemi kullanmak yerine ekleme işlecini (<<) wostringstream gibi bir çıkış akışıyla kullanırsınız. Daha fazla bilgi için bkz . iostream programlama ve C++/WinRT dizelerini biçimlendirme.
C# kodu, anahtar sözcüğüyle bir new oluşturur. C# dilinde nesneler, varsayılan olarak heap üzerinde new ile bildirilen başvuru tipleridir. Modern standart C++'da nesneler, yığında (kullanmadan new) bildirilen varsayılan değer türleridir. Dolayısıyla StringBuilder output = new StringBuilder(); öğesini C++/WinRT'ye basitçe std::wostringstream output; olarak taşırız.
C# var anahtar sözcüğü derleyiciden bir tür çıkarmasını ister.
var uygulamasını C++/WinRT ile auto ortamına taşırsınız. Ancak C++/WinRT'de, (kopyalamalardan kaçınmak için) çıkarılan (veya türetilen) bir türe referans isteyeceğiniz ve çıkarılan bir türe lvalue referansını auto& ile ifade edeceğiniz durumlar vardır. Ayrıca, lvalue ile mi yoksa rvalue ile mi başlatıldığına bakılmaksızın doğru şekilde bağlanan özel bir başvuru türüne sahip olmak istediğiniz durumlar da vardır. Ve bunu auto&& ile ifade edebilirsiniz. Bu, aşağıda yer alan aktarılmış koddaki for döngüsünde kullanıldığını gördüğünüz biçimdir.
lvalue ve rvalue kavramlarına giriş için bkz. Değer kategorileri ve onlara referanslar.
, pch.hve SampleConfiguration.h değerlerini aşağıdaki listelerle eşleşecek şekilde düzenleyinSampleConfiguration.cpp.
// pch.h
...
#include <sstream>
#include "winrt/Windows.ApplicationModel.DataTransfer.h"
...
// SampleConfiguration.h
...
struct SampleState
{
static hstring BuildClipboardFormatsOutputString();
...
}
...
// SampleConfiguration.cpp
...
using namespace Windows::ApplicationModel::DataTransfer;
...
hstring SampleState::BuildClipboardFormatsOutputString()
{
DataPackageView clipboardContent{ Clipboard::GetContent() };
std::wostringstream output;
if (clipboardContent && clipboardContent.AvailableFormats().Size() > 0)
{
output << L"Available formats in the clipboard:";
for (auto&& format : clipboardContent.AvailableFormats())
{
output << std::endl << L" * " << std::wstring_view(format);
}
}
else
{
output << L"The clipboard is empty";
}
return hstring{ output.str() };
}
Note
Kodun DataPackageView clipboardContent{ Clipboard::GetContent() }; satırındaki söz dizimi, modern standart C++'ta tekdüze ilklendirme olarak adlandırılan bir özelliği kullanır; bunun ayırt edici özelliği, = işareti yerine süslü ayraçların kullanılmasıdır. Bu söz dizimi, atama yerine başlatmanın gerçekleştirildiğini açıkça gösterir. Atama gibi görünen söz dizimi biçimini tercih ediyorsanız (ancak aslında değil), yukarıdaki söz dizimini eşdeğeriyle DataPackageView clipboardContent = Clipboard::GetContent();değiştirebilirsiniz. Bununla birlikte, karşılaştığınız kodda sık sık kullanılan her ikisini de görme olasılığınız yüksek olduğundan, başlatmayı ifade etmenin her iki yolunu da rahatça kullanmak iyi bir fikirdir.
DisplayToast
DisplayToast , C# MainPage sınıfının genel statik bir yöntemidir ve içinde SampleConfiguration.cstanımlandığını göreceksiniz. C++/WinRT'de bunu SampleState'in genel statik yöntemi yapacağız.
Bu yöntemi taşımayla ilgili ayrıntıların ve tekniklerin çoğuyla zaten karşılaştık. Dikkat edilmesi gereken yeni bir nokta, bir C# verbatim dize sabitini (@) standart bir C++ ham dize sabitine (LR) taşımanızdır.
Ayrıca, C++/WinRT'de ToastNotification ve XmlDocument türlerine başvurarak bunları ad alanı adına göre niteleyebilir veya aşağıdaki örnek gibi yönergeleri düzenleyebilir SampleConfiguration.cpp ve ekleyebilirsiniz using namespace .
using namespace Windows::UI::Notifications;
XmlDocument türüne başvururken ve başka bir Windows Çalışma Zamanı türüne başvurduğunda aynı seçime sahip olursunuz.
Bu öğeler dışında, aşağıdaki adımları gerçekleştirmek için daha önce yaptığınız yönergeleri izlemeniz gerekir.
- yöntemini içinde
SampleConfiguration.hbildirin ve içindeSampleConfiguration.cpptanımlayın. -
pch.höğesini, gerekli C++/WinRT Windows ad alanı başlık dosyalarını içerecek şekilde düzenleyin. - C++/WinRT nesnelerini öbekte değil, yığında oluşturun.
- Özellik alma erişimcilerine yapılan çağrıları işlev çağrısı söz dizimi (
()) ile değiştirin.
Derleyici/bağlayıcı hatalarının çok yaygın bir nedeni, ihtiyacınız olan C++/WinRT Windows ad alanı üst bilgi dosyalarını eklemeyi unutmaktır. Olası bir hata hakkında daha fazla bilgi için bkz. C3779: Derleyici neden "consume_Something: 'auto' döndüren işlev tanımlanmadan önce kullanılamaz" hatası veriyor?
Adım adım açıklamayı takip etmek ve DisplayToast'u kendiniz uyarlamak istiyorsanız, sonuçlarınızı indirdiğiniz Pano örneği kaynak kodunun ZIP dosyasındaki C++/WinRT sürümünün koduyla karşılaştırabilirsiniz.
Pano İçeriği Değişti Bildirimlerini Etkinleştir
EnableClipboardContentChangedNotifications , C# MainPage sınıfının genel statik bir yöntemidir ve içinde SampleConfiguration.cstanımlanır.
// SampleConfiguration.cs
...
public bool EnableClipboardContentChangedNotifications(bool enable)
{
if (IsClipboardContentChangedEnabled == enable)
{
return false;
}
IsClipboardContentChangedEnabled = enable;
if (enable)
{
Clipboard.ContentChanged += OnClipboardChanged;
Window.Current.Activated += OnWindowActivated;
}
else
{
Clipboard.ContentChanged -= OnClipboardChanged;
Window.Current.Activated -= OnWindowActivated;
}
return true;
}
...
private void OnClipboardChanged(object sender, object e) { ... }
private void OnWindowActivated(object sender, WindowActivatedEventArgs e) { ... }
...
C++/WinRT'de bunu SampleState'in genel statik yöntemi yapacağız.
C# dilinde, olay işleyici temsilcilerini eklemek ve kaldırmak için += ve -= işleç söz dizimini kullanırsınız. C++/WinRT'de, C++/WinRT'de temsilcileri kullanarak olayları işleme bölümünde açıklandığı gibi, temsilciyi kaydetmek/iptal etmek için çeşitli söz dizimsel seçenekleriniz vardır. Ancak genel yapı, olaya göre adlandırılmış bir çift işleve yapılan çağrılarla kaydolmanız ve kaydı kaldırmanızdır. Kaydetmek için temsilcinizi kaydetme işlevine geçirirsiniz ve karşılığında bir iptal belirteci alırsınız ( winrt::event_token). İptal etmek için bu belirteci iptal işlevine iletirsiniz. Bu durumda, işleyici statiktir ve (aşağıdaki kod listesinde görebileceğiniz gibi) fonksiyon çağrısı söz dizimi basittir.
Benzer belirteçler aslında arka planda C# dilinde kullanılır. Ama dil bu ayrıntıyı örtük hale getirir. C++/WinRT bunu açık hale getirir.
Nesne türü C# olay işleyicisi imzalarında görünür. C# dilinde, object, .NET System.Object türünün bir takma adıdır. C++/WinRT'deki eşdeğeri winrt::Windows::Foundation::IInspectable'dır. Bu nedenle, C++/WinRT olay işleyicilerinde IInspectable ifadesini görürsünüz.
SampleConfiguration.h ve SampleConfiguration.cpp öğelerini aşağıdaki listelerle eşleşecek şekilde düzenleyin.
// SampleConfiguration.h
...
static bool EnableClipboardContentChangedNotifications(bool enable);
...
private:
...
static event_token clipboardContentChangedToken;
static event_token activatedToken;
static void OnClipboardChanged(Windows::Foundation::IInspectable const& sender, Windows::Foundation::IInspectable const& e);
static void OnWindowActivated(Windows::Foundation::IInspectable const& sender, Microsoft::UI::Xaml::WindowActivatedEventArgs const& e);
...
// SampleConfiguration.cpp
...
using namespace Windows::Foundation;
using namespace Microsoft::UI;
using namespace Microsoft::UI::Xaml;
...
event_token SampleState::clipboardContentChangedToken;
event_token SampleState::activatedToken;
...
bool SampleState::EnableClipboardContentChangedNotifications(bool enable)
{
if (isClipboardContentChangedEnabled == enable)
{
return false;
}
IsClipboardContentChangedEnabled(enable);
if (enable)
{
clipboardContentChangedToken = Clipboard::ContentChanged(OnClipboardChanged);
activatedToken = Window::Current().Activated(OnWindowActivated);
}
else
{
Clipboard::ContentChanged(clipboardContentChangedToken);
Window::Current().Activated(activatedToken);
}
return true;
}
void SampleState::OnClipboardChanged(IInspectable const&, IInspectable const&){}
void SampleState::OnWindowActivated(IInspectable const&, WindowActivatedEventArgs const& e){}
OnClipboardChanged ve OnWindowActivated olay işleme temsilcilerini şimdilik boş iskelet olarak bırakın. Zaten taşınacak üyeler listemizde yer alıyorlar, bu yüzden onlara sonraki alt bölümlerde değineceğiz.
OnNavigatedTo
OnNavigatedTo , C# MainPage sınıfının korumalı bir yöntemidir ve içinde MainPage.xaml.cstanımlanır. İşte burada, başvurduğu XAML ListBox ile birlikte.
<!-- MainPage.xaml -->
...
<ListBox x:Name="ScenarioControl" ... />
...
// MainPage.xaml.cs
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// Populate the scenario list from the SampleConfiguration.cs file
var itemCollection = new List<Scenario>();
int i = 1;
foreach (Scenario s in scenarios)
{
itemCollection.Add(new Scenario { Title = $"{i++}) {s.Title}", ClassType = s.ClassType });
}
ScenarioControl.ItemsSource = itemCollection;
if (Window.Current.Bounds.Width < 640)
{
ScenarioControl.SelectedIndex = -1;
}
else
{
ScenarioControl.SelectedIndex = 0;
}
}
Senaryo nesneleri koleksiyonumuzun kullanıcı arabirimine atandığı yer burası olduğundan, bu önemli ve ilginç bir yöntemdir. C# kodu bir System.Collections.Generic.List of Scenario nesneleri oluşturur ve bunu ListBox'ınItemsSource özelliğine atar (bu bir öğe denetimidir). Ayrıca, C# dilinde dize ilişkilendirmesini kullanarak her Senaryo nesnesinin başlığını oluştururuz (özel karakterin kullanımına $ dikkat edin).
C++/WinRT'de OnNavigatedTo'yu MainPage'in ortak bir metodu yapacağız. Derlemenin başarıyla tamamlanması için XAML'ye bir yer tutucu ListBox öğesi ekleyeceğiz. Kod listesinden sonra bazı ayrıntıları inceleyeceğiz.
<!-- MainPage.xaml -->
...
<StackPanel ...>
...
<ListBox x:Name="ScenarioControl" />
</StackPanel>
...
// MainPage.h
...
void OnNavigatedTo(Microsoft::UI::Xaml::Navigation::NavigationEventArgs const& e);
...
// MainPage.cpp
...
using namespace winrt::Microsoft::UI::Xaml;
using namespace winrt::Microsoft::UI::Xaml::Navigation;
...
void MainPage::OnNavigatedTo(NavigationEventArgs const& /* e */)
{
auto itemCollection = winrt::single_threaded_observable_vector<IInspectable>();
int i = 1;
for (auto s : MainPage::scenarios())
{
s.Title = winrt::to_hstring(i++) + L") " + s.Title;
itemCollection.Append(winrt::box_value(s));
}
ScenarioControl().ItemsSource(itemCollection);
if (Window::Current().Bounds().Width < 640)
{
ScenarioControl().SelectedIndex(-1);
}
else
{
ScenarioControl().SelectedIndex(0);
}
}
...
Yine winrt::single_threaded_observable_vector işlevini çağırıyoruz, ancak bu kez bir IInspectable koleksiyonu oluşturmak için. Bu, Senaryo nesnelerimizin kutulama işlemini tam zamanında gerçekleştirme kararımızın bir parçasıydı.
C#'nin burada dize ilişkilendirmesi kullanması yerine, to_hstring işlevinin ve winrt::hstring birleştirme işlecinin bir bileşimini kullanırız.
isApplicationWindowActive
C# dilinde isApplicationWindowActive, bool sınıfına ait basit bir özel alandır ve içinde SampleConfiguration.cstanımlanır. Varsayılan değer olarak false ayarlanır. C++/WinRT'de, bunu (az önce açıkladığımız nedenlerden dolayı) SampleConfiguration.h ve SampleConfiguration.cpp dosyalarında, aynı varsayılan değerle, SampleState'in özel statik bir alanı yapacağız.
Statik bir alanı bildirmeyi, tanımlamayı ve başlatmayı zaten gördük. Hatırlamak için, isClipboardContentChangedEnabled alanıyla yaptıklarımıza geri dönün ve isApplicationWindowActive alanı için de aynısını yapın.
needToPrintClipboardFormat
isApplicationWindowActive ile aynı desen (bundan hemen önceki başlığa bakın).
Button_Click
Button_Click , C# MainPage sınıfının özel (olay işleme) yöntemidir ve içinde MainPage.xaml.cstanımlanır. İşte, başvurduğu XAML SplitView ile onu kaydeden ToggleButton birlikte burada.
<!-- MainPage.xaml -->
...
<SplitView x:Name="Splitter" ... />
...
<ToggleButton Click="Button_Click" .../>
...
private void Button_Click(object sender, RoutedEventArgs e)
{
Splitter.IsPaneOpen = !Splitter.IsPaneOpen;
}
C++/WinRT'ye aktarılan eşdeğeri de buradadır. C++/WinRT sürümünde olay işleyicisinin public olduğunu unutmayın (gördüğünüz gibi, onu private: bildirimlerinden önce bildirirsiniz). Bunun nedeni, bu örnekte olduğu gibi XAML işaretlemesinde kaydedilen bir olay işleyicisinin, XAML işaretlemesinin ona erişebilmesi için C++/WinRT'de public olması gerekmesidir. Öte yandan, kesinlik temelli kodda bir olay işleyicisi kaydederseniz (daha önce MainPage::EnableClipboardContentChangedNotifications'da yaptığımız gibi), olay işleyicisinin olması publicgerekmez.
<!-- MainPage.xaml -->
...
<StackPanel ...>
...
<SplitView x:Name="Splitter" />
</StackPanel>
...
// MainPage.h
...
void Button_Click(Windows::Foundation::IInspectable const& sender, Microsoft::UI::Xaml::RoutedEventArgs const& e);
private:
...
// MainPage.cpp
void MainPage::Button_Click(Windows::Foundation::IInspectable const& /* sender */, Microsoft::UI::Xaml::RoutedEventArgs const& /* e */)
{
Splitter().IsPaneOpen(!Splitter().IsPaneOpen());
}
DisplayChangedFormats
C# dilinde DisplayChangedFormats , MainPage sınıfına ait özel bir yöntemdir ve içinde SampleConfiguration.cstanımlanır.
private void DisplayChangedFormats()
{
string output = "Clipboard content has changed!" + Environment.NewLine;
output += BuildClipboardFormatsOutputString();
NotifyUser(output, NotifyType.StatusMessage);
}
C++/WinRT'de, bunu SampleConfiguration.h ve SampleConfiguration.cpp dosyalarında SampleState öğesinin özel bir statik alanı yapacağız (çünkü herhangi bir örnek üyesine erişmiyor). Bu yöntemin C# kodu System.Text.StringBuilder kullanmaz; ancak C++/WinRT sürümü için bunun std::wostringstream kullanmak için başka bir iyi yer olması için yeterli dize biçimlendirmesi yapar.
C# kodunda kullanılan statik System.Environment.NewLine özelliği yerine çıkış akışına standart C++ std::endl (yeni satır karakteri) ekleyeceğiz.
// SampleConfiguration.h
...
private:
static void DisplayChangedFormats();
...
// SampleConfiguration.cpp
void SampleState::DisplayChangedFormats()
{
std::wostringstream output;
output << L"Clipboard content has changed!" << std::endl;
output << BuildClipboardFormatsOutputString().c_str();
MainPage::Current().NotifyUser(output.str(), NotifyType::StatusMessage);
}
Yukarıdaki C++/WinRT sürümünün tasarımında küçük bir verimsizlik vardır. İlk olarak bir std::wostringstream oluşturacağız. Ancak daha önce taşıdığımız BuildClipboardFormatsOutputString yöntemini de çağırırız. Bu yöntem kendi std::wostringstream öğesini oluşturur. Ve akışını winrt::hstring'e dönüştürür ve bunu döndürür. Hstring'i geri döndüren hstring::c_str işlevini C stili bir dizeye geri döndürmek için çağırırız ve sonra bunu akışımıza ekleriz. Yalnızca bir std::wostringstream oluşturmak ve yöntemlerin dizeleri doğrudan buna ekleyebilmesi için onun referansını ilgili yerlere aktarmak daha verimli olacaktır.
İşte bunu, indirdiğiniz ZIP dosyasındaki Pano örneği kaynak kodunun C++/WinRT sürümünde yapıyoruz. Bu kaynak kodunda SampleState::AddClipboardFormatsOutputString adlı yeni bir özel statik yöntem vardır ve bu yöntem bir çıkış akışı başvurusu alır ve üzerinde çalışır. Ardından SampleState::D isplayChangedFormats ve SampleState::BuildClipboardFormatsOutputString yöntemleri bu yeni yöntemi çağırmak için yeniden düzenlenmiştir. İşlevsel olarak bu konudaki kod listeleriyle eşdeğerdir, ancak daha verimlidir.
Footer_Click
Footer_Click , C# MainPage sınıfına ait zaman uyumsuz bir olay işleyicidir ve içinde MainPage.xaml.cstanımlanır. Aşağıdaki kod listesi, indirdiğiniz kaynak koddaki yöntemle işlevsel olarak eşdeğerdir. Ama burada, ne yaptığını ve dolayısıyla onu nasıl port etmemiz gerektiğini görmeyi kolaylaştırmak için onu tek satırdan dört satıra çıkardım.
async void Footer_Click(object sender, RoutedEventArgs e)
{
var hyperlinkButton = (HyperlinkButton)sender;
string tagUrl = hyperlinkButton.Tag.ToString();
Uri uri = new Uri(tagUrl);
await Windows.System.Launcher.LaunchUriAsync(uri);
}
Yöntem teknik olarak eşzamansız olsa da, await sonrasında hiçbir şey yapmaz; bu nedenle await’e (ya da async anahtar sözcüğüne) gerek duymaz. Büyük olasılıkla Visual Studio IntelliSense iletisinden kaçınmak için bunları kullanır.
Eşdeğer C++/WinRT yöntemi de zaman uyumsuz olur ( Launcher.LaunchUriAsync'i çağırdığı için). Ancak co_await yapması ya da asenkron bir nesne döndürmesi gerekmez. Ve zaman uyumsuz nesneler hakkında co_await bilgi için bkz. C++/WinRT ile eşzamanlılık ve zaman uyumsuz işlemler.
Şimdi yöntemin ne yaptığını konuşalım. Bu, bir HyperlinkButton öğesinin Click olayının olay işleyicisi olduğundan, sender adlı nesne aslında bir HyperlinkButton'tır. Dolayısıyla tür dönüştürme güvenlidir (alternatif olarak bu dönüştürmeyi olarak sender as HyperlinkButtonifade edebilirdik). Ardından Tag özelliğinin değerini alacağız (C# projesindeki XAML işaretlemesine bakarsanız, bunun web URL'sini temsil eden bir dizeye ayarlandığını görürsünüz).
FrameworkElement.Tag özelliği (HyperlinkButton, bir FrameworkElement'tir) object türünde olsa da, C# dilinde bunu Object.ToString ile dizeye dönüştürebiliriz. Sonuçta elde edilen dizeden bir Uri nesnesi oluştururuz. Son olarak (Kabuğun yardımıyla) bir tarayıcı başlatıp URL'ye gideriz.
Burada C++/WinRT'ye (netlik için genişletilmiş) taşıma yöntemi ve ardından ayrıntıların açıklaması yer alır.
// pch.h
...
#include "winrt/Windows.System.h"
...
// MainPage.h
...
void Footer_Click(Windows::Foundation::IInspectable const& sender, Microsoft::UI::Xaml::RoutedEventArgs const& e);
private:
...
// MainPage.cpp
...
using namespace winrt::Windows::Foundation;
using namespace winrt::Microsoft::UI::Xaml::Controls;
...
void MainPage::Footer_Click(Windows::Foundation::IInspectable const& sender, Microsoft::UI::Xaml::RoutedEventArgs const&)
{
auto hyperlinkButton{ sender.as<HyperlinkButton>() };
hstring tagUrl{ winrt::unbox_value<hstring>(hyperlinkButton.Tag()) };
Uri uri{ tagUrl };
Windows::System::Launcher::LaunchUriAsync(uri);
}
Her zaman olduğu gibi olay işleyicisini publicyaparız. Bunu HyperlinkButton'a dönüştürmek için gönderen nesnesinde as işlevini kullanırız. C++/WinRT'de Tag özelliği bir IInspectable'dir (Nesne'nin eşdeğeri). Ama IInspectable'daTostring yok. Bunun yerine, IInspectable kutusunu bir skaler değere (bu örnekte bir dize) kaldırmamız gerekir. Yine, kutulama ve kutudan çıkarma hakkında daha fazla bilgi için bkz. Değerleri IInspectable’a kutulama ve kutudan çıkarma.
Son iki satır daha önce gördüğümüz taşıma desenlerini yineler ve C# sürümünü neredeyse yankılar.
HandleClipboardChanged
Bu yöntemi taşımakta yeni bir şey yok. İndirdiğiniz Pano örnek kaynak kodunun ZIP'inde C# ve C++/WinRT sürümlerini karşılaştırabilirsiniz.
OnClipboardChanged ve OnWindowActivated
Şu ana kadar bu iki olay işleyicisi için yalnızca boş saptamalar var. Ancak bunları taşıma işlemi basittir ve tartışılacak yeni bir şey oluşturmaz.
ScenarioControl_SelectionChanged
Bu, C# MainPage sınıfına ait ve içinde MainPage.xaml.cstanımlanan başka bir özel olay işleyicidir. C++/WinRT'de bunu public yapacağız ve MainPage.h ile MainPage.cpp içinde gerçekleştireceğiz.
Bu yöntem için, özel bir Boole alanı olan ve false ile başlatılan MainPage::navigating alanına ihtiyacımız olacak. Ve MainPage.xaml içinde, ScenarioFrame adlı bir Çerçeve gerekir. Ancak, bu ayrıntıların dışında, bu yöntemin taşınması herhangi bir yeni teknik ortaya çıkarmaz.
El ile taşıma yerine, indirdiğiniz Pano örnek kaynak kodunun ZIP'indeki C++/WinRT sürümünden kod kopyalarsanız, burada kullanılan mainpage::NavigateTo ifadesini görürsünüz. Şimdilik NavigateTo'nun içeriğini ScenarioControl_SelectionChanged olarak yeniden düzenlemeniz yeterlidir.
UpdateStatus
MainPage.UpdateStatus için şu ana kadar yalnızca bir taslak var. Uygulamanın taşınması da yine büyük ölçüde zaten bilinen konuları tekrar ele alır. Dikkat edilmesi gereken yeni bir nokta da şudur: C# dilinde string değerini String.Empty ile karşılaştırabilirken, C++/WinRT'de bunun yerine winrt::hstring::empty işlevini çağırırız. Bir diğeri de nullptr C#'nin nullstandart C++ eşdeğeridir.
Port etme işleminin geri kalanını, zaten ele aldığımız tekniklerle tamamlayabilirsiniz. Bu yöntemin taşınabilir sürümü derlenmeden önce yapmanız gerekenlerin bir listesi aşağıdadır.
-
MainPage.xamlöğesine StatusBorder adlı bir Kenarlık ekleyin. - öğesine
MainPage.xamlStatusBlock adlı bir TextBlock ekleyin. - öğesine
MainPage.xamlStatusPanel adlı bir StackPanel ekleyin. -
pch.heklemek için#include "winrt/Microsoft.UI.Xaml.Media.h"ekleyin. -
pch.höğesine#include "winrt/Microsoft.UI.Xaml.Automation.Peers.h"ekleyin. -
MainPage.cppeklemek içinusing namespace winrt::Microsoft::UI::Xaml::Media;. -
MainPage.cppeklemekusing namespace winrt::Microsoft::UI::Xaml::Automation::Peers;için.
MainPage'i taşıma işlemini tamamlamak için gereken XAML ve stilleri kopyalayın
XAML için ideal durum, C# ve C++/WinRT projesinde aynı XAML işaretlemesini kullanabilmenizdir. Pano örneği de bu durumlardan biridir.
Styles.xaml Dosyasında Pano örneği, uygulamanın kullanıcı arabirimindeki düğmelere, menülere ve diğer kullanıcı arabirimi öğelerine uygulanan stillerden oluşan bir XAML Kaynak Sözlüğüne sahiptir.
Styles.xaml sayfası, App.xaml içinde birleştirilmiştir. Ayrıca kullanıcı arabirimi için standart MainPage.xaml başlangıç noktası da vardır. Bu noktayı kısa bir süre önce görmüştük. Artık bu üç .xaml dosyayı değiştirmeden projenin C++/WinRT sürümünde yeniden kullanabiliriz.
Varlık dosyalarında olduğu gibi, uygulamanızın birden çok sürümündeki aynı paylaşılan XAML dosyalarına başvurmayı seçebilirsiniz. Bu kılavuzda basitlik açısından dosyaları C++/WinRT projesine kopyalayıp bu şekilde ekleyeceğiz.
Klasöre \Clipboard_sample\SharedContent\xaml gidin, ve App.xamlöğesini seçip kopyalayın MainPage.xaml ve ardından bu iki dosyayı \Clipboard\Clipboard C++/WinRT projenizdeki klasöre yapıştırın ve istendiğinde dosyaları değiştirmeyi seçin.
Visual Studio'deki C++/WinRT projesinde, açmak için Tüm Dosyaları Göster'e tıklayın. Şimdi proje düğümünü hemen altına yeni bir klasör ekleyin ve adını verin Styles. Dosya Gezgini'nde klasöre \Clipboard_sample\SharedContent\xaml gidin, öğesini seçip kopyalayın Styles.xamlve yeni oluşturduğunuz klasöre yapıştırın \Clipboard\Clipboard\Styles . C++/WinRT projesindeki Çözüm Gezgini’a geri dönün, Styles klasörüne sağ tıklayın, >Ekle>Varolan Öğe... seçeneğini belirleyin ve \Clipboard\Clipboard\Styles konumuna gidin. Dosya seçicide Styles seçin ve tıklayın.
C++/WinRT projesine proje düğümünü hemen altına ve adlı Stylesyeni bir klasör ekleyin. Klasöre \Clipboard_sample\SharedContent\xaml gidin, öğesini seçip kopyalayın Styles.xamlve C++/WinRT projenizdeki klasöre yapıştırın \Clipboard\Clipboard\Styles .
Styles klasörüne sağ tıklayın (C++/WinRT projesindeki Çözüm Gezgini'da) >Ekle>Varolan Öğe...'yi seçin ve \Clipboard\Clipboard\Styles konumuna gidin. Dosya seçicide Styles seçin ve tıklayın.
Kapatmak için Tüm Dosyaları Göster'e yeniden tıklayın.
MainPage'i taşımayı tamamladık ve adımları takip ediyorsanız C++/WinRT projeniz derlenip çalıştırılacaktır.
Dosyalarınızı .idl birleştirin
Kullanıcı arabirimi için standart MainPage.xaml başlangıç noktasına ek olarak Pano örneğinde senaryoya özgü beş XAML sayfası ve bunlara karşılık gelen arka planda kod dosyaları bulunur. Projenin C++/WinRT sürümünde bu sayfaların tümünün gerçek XAML işaretlemesini değiştirmeden yeniden kullanacağız. Sonraki birkaç önemli bölümde arka planda kodun nasıl taşınabilir olduğunu inceleyeceğiz. Ama ondan önce IDL'yi konuşalım.
Çalışma zamanı sınıflarınızın IDL'sini tek bir IDL dosyasında birleştirmenin bir değeri vardır. Bu değer hakkında bilgi edinmek için bkz: Çalışma zamanı sınıflarını MIDL dosyalarına ayırma (.idl). Sonraki adımda, CopyFiles.idl, CopyImage.idl, CopyText.idl, HistoryAndRoaming.idl ve OtherScenarios.idl dosyalarının içeriğini, bu IDL’yi Project.idl adlı tek bir dosyaya taşıyarak birleştireceğiz (ve ardından orijinal dosyaları sileceğiz).
Bunu yaparken, bu beş XAML sayfa türünün her birinden otomatik olarak oluşturulan kukla özelliğini (Int32 MyProperty;ve uygulamasını) da kaldıralım.
İlk olarak, C++/WinRT projesine yeni bir Midl Dosyası (.idl) öğesi ekleyin.
Project.idl olarak adlandırın. içindekilerin Project.idl tamamını aşağıdaki kodla değiştirin.
// Project.idl
namespace SDKTemplate
{
[default_interface]
runtimeclass CopyFiles : Microsoft.UI.Xaml.Controls.Page
{
CopyFiles();
}
[default_interface]
runtimeclass CopyImage : Microsoft.UI.Xaml.Controls.Page
{
CopyImage();
}
[default_interface]
runtimeclass CopyText : Microsoft.UI.Xaml.Controls.Page
{
CopyText();
}
[default_interface]
runtimeclass HistoryAndRoaming : Microsoft.UI.Xaml.Controls.Page
{
HistoryAndRoaming();
}
[default_interface]
runtimeclass OtherScenarios : Microsoft.UI.Xaml.Controls.Page
{
OtherScenarios();
}
}
Gördüğünüz gibi, bu yalnızca tek tek .idl dosyaların içeriğinin bir kopyasıdır, hepsi bir ad alanı içindedir ve MyProperty her çalışma zamanı sınıfından kaldırılmıştır.
Visual Studio'daki Çözüm Gezgini tüm özgün IDL dosyalarını (CopyFiles.idl, CopyImage.idl, CopyText.idl, HistoryAndRoaming.idlve OtherScenarios.idl) birden çok kez seçin ve Düzenle>Kaldır'ı seçin (iletişim kutusunda Sil'i seçin).
Son olarak — ve MyProperty öğesinin kaldırılmasını tamamlamak için — aynı beş XAML sayfa türünün her birine ait .h ve .cpp dosyalarında, int32_t MyProperty() erişimci ve void MyProperty(int32_t) değiştirici işlevlerinin bildirimlerini ve tanımlarını silin.
Bu arada, XAML dosyalarınızın adının temsil ettikleri sınıfın adıyla eşleşmesi her zaman iyi bir fikirdir. Örneğin, bir XAML işaretleme dosyanız x:Class="MyNamespace.MyPage" varsa, bu dosya olarak adlandırılmalıdır MyPage.xaml. Bu teknik bir gereksinim olmasa da, aynı artefakt için farklı adlar arasında gidip gelmek zorunda kalmamak, projenizi daha anlaşılır, bakımı daha kolay ve üzerinde çalışması daha kolay hâle getirir.
CopyFiles
C# projesinde, CopyFiles XAML sayfa türü, CopyFiles.xaml ve CopyFiles.xaml.cs kaynak kodu dosyalarında gerçekleştirilir.
CopyFiles üyelerinin her birine tek tek göz atalım.
rootPage
Bu özel bir alan.
// CopyFiles.xaml.cs
...
public sealed partial class CopyFiles : Page
{
MainPage rootPage = MainPage.Current;
...
}
...
C++/WinRT'de bu şekilde tanımlayabilir ve başlatabiliriz.
// CopyFiles.h
...
struct CopyFiles : CopyFilesT<CopyFiles>
{
...
private:
SDKTemplate::MainPage rootPage{ MainPage::Current() };
};
...
Yeniden ( MainPage::current ile olduğu gibi), CopyFiles::rootPage , uygulama türü değil, öngörülen tür olan SDKTemplate::MainPage türünde olarak bildirilir.
CopyFiles (oluşturucu)
C++/WinRT projesinde CopyFiles türü zaten istediğimiz kodu içeren bir oluşturucuya sahiptir (yalnızca InitializeComponent'i çağırır).
CopyButton_Click
C# CopyButton_Click yöntemi bir olay işleyicidir ve imzasında anahtar sözcüğünden async yöntemin zaman uyumsuz iş yaptığını söyleyebiliriz. C++/WinRT'de, zaman uyumsuz bir yöntemi eş yordam olarak gerçekleştiririz. C++/WinRT’de eşzamanlılığa giriş ve coroutine’in ne olduğuna dair bir açıklama için, C++/WinRT ile eşzamanlılık ve zaman uyumsuz işlemler konusuna bakın.
Bir coroutine tamamlandıktan sonra ek çalışmaları planlamak istemek yaygındır; bu gibi durumlarda coroutine, beklenebilen ve isteğe bağlı olarak ilerleme bilgisi sağlayan bir zaman uyumsuz nesne türü döndürür. Ancak bu noktalar genellikle bir olay işleyicisi için geçerli değildir. Yani, eşzamansız işlemler gerçekleştiren bir olay işleyiciniz olduğunda, bunu winrt::fire_and_forget döndüren bir korutin olarak uygulayabilirsiniz. Daha fazla bilgi için Ateşle ve unut bölümüne bakın.
Fire-and-forget coroutine fikri, ne zaman tamamlandığını umursamamanız olsa da, yapılan iş arka planda yine de sürer (veya askıya alınmış durumda olup sürdürülmeyi bekler). C# uygulamasından, CopyButton_Click öğesinin this işaretçisine bağlı olduğunu görebilirsiniz (örnek veri üyesi rootPage'ye erişir). Bu nedenle, this işaretçisinin (bir CopyFiles nesnesine işaret eden bir işaretçi) CopyButton_Click eş yordamından daha uzun süre varlığını sürdürdüğünden emin olmalıyız. Kullanıcının kullanıcı arabirimi sayfaları arasında gezindiği bu örnek uygulama gibi bir durumda, bu sayfaların yaşam ömrünü doğrudan denetleyemiyoruz.
CopyFiles sayfası, CopyButton_Click hâlâ bir arka plan iş parçacığında çalışıyorken (sayfadan ayrılarak) yok edilirse, rootPage öğesine erişmek güvenli olmayacaktır. Eş yordamın doğru çalışması için, this işaretçisine güçlü bir başvuru edinmesi ve bu başvuruyu eş yordamın ömrü boyunca koruması gerekir. Daha fazla bilgi için bkz. C++/WinRT'de güçlü ve zayıf başvurular.
Örneğin C++/WinRT sürümüne bakarsanız , CopyFiles::CopyButton_Click sayfasında, bunun yığın üzerinde basit bir bildirimle yapıldığını görürsünüz.
fire_and_forget CopyFiles::CopyButton_Click(IInspectable const&, RoutedEventArgs const&)
{
auto lifetime{ get_strong() };
...
}
Şimdi taşınabilir kodun dikkat çekici olan diğer yönlerine bakalım.
Kodda bir FileOpenPicker nesnesi örneği oluştururuz ve iki satır sonra bu nesnenin FileTypeFilter özelliğine erişiyoruz. Bu özelliğin dönüş türü, dizelerin bir IVector'ını uygular. Ve bu IVector üzerinde IVector<T>.ReplaceAll(T[]) yöntemini çağırırız. İlginç olan, bir dizinin beklendiği yönteme geçirmekte olduğumuz değerdir. Kod satırı aşağıdadır.
filePicker.FileTypeFilter().ReplaceAll({ L"*" });
Aktardığımız değer ({ L"*" }), standart bir C++ başlatıcı listesidir. Bu durumda tek bir nesne içerir, ancak başlatıcı listesi herhangi bir sayıda virgülle ayrılmış nesne içerebilir. C++/WinRT'nin, başlatıcı listesini bunun gibi bir yönteme geçirme kolaylığı sağlayan parçaları Standart başlatıcı listelerinde açıklanmıştır.
C++/WinRT’de C# await anahtar sözcüğünü co_await olarak kullanıyoruz. Koddaki örnek aşağıda verilmiştır.
auto storageItems{ co_await filePicker.PickMultipleFilesAsync() };
Ardından bu C# kodu satırını göz önünde bulundurun.
dataPackage.SetStorageItems(storageItems);
C#, < tarafından temsil edilen IReadOnlyList>StorageFile dosyasını > tarafından beklenen IEnumerableIStorageItem'a örtük olarak dönüştürebilir. Ancak C++/WinRT'de IVectorView<StorageFile'dan>IIterable<IStorageItem'a> açıkça dönüştürmemiz gerekir. Bu nedenle işlevin nasıl çalıştığının başka bir örneğini de elde ettik.
dataPackage.SetStorageItems(storageItems.as<IVectorView<IStorageItem>>());
C# dilinde anahtar sözcüğünü null kullandığımız yerde (örneğin, Clipboard.SetContentWithOptions(dataPackage, null)), C++/WinRT içinde kullanırız nullptr (örneğin, Clipboard::SetContentWithOptions(dataPackage, nullptr)).
PasteButton_Click
Bu, başlat ve unut türünde bir eşyordam biçimindeki başka bir olay işleyicisidir. Şimdi taşınabilir kodun dikkat çekici yönlerine bakalım.
Örneğin C# sürümünde, istisnaları catch (Exception ex) ile yakalarız. Aktarılmış C++/WinRT kodunda catch (winrt::hresult_error const& ex) ifadesini görürsünüz.
winrt::hresult_error ve bununla çalışma hakkında daha fazla bilgi için bkz. C++/WinRT ile hata işleme.
C# nesnesinin null olup olmadığını test etme örneğidir if (storageItems != null). C++/WinRT'de, nullptr'e karşı testi dahili olarak gerçekleştiren bool dönüştürme işlecine güvenebiliriz.
Aşağıda, örneğin taşınabilir C++/WinRT sürümünden kod parçasının biraz basitleştirilmiş bir sürümü verilmiştir.
std::wostringstream output;
output << std::wstring_view(ApplicationData::Current().LocalFolder().Path());
Gibi bir winrt::hstring'denstd::wstring_view oluşturmak, hstring::c_str işlevini çağırmaya bir alternatif gösterir (winrt::hstring'i C stili bir dizeye dönüştürmek için). Bu alternatif, hstring'instd::wstring_view dönüştürme işleci sayesinde çalışır.
C# öğesinin bu parçasını düşünün.
var file = storageItem as StorageFile;
if (file != null)
...
C# as anahtar sözcüğünü C++/WinRT'ye taşımak için, şimdiye kadar as işlevinin birkaç kez kullanıldığını gördük. Bu işlev, tür dönüştürmesi başarısız olursa bir özel durum oluşturur. Ancak başarısız olursa dönüştürmenin döndürülmesini nullptr istiyorsak (kodda bu koşulu işleyebilmemiz için), bunun yerine try_as işlevini kullanırız.
auto file{ storageItem.try_as<StorageFile>() };
if (file)
...
CopyFiles taşıma işlemini tamamlamak için gereken XAML'yi kopyalayın
Artık özgün örnek kaynak kodu indirme klasöründen dosyanın CopyFiles.xaml içeriğinin shared tamamını seçebilir ve bunu C++/WinRT projesindeki dosyaya CopyFiles.xaml yapıştırabilirsiniz (C++/WinRT projesinde bu dosyanın mevcut içeriğini değiştirerek).
Son olarak, CopyFiles.h ve .cpp öğelerini düzenleyin ve yer tutucu ClickHandler fonksiyonunu silin; çünkü ilgili XAML işaretlemesinin üzerine az önce yazdık.
CopyFiles'ı taşımayı tamamladık ve adımları takip ediyorsanız C++/WinRT projeniz derlenip çalıştırılacak ve CopyFiles senaryosu işlevsel olacaktır.
CopyImage
CopyImage XAML sayfa türünü taşımak için, CopyFiles için olduğu gibi aynı süreci izlersiniz. CopyImage'ı aktarırken, using deyiminin kullanımına rastlarsınız; bu deyim, IDisposable arabirimini uygulayan nesnelerin doğru şekilde bertaraf edilmesini sağlar.
if (imageReceived != null)
{
using (var imageStream = await imageReceived.OpenReadAsync())
{
... // Pass imageStream to other APIs, and do other work.
}
}
C++/WinRT'deki eşdeğer arabirim, tek Close yöntemiyle IClosable'dır. Yukarıdaki C# kodunun C++/WinRT eşdeğeri aşağıdadır.
if (imageReceived)
{
auto imageStream{ co_await imageReceived.OpenReadAsync() };
... // Pass imageStream to other APIs, and do other work.
imageStream.Close();
}
C++/WinRT nesneleri öncelikle belirlenimci sonlandırması olmayan dillerin yararına IClosable'ı uygular. C++/WinRT belirlenimci sonlandırmaya sahiptir ve bu nedenle C++/WinRT yazarken genellikle IClosable::Close çağrısı yapmamız gerekmez. Ama bazen bırakmak iyi olur ve bu da o anlardan biridir. Burada imageStream tanımlayıcısı, temelindeki bir Windows Çalışma Zamanı nesnesini (bu örnekte IRandomAccessStreamWithContentType uygulayan bir nesneyi) saran, başvuru sayımlı bir sarmalayıcıdır. imageStream sonlandırıcısının (yıkıcısının) içinde bulunduğu kapsamın sonunda (süslü parantezlerde) çalışacağını belirleyebilsek de, bu sonlandırıcının Close çağrısını yapacağından emin olamayız. Bunun nedeni, imageStream'i diğer API'lere aktarmış olmamızdır; bu API'ler hâlâ temeldeki Windows Çalışma Zamanı nesnesinin referans sayısına katkıda bulunuyor olabilir. Dolayısıyla bu durum, Close'u açıkça çağırmanın iyi bir fikir olduğu bir durumdur. Daha fazla bilgi için bkz. Tükettiğim çalışma zamanı sınıflarında IClosable::Close çağrısı yapmam gerekiyor mu?.
Ardından, OnDeferredImageRequestedHandler olay işleyicisinde bulacağınız C# ifadesi (uint)(imageDecoder.OrientedPixelWidth * 0.5)ni ele alın. Bu ifade, bir uint'ı bir double ile çarparak sonuçta bir double elde eder. Ardından bunu uint türüne dönüştürür. C++/WinRT'de benzer görünen bir C tarzı tür dönüştürme ((uint32_t)(imageDecoder.OrientedPixelWidth() * 0.5)) kullanabilirdik, ancak tam olarak hangi tür dönüştürmeyi kastettiğimizi açıkça belirtmek tercih edilir ve bu durumda bunu static_cast<uint32_t>(imageDecoder.OrientedPixelWidth() * 0.5) ile yapardık.
CopyImage.OnDeferredImageRequestedHandler'ın C# sürümünün yan finally tümcesi vardır, ancak yan catch tümcesi yoktur. C++/WinRT sürümünde biraz daha ileri gittik ve gecikmeli işlemenin başarılı olup olmadığını bildirebilmemiz için bir catch yan tümce uyguladık.
Bu XAML sayfasının geri kalanını taşımak, üzerinde tartışılacak yeni bir şey ortaya koymuyor. Sahte ClickHandler işlevini silmeyi unutmayın.
CopyFiles'da olduğu gibi, bağlantı noktasındaki son adım içeriğinin tamamını CopyImage.xamlseçmek ve C++/WinRT projesinde aynı dosyaya yapıştırmaktır.
Metin Kopyala
CopyText.xaml ve CopyText.xaml.cs'i, daha önce ele aldığımız teknikleri kullanarak taşıyabilirsiniz.
Geçmiş ve Roaming
HistoryAndRoaming XAML sayfa türünü taşıma sırasında ortaya çıkan bazı ilgi çekici noktalar vardır.
İlk olarak, C# kaynak koduna göz atın ve denetim akışını OnNavigatedTo'dan OnHistoryEnabledChanged olay işleyicisi üzerinden, son olarak da asenkron CheckHistoryAndRoaming işlevine kadar takip edin (bu işlev await edilmediğinden, temelde “başlat ve unut” şeklinde çalışır).
CheckHistoryAndRoaming zaman uyumsuz olduğundan, C++/WinRT'de işaretçinin this ömrü konusunda dikkatli olmamız gerekir. Kaynak kod dosyasındaki uygulamaya HistoryAndRoaming.cpp bakarsanız sonucu görebilirsiniz. İlk olarak, Pano::HistoryEnabledChanged ve Pano::RoamingEnabledChanged olaylarına temsilciler eklediğimizde, HistoryAndRoaming sayfa nesnesine yalnızca zayıf bir başvuru alıyoruz. Bunu, temsilciyi this işaretçisine değil, winrt::get_weak tarafından döndürülen değere bağımlı olacak şekilde oluşturarak yaparız. Bu da, nihayetinde zaman uyumsuz koda çağrı yapan temsilcinin kendisinin, ondan ayrılmamız durumunda HistoryAndRoaming sayfasının etkin kalmasını sağlamadığı anlamına gelir.
İkincisi de, sonunda başlatıp bıraktığımız CheckHistoryAndRoaming coroutine’ine ulaştığımızda, yaptığımız ilk şey, HistoryAndRoaming sayfasının en azından coroutine tamamen tamamlanana kadar varlığını sürdürmesini garanti altına almak için this öğesine güçlü bir başvuru almaktır. Az önce açıklanan her iki özellik hakkında daha fazla bilgi için bkz. C++/WinRT'de güçlü ve zayıf başvurular.
CheckHistoryAndRoaming'i taşıma sırasında başka bir ilgi çekici nokta bulduk. Kullanıcı arabirimini güncelleştirmek için kod içerir; bu nedenle bunu ana kullanıcı arabirimi iş parçacığında yaptığımızdan emin olmamız gerekir. Başlangıçta bir olay işleyicisine çağrıda bulunan iş parçacığı, ana kullanıcı arabirimi iş parçacığıdır. Ancak genellikle eşzamansız bir yöntem, rastgele bir iş parçacığında çalışabilir ve/veya çalışmasına devam edebilir. C# dilinde çözüm, işi UI iş parçacığına yönlendirmektir. C++/WinRT'de winrt::resume_foreground işlevini işaretçinin thisDispatcherQueue işleviyle birlikte kullanarak coroutine'yi askıya alabilir ve ana kullanıcı arabirimi iş parçacığında hemen devam edebiliriz.
İlgili ifade şeklindedir co_await winrt::resume_foreground(DispatcherQueue());. Daha kısa sürüm, C++/WinRT tarafından sağlanan bir dönüştürme işlecinin izniyle elde edilir.
Bu XAML sayfasının geri kalanının uyarlanması, tartışmaya değer yeni bir şey ortaya koymaz. Sahte ClickHandler işlevini silmeyi ve XAML işaretlemesi üzerinden kopyalamayı unutmayın.
OtherScenarios
OtherScenarios.xaml ve OtherScenarios.xaml.cs öğelerini, daha önce ele aldığımız teknikleri kullanarak taşıyabilirsiniz.
Sonuç
Umarım bu kılavuz, artık kendi C# uygulamalarınızı C++/WinRT'ye taşımanıza başlayabilmeniz için size yeterli bilgi ve teknikleri sağlamıştır. Yenileyici yoluyla, Pano örneğindeki kaynak kodun önceki (C#) ve sonra (C++/WinRT) sürümlerine geri başvurmaya devam edebilir ve yazışmaları görmek için bunları yan yana karşılaştırabilirsiniz.
İlgili konular
Windows developer