【问题标题】:Using Value Converters in WPF without having to define them as resources first在 WPF 中使用值转换器,而不必先将它们定义为资源
【发布时间】:2011-01-19 05:49:40
【问题描述】:

是否可以使用价值转换器而不必事先将它们定义为资源?

我现在有

<Window.Resources>
    <local:TrivialFormatter x:Key="trivialFormatter" />
</Window.Resources>

和

<Button Width="{Binding Width, 
               ElementName=textBox1, 
               UpdateSourceTrigger=PropertyChanged, 
               Converter={StaticResource trivialFormatter}}">

难道我不必在 Window.Resources 中声明 trivialFormatter 资源,而可以直接从 Button 的宽度绑定中引用它吗?类似的东西

Converter = {local:TrivialFormatter}

谢谢

【问题讨论】:

    标签: c# wpf data-binding converters


    【解决方案1】:

    在单例类型IValueConverters 的情况下(例如,它们不需要来自当前绑定实例的任何状态)我使用静态转换器,即:

    Converter={x:Static SomeNamespace:SomeConverter.Instance}
    

    WPF 博士还发表了一篇很棒的文章,内容是使用标记扩展使其更清晰内联here。

    【讨论】:

    • 标记扩展的绝妙技巧!但是,在您的第一个示例中,您不需要制作 VC 的静态实例,例如{x:Static local:SomeConverter.Instance}?
    • 这是一个非常好的建议。在创建自己的值转换器时,您应该始终希望它们接受 ConverterParameter 而不是实例属性,以便您可以使用这样的单例实例。这比使用资源要容易得多。我通常称我为 {x:Static Local:InvertedBooleanConverter.Default} 之类的东西。
    • @itowlson -- 在实例上是的 -- 我已经编辑了我的回复。谢谢你的收获。
    【解决方案2】:

    从技术上讲,我相信你可以做到这一点,但 XAML 太可怕了,相比之下,它会使“大量琐碎资源”的方法看起来像一个简单明了的避风港:

    <Button Height="50">
      <Button.Width>
        <Binding Path="Width" ElementName="textBox1" UpdateSourceTrigger="PropertyChanged">
          <Binding.Converter>
            <local:TrivialFormatter />
          </Binding.Converter>
        </Binding>
      </Button.Width>
    </Button>
    

    我没有对此进行测试,因为即使是 阅读它也会让我的眼睛流泪......

    【讨论】:

    • 这也是一种非常有效的方法。但正如您所指出的,XAML 的冗长就像约柜一样,仅仅看着它就会吞噬您的灵魂。
    • 如果您想在转换器的特定实例上设置某些属性,这种方法可能很有用,例如&lt;local:TrivialFormatter RoundToTenth="True"/&gt;
    【解决方案3】:

    我肯定会研究 Micah 的建议,其中涉及使用转换器的静态单例实例。但要考虑的另一件事是,如果您使用像 MVVM 这样的分离表示模式,您通常可以通过在 ViewModel 中实现转换来避免对值转换器的要求。

    您可能想要这样做的原因有很多。

    首先,它更易于测试。您的单元测试可以确保从 ViewModel 出来的东西就是 UI 将显示的东西。您可以想象测试美元值必须遵循当前文化的货币格式、必须使用两位小数等要求。

    另一个很好的理由是值转换器中的异常不会被视为验证错误,这在 Silverlight 中可能是一个巨大的痛苦。即使您在绑定中将 ValidatesOnExceptions 设置为 true,如果您的值转换器引发异常,Silverlight 也会让它传播。但是,如果您使用 ViewModel 进行转换,则会将异常视为验证错误。

    缺点是您失去了通用值转换器的一些“可重用性”。

    【讨论】:

      【解决方案4】:

      我不知道如何按照您所说的方式执行此操作,但我只是将其作为示例进行了尝试,并且有效。在您的 App.xaml.cs 文件中,您可以创建一个使用反射来加载转换器的方法。

      private void Application_Startup(object sender, StartupEventArgs e)
      {
          LoadConverters();
      }
      
      private void LoadConverters()
      {
          foreach(var t in Assembly.GetExecutingAssembly().GetTypes())
          {
              if (t.GetInterfaces().Any(i => i.Name == "IValueConverter"))
              {
                  Resources.Add(t.Name, Activator.CreateInstance(t));
              }
          }
      }
      

      然后你可以像这样使用转换器,我猜是一半。

      <Button Width="{Binding Width, Converter={StaticResource TrivialFormatter}}" />
      

      您提出的方法的问题是 Xaml 解析器不知道何时以及创建多少个转换器实例。将其创建为资源确保只有一个实例。

      【讨论】:

      • 在类似的主题上,您可以创建一个返回其参数类型实例的标记扩展。然后你可以写Converter={local:InstanceOf {x:Type local:TrivialFormatter}}。但是,正如您所注意到的,这会导致每个绑定都单独实例化,这可能会造成浪费(尤其是在 ItemTemplate 中用于大型 ItemsControl 时)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-06
      • 2019-05-08
      • 2015-07-18
      • 2014-11-12
      • 2017-10-02
      相关资源
      最近更新 更多