[WPF]数据绑定时为何会出现StringFormat失效

打印 上一主题 下一主题

主题 525|帖子 525|积分 1575

在数据绑定过程中,我们经常会使用StringFormat对要表现的数据举行格式化,以便获得更为直观的展示效果,但在某些情况下格式化操纵并未生效,比方 Button的 Content属性以及ToolTip属性绑定数据举行StringFormat时是无效的。首先回首一下StringFormat的根本用法。
StringFormat的用法

StringFormat是 BindingBase的属性,指定如果绑定值表现为字符串,应怎样设置该绑定的格式。因此,BindingBase 的三个子类:Binding、MultiBinding、PriorityBinding都可以对绑定数据举行格式化。
Binding

Binding 是最常用的绑定方式,使用StringFormat依照.Net格式字符串标准即可。比方:
  1. [/code]或者
  2. [code]
复制代码
其中{0}表示第一个数值,如果 StringFormat 属性的值是以花括号开头,前边需要有一对花括号 {} 举行转义,也就是第一个例子中的 {}{0:C},否则不需要,如第二个示例一样。
如果设置 Converter 和 StringFormat属性,则首先将转换器应用于数据值,然后StringFormat 应用该值。
MultiBinding

Binding 绑定时,格式化只能指定一个参数,MultiBinding 绑定时则可指定多个参数。比方:
  1. <TextBlock>
  2.     <TextBlock.Text>
  3.         <MultiBinding StringFormat="{}{0} {1}">
  4.             <Binding Path="FirstName" ElementName="self"/>
  5.             <Binding Path="LastName" ElementName="self"/>
  6.         </MultiBinding>
  7.     </TextBlock.Text>
  8. </TextBlock>
复制代码
这个例子中 MultiBinding 是由多个子 Binding 组成,StringFormat 仅在设置 MultiBinding 时适用,子 Binding 中虽然也可以设置 StringFormat,但是会被忽略。
PriorityBinding

相比于前两种绑定,PriorityBinding 使用的频率没那么高,它的主要作用是按照一定优先级顺序设置绑定列表, 如果最高优先级绑定在处理时成功返回值,则无需处理列表中的其他绑定。 如果计算优先级最高的绑定需要很长时间,那么将会使用成功返回值的次高优先级,直到优先级较高的绑定成功返回值。PriorityBinding 和其包罗的绑定列表中的子 Binding 也都可以设置 StringFormat 属性。比方:
  1. <TextBlock>
  2.     <TextBlock.Text>
  3.         <MultiBinding StringFormat="{}{0} {1}">
  4.             <Binding Path="FirstName" ElementName="self"/>
  5.             <Binding Path="LastName" ElementName="self"/>
  6.         </MultiBinding>
  7.     </TextBlock.Text>
  8. </TextBlock>            
复制代码
与 MultiBinding 差异的是,PriorityBinding 的子 Binding中的  StringFormat是会生效的,其规则是优先使用子 Binding 设置的格式,其次才使用PriorityBinding 设置的格式。
Content属性格式化失效的原因

Button 的 Content 属性可以用字符串赋值并表现在按钮上,但是使用 StringFormat 格式化并不会生效。原本我以为是涉及到类型转换器,在类型转换过程中处理掉了,但这只是推测,通过源码发现并不是这样的。在 BindingExpressionBase 中有这样一段代码:
  1. internal virtual bool AttachOverride(DependencyObject target, DependencyProperty dp)
  2. {
  3.         _targetElement = new WeakReference(target);
  4.         _targetProperty = dp;
  5.         DataBindEngine currentDataBindEngine = DataBindEngine.CurrentDataBindEngine;
  6.         if (currentDataBindEngine == null || currentDataBindEngine.IsShutDown)
  7.         {
  8.                 return false;
  9.         }
  10.         _engine = currentDataBindEngine;
  11.         DetermineEffectiveStringFormat();
  12.         DetermineEffectiveTargetNullValue();
  13.         DetermineEffectiveUpdateBehavior();
  14.         DetermineEffectiveValidatesOnNotifyDataErrors();
  15.         if (dp == TextBox.TextProperty && IsReflective && !IsInBindingExpressionCollection && target is TextBoxBase textBoxBase)
  16.         {
  17.                 textBoxBase.PreviewTextInput += OnPreviewTextInput;
  18.         }
  19.         if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.Attach))
  20.         {
  21.                 TraceData.TraceAndNotifyWithNoParameters(TraceEventType.Warning, TraceData.AttachExpression(TraceData.Identify(this), target.GetType().FullName, dp.Name, AvTrace.GetHashCodeHelper(target)), this);
  22.         }
  23.         return true;
  24. }
复制代码
其中第11行调用了一个名为 DetermineEffectiveStringFormat 的方法,顾名思义就是检测有效的 StringFormat。接下来看看里边的逻辑:
  1. internal void DetermineEffectiveStringFormat()
  2. {
  3.         Type type = TargetProperty.PropertyType;
  4.         if (type != typeof(string))
  5.         {
  6.                 return;
  7.         }
  8.         string stringFormat = ParentBindingBase.StringFormat;
  9.         for (BindingExpressionBase parentBindingExpressionBase = ParentBindingExpressionBase; parentBindingExpressionBase != null; parentBindingExpressionBase = parentBindingExpressionBase.ParentBindingExpressionBase)
  10.         {
  11.                 if (parentBindingExpressionBase is MultiBindingExpression)
  12.                 {
  13.                         type = typeof(object);
  14.                         break;
  15.                 }
  16.                 if (stringFormat == null && parentBindingExpressionBase is PriorityBindingExpression)
  17.                 {
  18.                         stringFormat = parentBindingExpressionBase.ParentBindingBase.StringFormat;
  19.                 }
  20.         }
  21.         if (type == typeof(string) && !string.IsNullOrEmpty(stringFormat))
  22.         {
  23.                 SetValue(Feature.EffectiveStringFormat, Helper.GetEffectiveStringFormat(stringFormat), null);
  24.         }
  25. }
复制代码
这段代码的作用就是检测有效的 StringFormat,并通过 SetValue 方法保存起来,从第4~7行代码可以看到,一开始就会检测目的属性的类型是不是 String 类型,不是的话直接返回,绑定表达式中的 StringFormat 也就不会保存了。在后续的 BindingExpression 类计算绑定表达式值时获取到 StringFormat 为 null,也就不会举行格式化了。

Button 的 Content 属性虽然可以用字符串赋值,但它其实的 Object 类型。因此,在检测有效的 StringFormat 表达式时直接过滤了。ToolTip也同样是 Object 类型。

办理方法

对于 Content 这种 Object 类型的属性绑定字符串并且需要格式化时,可以采用以下三种方式办理:

  • 最通用的方法就是自界说 ValueConverter,在 ValueConverter 中对字符串举行格式化;
  • 绑定到其他可举行 StringFormat 的属性上,比如 TextBlock 的 Text 属性举行格式化,ToolTip 绑定到 Text 上;
  • 既然是 Object 类型,那也可把  TextBlock 作为 Content的值。
  1. <Button Width="120" Height="30">
  2.     <Button.Content>
  3.         <TextBlock Text="{Binding TestString,ElementName=self,StringFormat=test:{0}}"/>
  4.     </Button.Content>
  5. </Button>
复制代码
小结

数据绑定时出现StringFormat失效的主要分为两种情况。一是没有依照绑定时StringFormat使用的束缚,二是绑定的目的属性不是 String 类型。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

玛卡巴卡的卡巴卡玛

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表