WPF數(shù)據(jù)綁定時出現(xiàn)StringFormat失效的原因和解決方法
前言
在數(shù)據(jù)綁定過程中,我們經(jīng)常會使用StringFormat
對要顯示的數(shù)據(jù)進行格式化,以便獲得更為直觀的展示效果,但在某些情況下格式化操作并未生效,例如 Button
的 Content
屬性以及ToolTip
屬性綁定數(shù)據(jù)進行StringFormat
時是無效的。
首先回顧一下StringFormat
的基本用法。
StringFormat的用法
StringFormat
是 BindingBase
的屬性,指定如果綁定值顯示為字符串,應(yīng)如何設(shè)置該綁定的格式。
因此,BindingBase
的三個子類:Binding
、MultiBinding
、PriorityBinding
都可以對綁定數(shù)據(jù)進行格式化。
Binding
Binding
是最常用的綁定方式,使用StringFormat
遵循.Net格式字符串標準即可。
例如:
<TextBlock Text="{Binding Price,ElementName=self,StringFormat={}{0:C}}"/>
或者
<TextBlock Text="{Binding TestString,ElementName=self,StringFormat=test:{0}}"/>
其中{0}
表示第一個數(shù)值,如果 StringFormat
屬性的值是以花括號開頭,前邊需要有一對花括號 {}
進行轉(zhuǎn)義,也就是第一個例子中的 {}{0:C}
,否則不需要,如第二個示例一樣。
如果設(shè)置 Converter
和 StringFormat
屬性,則首先將轉(zhuǎn)換器應(yīng)用于數(shù)據(jù)值,然后StringFormat
應(yīng)用該值。
MultiBinding
Binding
綁定時,格式化只能指定一個參數(shù),MultiBinding
綁定時則可指定多個參數(shù)。
例如:
<TextBlock> <TextBlock.Text> <MultiBinding StringFormat="{}{0} {1}"> <Binding Path="FirstName" ElementName="self"/> <Binding Path="LastName" ElementName="self"/> </MultiBinding> </TextBlock.Text> </TextBlock>
這個例子中 MultiBinding
是由多個子 Binding
組成,StringFormat
僅在設(shè)置 MultiBinding
時適用,子 Binding
中雖然也可以設(shè)置 StringFormat
,但是會被忽略。
PriorityBinding
相比于前兩種綁定,PriorityBinding
使用的頻率沒那么高,它的主要作用是按照一定優(yōu)先級順序設(shè)置綁定列表, 如果最高優(yōu)先級綁定在處理時成功返回值,則無需處理列表中的其他綁定。
如果計算優(yōu)先級最高的綁定需要很長時間,那么將會使用成功返回值的次高優(yōu)先級,直到優(yōu)先級較高的綁定成功返回值。
PriorityBinding
和其包含的綁定列表中的子 Binding
也都可以設(shè)置 StringFormat
屬性。
例如:
<TextBlock Width="100" HorizontalAlignment="Center" Background="Honeydew"> <TextBlock.Text> <PriorityBinding FallbackValue="defaultvalue" StringFormat="haha:{0}"> <Binding IsAsync="True" Path="SlowestDP" StringFormat="hi:{0}"/> <Binding IsAsync="True" Path="SlowerDP" /> <Binding Path="FastDP" /> </PriorityBinding> </TextBlock.Text> </TextBlock>
與 MultiBinding
不同的是,PriorityBinding
的子 Binding
中的 StringFormat
是會生效的,其規(guī)則是優(yōu)先使用子 Binding
設(shè)置的格式,其次才使用PriorityBinding
設(shè)置的格式。
Content屬性格式化失效的原因
Button
的 Content
屬性可以用字符串賦值并顯示在按鈕上,但是使用 StringFormat
格式化并不會生效。
原本我以為是涉及到類型轉(zhuǎn)換器,在類型轉(zhuǎn)換過程中處理掉了,但這只是猜測,通過源碼發(fā)現(xiàn)并不是這樣的。
在 BindingExpressionBase
中有這樣一段代碼:
internal virtual bool AttachOverride(DependencyObject target, DependencyProperty dp) { _targetElement = new WeakReference(target); _targetProperty = dp; DataBindEngine currentDataBindEngine = DataBindEngine.CurrentDataBindEngine; if (currentDataBindEngine == null || currentDataBindEngine.IsShutDown) { return false; } _engine = currentDataBindEngine; DetermineEffectiveStringFormat(); DetermineEffectiveTargetNullValue(); DetermineEffectiveUpdateBehavior(); DetermineEffectiveValidatesOnNotifyDataErrors(); if (dp == TextBox.TextProperty && IsReflective && !IsInBindingExpressionCollection && target is TextBoxBase textBoxBase) { textBoxBase.PreviewTextInput += OnPreviewTextInput; } if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.Attach)) { TraceData.TraceAndNotifyWithNoParameters(TraceEventType.Warning, TraceData.AttachExpression(TraceData.Identify(this), target.GetType().FullName, dp.Name, AvTrace.GetHashCodeHelper(target)), this); } return true; }
其中第11行調(diào)用了一個名為 DetermineEffectiveStringFormat
的方法,顧名思義就是檢測有效的 StringFormat
。接下來看看里邊的邏輯:
internal void DetermineEffectiveStringFormat() { Type type = TargetProperty.PropertyType; if (type != typeof(string)) { return; } string stringFormat = ParentBindingBase.StringFormat; for (BindingExpressionBase parentBindingExpressionBase = ParentBindingExpressionBase; parentBindingExpressionBase != null; parentBindingExpressionBase = parentBindingExpressionBase.ParentBindingExpressionBase) { if (parentBindingExpressionBase is MultiBindingExpression) { type = typeof(object); break; } if (stringFormat == null && parentBindingExpressionBase is PriorityBindingExpression) { stringFormat = parentBindingExpressionBase.ParentBindingBase.StringFormat; } } if (type == typeof(string) && !string.IsNullOrEmpty(stringFormat)) { SetValue(Feature.EffectiveStringFormat, Helper.GetEffectiveStringFormat(stringFormat), null); } }
這段代碼的作用就是檢測有效的 StringFormat
,并通過 SetValue
方法保存起來,從第4~7行代碼可以看到,一開始就會檢測目標屬性的類型是不是 String
類型,不是的話直接返回,綁定表達式中的 StringFormat
也就不會保存了。
在后續(xù)的 BindingExpression
類計算綁定表達式值時獲取到 StringFormat
為 null
,也就不會進行格式化了。
Button
的 Content
屬性雖然可以用字符串賦值,但它其實的 Object
類型。因此,在檢測有效的 StringFormat
表達式時直接過濾了。ToolTip
也同樣是 Object
類型。
解決方法
對于 Content
這種 Object
類型的屬性綁定字符串并且需要格式化時,可以采用以下三種方式解決:
1、最通用的方法就是自定義 ValueConverter
,在 ValueConverter
中對字符串進行格式化;
2、綁定到其他可進行 StringFormat
的屬性上,比如 TextBlock
的 Text
屬性進行格式化,ToolTip
綁定到 Text
上;
3、既然是 Object
類型,那也可把 TextBlock
作為 Content
的值。
<Button Width="120" Height="30"> <Button.Content> <TextBlock Text="{Binding TestString,ElementName=self,StringFormat=test:{0}}"/> </Button.Content> </Button>
小結(jié)
數(shù)據(jù)綁定時出現(xiàn)StringFormat失效的主要分為兩種情況。一是沒有遵循綁定時StringFormat使用的約束,二是綁定的目標屬性不是 String
類型。
最后
以上就是WPF數(shù)據(jù)綁定時出現(xiàn)StringFormat失效的原因和解決方法的詳細內(nèi)容,更多關(guān)于WPF StringFormat失效的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
c#連接sqlserver數(shù)據(jù)庫、插入數(shù)據(jù)、從數(shù)據(jù)庫獲取時間示例
這篇文章主要介紹了c#連接sqlserver數(shù)據(jù)庫、插入數(shù)據(jù)、從數(shù)據(jù)庫獲取時間示例,需要的朋友可以參考下2014-05-05關(guān)于C#泛型列表List<T>的基本用法總結(jié)
本篇文章主要是對C#中泛型列表List<T>的基本用法進行了詳細的總結(jié)介紹,需要的朋友可以過來參考下,希望對大家有所幫助2014-01-01詳解Unity中Mask和RectMask2D組件的對比與測試
本篇文章給大家介紹Unity中Mask和RectMask2D組件的對比與測試,包括組件用法及RectMask2D的基本用法,通過Mask的原理分析實例代碼相結(jié)合給大家講解的非常詳細,需要的朋友參考下吧2021-06-06