2015-05-26 4 views
4

Я подумываю о способах продвижения интерполяции строк C# 6 в XAML, например, вместо использования преобразователей значений в некоторых простых сценариях, таких как замена нуля пустой строкой при привязке к номерам.Строковая интерполяция в XAML

От его design discussions:

интерполированной строки представляет собой способ построить значение типа String, (или IFormattable), написав текст строки вместе с выражений, которые будут заполнять «дыру» в строке. Компилятор создает строку форматирования и последовательность значений заполнения из интерполированной строки .

Однако, как я подозревал, что кажется, что они не могут быть использованы из XAML, так как он использует другой компилятор для генерации BAML, и я не нахожу никаких следов строк в генерируемых .g.i.cs файлов.

  • Являются ли строковые интерполяции не поддерживаемыми в XAML?
  • Какие обходные пути могут быть? Может быть, используя расширения разметки для динамической компиляции интерполяций строк?
+0

Та же проблема возникает с Razor в ASP.NET MVC. Вероятно, это ошибка [https://github.com/aspnet/Razor/issues/401) в двух синтаксических анализаторах –

ответ

2

Это сложно поддерживать из-за того, что Binding работает в WPF. Строковые интерполяции в коде C# могут быть скомпилированы непосредственно на звонки string.Format и в основном просто обеспечивают удобный синтаксический сахар. Однако для выполнения этой работы с привязкой необходимо выполнить некоторую работу во время выполнения.

Я собрал простой класс, который может это сделать, хотя он имеет несколько ограничений. В частности, он не поддерживает передачу всех параметров привязки, и неудобно вводить XAML, поскольку вам нужно избегать фигурных скобок (возможно, стоит использовать другой символ?). Он должен обрабатывать привязки с несколькими путями и произвольно сложный формат однако, если они правильно экранированы для использования в XAML.

В связи с одним конкретным моментом в вашем вопросе это не позволяет вставлять произвольные выражения, как вы можете делать в интерполированных строках. Если вы захотите это сделать, вам придется немного поучаствовать и сделать что-то вроде компиляции кода «на лету» с точки зрения связанных значений. Скорее всего, вам нужно будет испустить вызов функции, который принимает значения параметров, затем вызовите это как делегат из преобразователя значений и попросите его выполнить встроенные выражения. Это должно быть возможно, но, вероятно, непросто реализовать.

Использование выглядит следующим образом:

<TextBlock Text="{local:InterpolatedBinding '\{TestString\}: \{TestDouble:0.0\}'}"/> 

А вот расширение разметки, который делает работу:

public sealed class InterpolatedBindingExtension : MarkupExtension 
{ 
    private static readonly Regex ExpressionRegex = new Regex(@"\{([^\{]+?)(?::(.+?))??\}", RegexOptions.Compiled); 

    public InterpolatedBindingExtension() 
    { 
    } 

    public InterpolatedBindingExtension(string expression) 
    { 
     Expression = expression; 
    } 

    public string Expression { get; set; } 

    public override object ProvideValue(IServiceProvider serviceProvider) 
    { 
     //Parse out arguments captured in curly braces 
     //If none found, just return the raw string 
     var matches = ExpressionRegex.Matches(Expression); 
     if (matches.Count == 0) 
      return Expression; 

     if (matches.Count == 1) 
     { 
      var formatBuilder = new StringBuilder(); 

      //If there is only one bound target, can use a simple binding 
      var varGroup = matches[0].Groups[1]; 
      var binding = new Binding(); 
      binding.Path = new PropertyPath(varGroup.Value); 
      binding.Mode = BindingMode.OneWay; 

      formatBuilder.Append(Expression.Substring(0, varGroup.Index)); 
      formatBuilder.Append('0'); 
      formatBuilder.Append(Expression.Substring(varGroup.Index + varGroup.Length)); 

      binding.Converter = new FormatStringConverter(formatBuilder.ToString()); 
      return binding.ProvideValue(serviceProvider); 
     } 
     else 
     { 
      //Multiple bound targets, so we need a multi-binding 
      var multiBinding = new MultiBinding(); 
      var formatBuilder = new StringBuilder(); 
      int lastExpressionIndex = 0; 
      for (int i=0; i<matches.Count; i++) 
      { 
       var varGroup = matches[i].Groups[1]; 
       var binding = new Binding(); 
       binding.Path = new PropertyPath(varGroup.Value); 
       binding.Mode = BindingMode.OneWay; 

       formatBuilder.Append(Expression.Substring(lastExpressionIndex, varGroup.Index - lastExpressionIndex)); 
       formatBuilder.Append(i.ToString()); 
       lastExpressionIndex = varGroup.Index + varGroup.Length; 

       multiBinding.Bindings.Add(binding); 
      } 
      formatBuilder.Append(Expression.Substring(lastExpressionIndex)); 

      multiBinding.Converter = new FormatStringConverter(formatBuilder.ToString()); 
      return multiBinding.ProvideValue(serviceProvider); 
     } 
    } 

    private sealed class FormatStringConverter : IMultiValueConverter, IValueConverter 
    { 
     private readonly string _formatString; 

     public FormatStringConverter(string formatString) 
     { 
      _formatString = formatString; 
     } 

     public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 
     { 
      if (targetType != typeof(string)) 
       return null; 

      return string.Format(_formatString, values); 
     } 

     public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 
     { 
      return null; 
     } 

     public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
     { 
      if (targetType != typeof(string)) 
       return null; 

      return string.Format(_formatString, value); 
     } 

     public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
     { 
      return null; 
     } 
    } 
} 

я сделал очень ограниченное тестирование, поэтому я рекомендую более тщательное тестирование и закаливание перед использованием этого в производстве. Должно быть, надеюсь, что хорошей отправной точкой для кого-то сделать что-то полезное.

1

Это очень похоже на атрибут StringFormat, введенный в .Net 3.5. Как вы цитируете, «написание текста строки вместе с выражениями, которые будут заполнять в„дырах“в строке», это может быть сделано в рамках XAML связывание, как это:

<TextBlock Text="{Binding Amount, StringFormat=Total: {0:C}}" /> 

Так как вы можете использовать любого из пользовательские строковые форматы, здесь есть много энергии под капотом. Или вы спрашиваете что-то еще?

+2

OP спрашивает что-то еще. Строчная интерполяция добавляется в C# 6 и работает только с Visual Studio 2015 RC. Теперь вы можете написать '$ '{myDateVar: yyyy-MM-dd} Boo" 'вместо эквивалентного' String.Format ("{0: yyyy-MM-dd} Boo", myDateVar) ' –

Смежные вопросы