最近是真的比較閑,花了點時間算是把我自己的微博庫的 nuget 包的坑填上了(https://github.com/h82258652/HN.Social.Weibo 歡迎大佬來 Star)。dino 大佬也一直忽悠我弄動畫,可惜我沒啥藝術細胞而且 Composition API 也不太熟悉,就只能 ...
最近是真的比較閑,花了點時間算是把我自己的微博庫的 nuget 包的坑填上了(https://github.com/h82258652/HN.Social.Weibo 歡迎大佬來 Star)。dino 大佬也一直忽悠我弄動畫,可惜我沒啥藝術細胞而且 Composition API 也不太熟悉,就只能逃了(哈哈哈 )。閑著無事就刷刷 Github,看到 wpf repo 的一個 issue(https://github.com/dotnet/wpf/issues/499),確實目前的 XAML 跟控制反轉這塊幾乎都沒啥結合。控制項層面由於要求無參構造函數,所以目前來看難以實現了。但 ValueConverter 這玩意,想了下,好像可以耶,於是做了下實驗,成功並且寫下了這篇 blog。
UWP 的 MarkupExtension 是在 16299 版本引入的,所以我們的項目必須要 target 16299 或以上。
以一般 MVVM 模式為例,創建 ViewModelLocator.cs,這裡 IoC 容器我就使用最常用的 Autofac 好了,引用 Autofac.Extras.CommonServiceLocator 包。
public class ViewModelLocator { static ViewModelLocator() { var autofacServiceLocator = new AutofacServiceLocator(CreateAutofacContainer()); ServiceLocator.SetLocatorProvider(() => autofacServiceLocator); } private static IContainer CreateAutofacContainer() { var containerBuilder = new ContainerBuilder(); // TODO Register services return containerBuilder.Build(); } }
並修改 App.xaml
<Application x:Class="ConverterIocDemo.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:viewModels="using:ConverterIocDemo.ViewModels"> <Application.Resources> <ResourceDictionary> <viewModels:ViewModelLocator x:Key="Locator" /> </ResourceDictionary> </Application.Resources> </Application>
接下來添加一些測試代碼吧。
namespace ConverterIocDemo.Models { public class Person { public string Name { get; set; } public int Age { get; set; } } }
using ConverterIocDemo.Models; namespace ConverterIocDemo.Services { public interface IPersonService { string GetHello(Person person); } }
using System; using ConverterIocDemo.Models; namespace ConverterIocDemo.Services { public class PersonService : IPersonService { public string GetHello(Person person) { if (person == null) { throw new ArgumentNullException(nameof(person)); } var now = DateTime.Now; if (now.Hour >= 9 && now.Hour <= 21 && now.DayOfWeek != DayOfWeek.Sunday) { return $"大家好,我叫 {person.Name},今年 {person.Age} 歲"; } else { return "996 大法好(mmp)"; } } } }
using ConverterIocDemo.Models; namespace ConverterIocDemo.ViewModels { public class MainViewModel { public MainViewModel() { Person = new Person { Name = "justin liu", Age = 18 }; } public Person Person { get; } } }
using ConverterIocDemo.Services; using System; using Windows.UI.Xaml.Data; using ConverterIocDemo.Models; namespace ConverterIocDemo.Converters { public class PersonSayHelloConverter : IValueConverter { private readonly IPersonService _personService; public PersonSayHelloConverter(IPersonService personService) { _personService = personService; } public object Convert(object value, Type targetType, object parameter, string language) { return _personService.GetHello((Person)value); } public object ConvertBack(object value, Type targetType, object parameter, string language) { throw new NotImplementedException(); } } }
修改一下 ViewModelLocator,把這堆玩意註冊上去。
using Autofac; using Autofac.Extras.CommonServiceLocator; using CommonServiceLocator; using ConverterIocDemo.Converters; using ConverterIocDemo.Services; namespace ConverterIocDemo.ViewModels { public class ViewModelLocator { static ViewModelLocator() { var autofacServiceLocator = new AutofacServiceLocator(CreateAutofacContainer()); ServiceLocator.SetLocatorProvider(() => autofacServiceLocator); } public MainViewModel Main => ServiceLocator.Current.GetInstance<MainViewModel>(); private static IContainer CreateAutofacContainer() { var containerBuilder = new ContainerBuilder(); containerBuilder.RegisterType<PersonService>().As<IPersonService>(); containerBuilder.RegisterType<MainViewModel>(); containerBuilder.RegisterType<PersonSayHelloConverter>().SingleInstance(); return containerBuilder.Build(); } } }
接下來就是本文關鍵,通過 MarkupExtension 消費這個 PersonSayHelloConveter 了。這裡我就叫 ConverterProviderExtension。
using CommonServiceLocator; using System; using Windows.UI.Xaml.Data; using Windows.UI.Xaml.Markup; namespace ConverterIocDemo.Converters { [MarkupExtensionReturnType(ReturnType = typeof(IValueConverter))] public class ConverterProviderExtension : MarkupExtension { public Type ConverterType { get; set; } protected override object ProvideValue() { if (ConverterType == null) { throw new ArgumentException("轉換器類型沒有設置"); } return ServiceLocator.Current.GetInstance(ConverterType); } } }
接下來修改 MainPage 看看效果了
<Page x:Class="ConverterIocDemo.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:converters="using:ConverterIocDemo.Converters" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="using:ConverterIocDemo" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" DataContext="{Binding Source={StaticResource Locator}, Path=Main}" mc:Ignorable="d"> <Grid> <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding Path=Person, Converter={converters:ConverterProvider ConverterType=converters:PersonSayHelloConverter}}" /> </Grid> </Page>
運行起來:
改個時間再跑起來:
還行。
理論上可以修改 MarkupExtensionReturnTypeAttribute 的 ReturnType 為 typeof(object) 然後從 IoC 容器獲取任意已經註冊了的東西就是了。但寫完 blog 發現好像滿滿的偽需求的樣子。ε=ε=ε=┏(゜ロ゜;)┛