Dưới đây là một dự án đầy đủ và làm việc có thể giải quyết vấn đề của bạn. Lúc đầu, tôi sẽ đề nghị sử dụng thuộc tính [XamlSetMarkupExtension]
trên lớp Country
của bạn, nhưng thực sự tất cả những gì bạn cần là độ phân giải tên chuyển tiếp của XamlSchemaContext
.
Mặc dù tài liệu cho tính năng đó là rất mỏng trên mặt đất, bạn có thể trên thực tế nói XAML Dịch vụ hoãn yếu tố mục tiêu của bạn, và các mã sau đây cho thấy như thế nào. Lưu ý rằng tất cả các tên ngôn ngữ của bạn được giải quyết đúng cách mặc dù các phần từ ví dụ của bạn bị đảo ngược.
Về cơ bản, nếu bạn cần một tên không thể giải quyết được, bạn yêu cầu trì hoãn bằng cách trả lại mã thông báo sửa lỗi. Đúng vậy, như Dmitry nói nó mờ đục với chúng tôi, nhưng điều đó không quan trọng. Khi bạn gọi GetFixupToken(...)
, bạn sẽ chỉ định danh sách tên mà bạn cần. Tiện ích mở rộng đánh dấu của bạn— ProvideValue
, tức là — sẽ được gọi lại sau khi các tên đó đã có sẵn. Tại thời điểm đó, về cơ bản nó là một việc làm.
Không hiển thị ở đây là bạn cũng nên kiểm tra thuộc tính Boolean
IsFixupTokenAvailable
trên số IXamlNameResolver
. Nếu tên thật sự được tìm thấy sau, thì điều này sẽ trả về true
. Nếu giá trị là false
và bạn vẫn có tên chưa được giải quyết, thì bạn nên cố gắng không hoạt động, có lẽ vì các tên được đưa ra trong Xaml cuối cùng không thể được giải quyết.
Một số người có thể tò mò muốn lưu ý rằng dự án này là không ứng dụng WPF, tức là nó không tham chiếu đến thư viện WPF; tham chiếu duy nhất bạn phải thêm vào độc lập này ConsoleApplication là System.Xaml
. Điều này đúng ngay cả khi có tuyên bố using
cho System.Windows.Markup
(một tạo tác lịch sử). Chính trong .NET 4.0, việc hỗ trợ XAML Services đã được chuyển từ WPF (và các nơi khác) và vào các thư viện BCL cốt lõi.
IMHO, thay đổi này được thực hiện Dịch vụ XAML tính năng BCL lớn nhất mà không ai được nghe đến. Không có nền tảng tốt hơn để phát triển một ứng dụng cấp hệ thống lớn có khả năng cấu hình lại triệt để như là một yêu cầu chính. Một ví dụ về một 'ứng dụng' như vậy là WPF.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Windows.Markup;
using System.Xaml;
namespace test
{
public class Language { }
public class Country { public IEnumerable<Language> Languages { get; set; } }
public class LanguageSelector : MarkupExtension
{
public LanguageSelector(String items) { this.items = items; }
String items;
public override Object ProvideValue(IServiceProvider ctx)
{
var xnr = ctx.GetService(typeof(IXamlNameResolver)) as IXamlNameResolver;
var tmp = items.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)
.Select(s_lang => new
{
s_lang,
lang = xnr.Resolve(s_lang) as Language
});
var err = tmp.Where(a => a.lang == null).Select(a => a.s_lang);
return err.Any() ?
xnr.GetFixupToken(err) :
tmp.Select(a => a.lang).ToList();
}
};
public class myClass
{
Collection<Language> _l = new Collection<Language>();
public Collection<Language> Languages { get { return _l; } }
Collection<Country> _c = new Collection<Country>();
public Collection<Country> Countries { get { return _c; } }
// you must set the name of your assembly here ---v
const string s_xaml = @"
<myClass xmlns=""clr-namespace:test;assembly=ConsoleApplication2""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
<myClass.Countries>
<Country x:Name=""UK"" Languages=""{LanguageSelector 'English'}"" />
<Country x:Name=""France"" Languages=""{LanguageSelector 'French'}"" />
<Country x:Name=""Italy"" Languages=""{LanguageSelector 'Italian'}"" />
<Country x:Name=""Switzerland"" Languages=""{LanguageSelector 'English, French, Italian'}"" />
</myClass.Countries>
<myClass.Languages>
<Language x:Name=""English"" />
<Language x:Name=""French"" />
<Language x:Name=""Italian"" />
</myClass.Languages>
</myClass>
";
static void Main(string[] args)
{
var xxr = new XamlXmlReader(new StringReader(s_xaml));
var xow = new XamlObjectWriter(new XamlSchemaContext());
XamlServices.Transform(xxr, xow);
myClass mc = (myClass)xow.Result; /// works with forward references in Xaml
}
};
}
[sửa ...]
Như Tôi chỉ học Dịch vụ XAML, tôi có thể có được overthinking nó. Dưới đây là giải pháp đơn giản cho phép bạn thiết lập bất kỳ tham chiếu nào bạn mong muốn - hoàn toàn bằng XAML - chỉ sử dụng các tiện ích đánh dấu được tích hợp sẵn x:Array
và x:Reference
. Bằng cách nào đó tôi đã không nhận ra rằng không chỉ có thể x:Reference
cư một thuộc tính (như nó thường thấy: {x:Reference some_name}
), nhưng nó cũng có thể đứng như một thẻ XAML của riêng mình (<Reference Name="some_name" />
). Trong cả hai trường hợp, nó hoạt động như một tham chiếu proxy tới một đối tượng ở đâu đó trong tài liệu. Điều này cho phép bạn điền một số x:Array
với các tham chiếu đến các đối tượng XAML khác và sau đó chỉ cần đặt mảng làm giá trị cho thuộc tính của bạn. Trình phân tích cú pháp XAML tự động giải quyết các tham chiếu chuyển tiếp theo yêu cầu.
<myClass xmlns="clr-namespace:test;assembly=ConsoleApplication2"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<myClass.Countries>
<Country x:Name="UK">
<Country.Languages>
<x:Array Type="Language">
<x:Reference Name="English" />
</x:Array>
</Country.Languages>
</Country>
<Country x:Name="France">
<Country.Languages>
<x:Array Type="Language">
<x:Reference Name="French" />
</x:Array>
</Country.Languages>
</Country>
<Country x:Name="Italy">
<Country.Languages>
<x:Array Type="Language">
<x:Reference Name="Italian" />
</x:Array>
</Country.Languages>
</Country>
<Country x:Name="Switzerland">
<Country.Languages>
<x:Array Type="Language">
<x:Reference Name="English" />
<x:Reference Name="French" />
<x:Reference Name="Italian" />
</x:Array>
</Country.Languages>
</Country>
</myClass.Countries>
<myClass.Languages>
<Language x:Name="English" />
<Language x:Name="French" />
<Language x:Name="Italian" />
</myClass.Languages>
</myClass>
Để dùng thử, đây là ứng dụng giao diện điều khiển hoàn chỉnh ngay lập tức đối tượng myClass
từ tệp XAML trước đó. Như trước đây, hãy thêm tham chiếu đến System.Xaml.dll
và thay đổi dòng đầu tiên của XAML ở trên để khớp với tên lắp ráp của bạn.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Xaml;
namespace test
{
public class Language { }
public class Country { public IEnumerable<Language> Languages { get; set; } }
public class myClass
{
Collection<Language> _l = new Collection<Language>();
public Collection<Language> Languages { get { return _l; } }
Collection<Country> _c = new Collection<Country>();
public Collection<Country> Countries { get { return _c; } }
static void Main()
{
var xxr = new XamlXmlReader(new StreamReader("XMLFile1.xml"));
var xow = new XamlObjectWriter(new XamlSchemaContext());
XamlServices.Transform(xxr, xow);
myClass mc = (myClass)xow.Result;
}
};
}
+1 Cảm ơn bạn đã đăng bài này. Tôi thấy nó là một bài tập tuyệt vời cho đường cong học tập của tôi trên XAML Servces.Tôi hy vọng các đề xuất tôi đã đăng bên dưới có thể vẫn được sử dụng cho bạn một năm sau đó. –
@Glenn Slayden: cảm ơn bạn đã theo dõi về điều này. Bạn đã đề xuất hai giải pháp rất sáng tạo. Mặc dù mã của tôi hiện đã được triển khai và đang hoạt động bằng ý tưởng do DmitryG đề xuất, sẽ rất thú vị khi xem xét và điều chỉnh nó để sử dụng cách tiếp cận ngắn gọn hơn của bạn. –