我的目标是将格式化的字符串传递给datatemplate:
<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:'System.String'的'Number is {0}'到'System.String',参数''TestConverter:'System.String'的数字是{0}'到'System.String',参数“”
但第二种方法不起作用:screenshot
问题:为什么第二种方法不起作用,而绑定结果是一样的?
TL; DR
我使用了你的项目,发现我机器上安装的所有.Net框架都发生了同样的事情。所以我挖了更多的北斗来找出发生了什么,我得出的结论是,这只是一个时间问题,你的工作模板的ContentStringFormat
属性正在被评估比较早(在标签被绘制之前),而不是你的工作模板。
解
只是为了解决您的问题,请更改构造函数代码:
public MainWindow()
{
this.DataContext = this;
InitializeComponent();
}
至:
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
如果你想阅读一些真正有趣的东西,请继续阅读!
为什么我认为这是一个时间问题?
我在两个模板diag:PresentationTraceSources.TraceLevel=High
上设置诊断属性,并在日志后面找到。
如果你仔细观察两张图片,你会发现评估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++;
}
}