在 WPF 中嘗試透過 Style 設定 ToolTipService.ToolTip 並直接在 Setter.Value 放入 TextBlock 時,會拋出 XamlParseException:「無法將 'System.Windows.Controls.TextBlock' 類型的內容加入 'System.Object' 類型的物件」。本文從 BAML 解析機制深入分析根本原因,並提供正確解決方案。
<TextBlock x:Name="textBlockContainer" Text="ABC" Margin="10">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="ToolTipService.ToolTip">
<Setter.Value>
<TextBlock x:Name="tooltipBlock" Text="長文字內容" TextWrapping="Wrap" Width="70" />
</Setter.Value>
</Setter>
</Style>
</TextBlock.Style>
</TextBlock>
錯誤訊息:Cannot add content of type 'System.Windows.Controls.TextBlock' to an object of type 'System.Object'.
開啟 CLR Exception 中斷點後,可發現更早的內部異常:InvalidCastException: Unable to cast object of type 'System.Object' to type 'System.Reflection.MemberInfo'. 發生在 BamlRecordReader.SetPropertyValueToParent 方法。
WPF XAML 解析採自頂向下、由外而內方式。解析到 Setter.Value 時:
TextBlock (tooltipBlock) 實例Setter.Value) 的某個屬性Setter.Value 未賦值時回傳 DependencyProperty.UnsetValue(型別為 Object)ContentProperty、IList、IAddChild 等介面,均失敗WPF XAML 節點處理機制:實例化當前節點 → 設值給父節點屬性。若父節點屬性型別為 Object (如 UnsetValue),無法接受 UIElement 內容,故失敗。
將 ToolTip 內容定義為資源,再透過靜態資源引用:
<TextBlock x:Name="textBlockContainer" Text="ABC" Margin="10">
<TextBlock.Resources>
<TextBlock x:Key="toolTipBlock" Text="長文字內容" TextWrapping="Wrap" Width="70" />
</TextBlock.Resources>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="ToolTipService.ToolTip" Value="{StaticResource toolTipBlock}" />
</Style>
</TextBlock.Style>
</TextBlock>
此方式避開 Setter.Value 直接接受 UIElement 的問題,符合 WPF 資源解析機制。
若直接在屬性元素語法設定 TextBlock.ToolTip(非透過 Style)則無此問題,因父節點為 ToolTip 物件,具備 Content 屬性可接受 UIElement。