我想在WPF应用程序中使用自定义控件,并且使用StringFormat绑定时出现了一些问题。在自定义控件上绑定StringFormat
问题很容易重现。首先,我们创建一个WPF应用程序并将其称为“TemplateBindingTest”。在那里,添加一个只有一个属性(Text)的自定义ViewModel,并将其分配给Window的DataContext。将Text属性设置为“Hello World!”。
现在,将自定义控件添加到解决方案。自定义控制很简单,因为它可以得到:
using System.Windows;
using System.Windows.Controls;
namespace TemplateBindingTest
{
public class CustomControl : Control
{
static CustomControl()
{
TextProperty = DependencyProperty.Register(
"Text",
typeof(object),
typeof(CustomControl),
new FrameworkPropertyMetadata(null));
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl), new FrameworkPropertyMetadata(typeof(CustomControl)));
}
public static DependencyProperty TextProperty;
public object Text
{
get
{
return this.GetValue(TextProperty);
}
set
{
SetValue(TextProperty, value);
}
}
}
}
当添加自定义的控制解决方案,Visual Studio中自动创建一个主题元素文件夹,用generic.xaml文件。让我们把的默认样式的控制有:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TemplateBindingTest">
<Style TargetType="{x:Type local:CustomControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomControl}">
<TextBlock Text="{TemplateBinding Text}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
现在,只需将控件添加到窗口,并设置Text属性的绑定,使用的StringFormat。还添加了一个简单的TextBlock,以确保绑定语法是正确的:
<Window x:Class="TemplateBindingTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:TemplateBindingTest="clr-namespace:TemplateBindingTest" Title="MainWindow" Height="350" Width="525">
<StackPanel>
<TemplateBindingTest:CustomControl Text="{Binding Path=Text, StringFormat=Test1: {0}}"/>
<TextBlock Text="{Binding Path=Text, StringFormat=Test2: {0}}" />
</StackPanel>
编译,运行aaaaand ...窗口上显示的文字是:
的Hello World !
Test2:Hello World!
在自定义控件上,StringFormat被完全忽略。在VS输出窗口中没有错误可见。这是怎么回事?
编辑:解决方法。
好吧,TemplateBinding是误导。我发现原因和肮脏的解决方法。
首先,请注意这个问题是按钮的内容属性是相同的:
<Button Content="{Binding Path=Text, StringFormat=Test3: {0}}" />
那么,这是怎么回事?让我们使用Reflector并潜入BindingBase类的StringFormat属性。 “分析”功能显示该属性由内部DetermineEffectiveStringFormat
方法使用。让我们看看这个方法:
internal void DetermineEffectiveStringFormat()
{
Type propertyType = this.TargetProperty.PropertyType;
if (propertyType == typeof(string))
{
// Do some checks then assign the _effectiveStringFormat field
}
}
问题就在这里。 effectiveStringFormat字段是解析Binding时使用的字段。只有在DependencyProperty类型为String
(我的Button为Content的内容属性Object
)时才会分配此字段。
为什么选择Object?因为我的自定义控件比我粘贴的控件复杂一些,并且像Button一样,我希望控件的用户能够提供子控件而不仅仅是文本。
那么,现在呢?即使在WPF核心控件中,我们也会遇到一种行为,所以我可以将其保留为“原样”。尽管如此,正如我的自定义控制用于仅在一个内部项目,我希望它是更容易从XAML中使用,我决定使用这个技巧:
using System.Windows;
using System.Windows.Controls;
namespace TemplateBindingTest
{
public class CustomControl : Control
{
static CustomControl()
{
TextProperty = DependencyProperty.Register(
"Text",
typeof(string),
typeof(CustomControl),
new FrameworkPropertyMetadata(null, Callback));
HeaderProperty = DependencyProperty.Register(
"Header",
typeof(object),
typeof(CustomControl),
new FrameworkPropertyMetadata(null));
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl), new FrameworkPropertyMetadata(typeof(CustomControl)));
}
static void Callback(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
obj.SetValue(HeaderProperty, e.NewValue);
}
public static DependencyProperty TextProperty;
public static DependencyProperty HeaderProperty;
public object Header
{
get
{
return this.GetValue(HeaderProperty);
}
set
{
SetValue(HeaderProperty, value);
}
}
public string Text
{
set
{
SetValue(TextProperty, value);
}
}
}
}
Header
是我TemplateBinding使用的属性。当为Text
提供值时,将应用StringFormat,因为该属性的类型为String
,那么使用回调将该值转发给Header
属性。它的工作原理,但它确实脏:
- 的
Header
和Text
财产不同步,如不更新Text
当我更新Header
。我选择不为Text
属性提供吸气剂以避免一些错误,但如果有人直接从DependencyProperty(GetValue(TextProperty)
)中读取值,它仍然可能发生。 - 如果某人向
Header
和Text
属性提供值,则会发生不可预知的行为,因为其中一个值将丢失。
总的来说,我不会推荐使用这个黑客。只有在您的项目是真的才能控制您的项目。如果控件在其他项目中使用的机会甚微,那就放弃StringFormat。
错误。在格式化字符串之前,运行时为所有对象调用'object.ToString()'。 – 2011-12-27 14:05:25
是的,但它在应用StringFormat之前检查依赖项属性的基础类型。如果我将dp的类型更改为String而不是Object,它会有效。 – 2011-12-27 14:11:06