Передача ContentStringFormat в Datatemplate - PullRequest
0 голосов
/ 30 августа 2018

Моя цель - передать отформатированные строки в таблицу данных:

<ContentControl ContentTemplate="{StaticResource WorkingLabelTemplate}"    Content="123"
                ContentStringFormat="Number is {0}" Grid.Row="0"/>

<ContentControl ContentTemplate="{StaticResource NotWorkingLabelTemplate}" Content="123"
                ContentStringFormat="Number is {0}" Grid.Row="1"/>

Первый подход:

<DataTemplate x:Key="WorkingLabelTemplate">
    <Label Content="{Binding}"
            ContentStringFormat="{Binding RelativeSource={RelativeSource TemplatedParent}, 
            Path=ContentStringFormat, Converter={StaticResource TestConverter}}"/>
</DataTemplate>

Второй подход:

<DataTemplate x:Key="NotWorkingLabelTemplate">
    <Label Content="{Binding}" 
            ContentStringFormat="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContentControl},
            Path=ContentStringFormat, Converter={StaticResource TestConverter}}"/>
</DataTemplate>

Для обоих подходов TestConverter говорит, что привязка работает правильно:

TestConverter: 'Number is {0}' типа 'System.String' в 'System.String', параметр '' TestConverter: 'Number is {0}' типа 'System.String' в 'System.String', параметр ''

Но второй подход не работает: Скриншот

Вопрос: Почему второй подход не работает, а результат привязки тот же?

1 Ответ

0 голосов
/ 31 августа 2018

TL; DR

Я использовал ваш проект и обнаружил, что то же самое происходит во всех платформах .Net, установленных на моей машине. Итак, я выкопал еще один ковш, чтобы выяснить, что происходит, и в заключение пришел к выводу, что это просто проблема синхронизации , ContentStringFormat свойство для вашего рабочего шаблона оценивается немного раньше (до того, как метка была ), чем для вашего неработающего шаблона.

Решение

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

public MainWindow()
{
    this.DataContext = this;
    InitializeComponent();
}

Кому:

public MainWindow()
{
    InitializeComponent();
    this.DataContext = this;
}

Продолжайте читать, если хотите прочитать что-то действительно интересное!

Почему я считаю, что это проблема времени?

Я установил свойства диагностики для обоих шаблонов diag:PresentationTraceSources.TraceLevel=High и обнаружил следующий журнал.

Рабочий шаблон enter image description here

Не работает шаблон enter image description here

Если вы внимательно посмотрите на оба изображения, вы увидите, что между временем, затраченным на оценку свойства ContentStringFormat, есть разница.

Еще одно доказательство

Я внес еще одно изменение в ваш проект, который подтвердил мою веру в правду. Если вы запустите код ниже, он похож на ваш проект, но с двумя дополнительными кнопками; один для изменения данных и другой для изменения формата строки. Когда вы запускаете программу и изменяете формат строки, содержимое не перерисовывается с новым форматом строки , но когда вы сами изменяете данные, он переоценивает формат строки и повторно отображает элемент управления!

MainWindow.xaml

<Window x:Class="StringFormatTestProject.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:StringFormatTestProject"
        mc:Ignorable="d"
        xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <local:TestConverter x:Key="TestConverter"/>

        <DataTemplate x:Key="WorkingLabelTemplate">
            <Label Content="{Binding diag:PresentationTraceSources.TraceLevel=High}"
                   ContentStringFormat="{Binding RelativeSource={RelativeSource TemplatedParent},
                                                 Path=ContentStringFormat, Converter={StaticResource TestConverter}, diag:PresentationTraceSources.TraceLevel=High}"/>
        </DataTemplate>

        <DataTemplate x:Key="NotWorkingLabelTemplate">
            <Label Content="{Binding}"
                   ContentStringFormat="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContentControl},
                                                 Path=ContentStringFormat, Converter={StaticResource TestConverter}}">
            </Label>
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>


        <ContentControl ContentTemplate="{StaticResource NotWorkingLabelTemplate}" Content="{Binding SomeDatatInVM}"
                        ContentStringFormat="{Binding FormatStringInVM}" Grid.Row="0"/>

        <ContentControl ContentTemplate="{StaticResource WorkingLabelTemplate}"    Content="{Binding SomeDatatInVM}"
                        ContentStringFormat="{Binding FormatStringInVM}" Grid.Row="1"/>

        <Grid Grid.Row="2">
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition Width="*" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>

            <Button Click="Button_Click" Grid.Column="0">Change Data</Button>
            <Button Click="Button_Click_1" Grid.Column="2">Change Format String</Button>
        </Grid>
    </Grid>
</Window>

MainWindow.xaml.cs

public partial class MainWindow : Window, INotifyPropertyChanged
{

    public event PropertyChangedEventHandler PropertyChanged;

    private string formatStringInVM = "Peer: {0}";
    public string FormatStringInVM
    {
        get { return formatStringInVM; }
        set
        {
            formatStringInVM = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(FormatStringInVM)));
        }
    }

    private int someDatatInVM = 123;
    public int SomeDatatInVM
    {
        get { return someDatatInVM; }
        set
        {
            someDatatInVM = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SomeDatatInVM)));
        }
    }

    public MainWindow()
    {    
        this.DataContext = this;

        InitializeComponent();
    }

    private void Button_Click(Object sender, RoutedEventArgs e)
    {
        SomeDatatInVM++;
    }

    private static int i = 1;
    private void Button_Click_1(Object sender, RoutedEventArgs e)
    {
        FormatStringInVM = "Peer-" + i.ToString() + ": {0}";
        i++;
    }
}
...