zoukankan      html  css  js  c++  java
  • 【UWP】通过 MarkupExtension 实现 ValueConveter 的依赖注入

    最近是真的比较闲,花了点时间算是把我自己的微博库的 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>

    运行起来:

    Snipaste_2020-04-03_10-31-24

    改个时间再跑起来:

    Snipaste_2020-04-03_23-36-27

    还行。


    理论上可以修改 MarkupExtensionReturnTypeAttribute 的 ReturnType 为 typeof(object) 然后从 IoC 容器获取任意已经注册了的东西就是了。但写完 blog 发现好像满满的伪需求的样子。ε=ε=ε=┏(゜ロ゜;)┛

  • 相关阅读:
    arguments.callee
    vue的生命周期
    Vue中的v-cloak用法
    控制input只能输入数字和两位小数
    css3新单位vw、vh的使用详解
    关于图片的Base64编码
    Logic and Fault simulation
    scan design flow(二)
    scan design flow(一)
    异构计算
  • 原文地址:https://www.cnblogs.com/h82258652/p/12625081.html
Copyright © 2011-2022 走看看