如何更改 FlowDocumentReader 的 FindTextBox 样式以匹配我的应用程序的主题?
How do I change the style of FindTextBox of FlowDocumentReader to match the theme of my app?
我有一个带有 FlowDocumentReader
控件的 WPF 应用程序,当我单击搜索按钮搜索文本时,文本框与应用程序的主题不匹配。文本有,但文本框的背景没有。因此,如果我使用深色主题,我在文本框中输入的文本是白色的(这是正确的),但文本框背景也是白色的(不正确,使文本难以辨认)。
有谁知道如何解决这个问题?我尝试应用样式,但我不知道要定位哪个组件。
哇,微软 真的 没让这一切变得简单。
我的过程
我尝试了将 Style TargeType="TextBox"
添加到 FlowDocumentReader.Resources
的简单技巧,但这不起作用。
我尝试以“正确”的方式做事并覆盖 FlowDocumentReader
的 ControlTemplate
,但有问题的 TextBox
甚至不是 ControlTemaplte
的一部分!相反,有一个名为 PART_FindToolBarHost
的 Border
。我们想要的 TextBox
在代码中作为子项添加到 PART_FindToolBarHost
- 但仅在 之后 用户单击“查找”按钮(带放大镜的按钮)玻璃图标)。您可以通过查看控件的 source code.
来亲自了解这一点
由于没有更多 XAML 的想法,我不得不求助于使用代码。我们需要以某种方式获得对正在创建的 TextBox
的引用,我想不出比手动搜索可视化树更好的方法了。由于 TextBox
仅在执行 find 命令后才存在,所以这很复杂。
具体来说,FlowDocumentReader
中的查找按钮绑定到 ApplicationCommands.Find
。我尝试将该命令的 CommandBinding
添加到 FlowDocumentReader
,这样我就可以将其用作检索 TextBox
的触发器。不幸的是,添加这样的 CommandBinding
以某种方式破坏了内置功能并阻止了 TextBox
的生成。即使您设置 e.Handled = False
.
也会中断
幸运的是,FlowDocumentReader
公开了一个 OnFindCommand
- except, of course, it's a Protected
method. So I finally gave in and decided to inherit FlowDocumentReader
. OnFindCommand
works as a reliable trigger, but it turns out the TextBox
isn't created until after the sub finishes. I was forced to use Dispatcher.BeginInvoke
,以便在实际添加 TextBox
之后为 运行 安排一个方法。
使用OnFindCommand
作为触发器,我终于能够可靠地获得对“查找”TextBox
的引用,它实际上被命名为FindTextBox
.
现在我可以获得参考,我们可以应用我们自己的 Style
。除了:FindTextBox
已经有一个 Style
,所以除非我们想覆盖它,否则我们将不得不合并两个 Style
。对此没有可公开访问的方法(尽管 WPF 在某些地方在内部执行此操作),但幸运的是我已经有了一些代码。
工作代码
首先,Module
和我使用的辅助方法:
FindVisualChild
用于循环遍历可视化树并获取对 FindTextBox
.
的引用
MergeStyles
用于将现有的 Style
与我们提供的 Style
结合起来,一旦我们有了该参考。
Module OtherMethods
<Extension()>
Public Function FindVisualChild(obj As DependencyObject, Name As String) As FrameworkElement
For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(obj) - 1
Dim ChildObj As DependencyObject = VisualTreeHelper.GetChild(obj, i)
If TypeOf ChildObj Is FrameworkElement AndAlso DirectCast(ChildObj, FrameworkElement).Name = Name Then Return ChildObj
ChildObj = FindVisualChild(ChildObj, Name)
If ChildObj IsNot Nothing Then Return ChildObj
Next
Return Nothing
End Function
Public Function MergeStyles(ByVal style1 As Style, ByVal style2 As Style) As Style
Dim R As New Style
If style1 Is Nothing Then Throw New ArgumentNullException("style1")
If style2 Is Nothing Then Throw New ArgumentNullException("style2")
If style2.BasedOn IsNot Nothing Then style1 = MergeStyles(style1, style2.BasedOn)
For Each currentSetter As SetterBase In style1.Setters
R.Setters.Add(currentSetter)
Next
For Each currentTrigger As TriggerBase In style1.Triggers
R.Triggers.Add(currentTrigger)
Next
For Each key As Object In style1.Resources.Keys
R.Resources(key) = style1.Resources(key)
Next
For Each currentSetter As SetterBase In style2.Setters
R.Setters.Add(currentSetter)
Next
For Each currentTrigger As TriggerBase In style2.Triggers
R.Triggers.Add(currentTrigger)
Next
For Each key As Object In style2.Resources.Keys
R.Resources(key) = style2.Resources(key)
Next
Return R
End Function
End Module
然后是StyleableFlowDocumentReader
,也就是我给继承FlowDocumentReader
的扩展控件命名的:
Public Class StyleableFlowDocumentReader
Inherits FlowDocumentReader
Protected Overrides Sub OnFindCommand()
MyBase.OnFindCommand()
Dispatcher.BeginInvoke(Sub() GetFindTextBox(), DispatcherPriority.Render)
End Sub
Private Sub GetFindTextBox()
findTextBox = Me.FindVisualChild("FindTextBox")
ApplyFindTextBoxStyle()
End Sub
Private Sub ApplyFindTextBoxStyle()
If findTextBox IsNot Nothing Then
If findTextBox.Style IsNot Nothing AndAlso FindTextBoxStyle IsNot Nothing Then
findTextBox.Style = MergeStyles(findTextBox.Style, FindTextBoxStyle)
Else
findTextBox.Style = If(FindTextBoxStyle, findTextBox.Style)
End If
End If
End Sub
Private findTextBox As TextBox
Public Property FindTextBoxStyle As Style
Get
Return GetValue(FindTextBoxStyleProperty)
End Get
Set(ByVal value As Style)
SetValue(FindTextBoxStyleProperty, value)
End Set
End Property
Public Shared ReadOnly FindTextBoxStyleProperty As DependencyProperty =
DependencyProperty.Register("FindTextBoxStyle",
GetType(Style), GetType(StyleableFlowDocumentReader),
New PropertyMetadata(Nothing, Sub(d, e) DirectCast(d, StyleableFlowDocumentReader).ApplyFindTextBoxStyle()))
End Class
然后,最后,一个用法示例:
<local:StyleableFlowDocumentReader x:Name="Reader">
<local:StyleableFlowDocumentReader.FindTextBoxStyle>
<Style TargetType="TextBox">
<Setter Property="Foreground" Value="Blue"/>
</Style>
</local:StyleableFlowDocumentReader.FindTextBoxStyle>
<FlowDocument/>
</local:StyleableFlowDocumentReader>
我有一个带有 FlowDocumentReader
控件的 WPF 应用程序,当我单击搜索按钮搜索文本时,文本框与应用程序的主题不匹配。文本有,但文本框的背景没有。因此,如果我使用深色主题,我在文本框中输入的文本是白色的(这是正确的),但文本框背景也是白色的(不正确,使文本难以辨认)。
有谁知道如何解决这个问题?我尝试应用样式,但我不知道要定位哪个组件。
哇,微软 真的 没让这一切变得简单。
我的过程
我尝试了将 Style TargeType="TextBox"
添加到 FlowDocumentReader.Resources
的简单技巧,但这不起作用。
我尝试以“正确”的方式做事并覆盖 FlowDocumentReader
的 ControlTemplate
,但有问题的 TextBox
甚至不是 ControlTemaplte
的一部分!相反,有一个名为 PART_FindToolBarHost
的 Border
。我们想要的 TextBox
在代码中作为子项添加到 PART_FindToolBarHost
- 但仅在 之后 用户单击“查找”按钮(带放大镜的按钮)玻璃图标)。您可以通过查看控件的 source code.
由于没有更多 XAML 的想法,我不得不求助于使用代码。我们需要以某种方式获得对正在创建的 TextBox
的引用,我想不出比手动搜索可视化树更好的方法了。由于 TextBox
仅在执行 find 命令后才存在,所以这很复杂。
具体来说,FlowDocumentReader
中的查找按钮绑定到 ApplicationCommands.Find
。我尝试将该命令的 CommandBinding
添加到 FlowDocumentReader
,这样我就可以将其用作检索 TextBox
的触发器。不幸的是,添加这样的 CommandBinding
以某种方式破坏了内置功能并阻止了 TextBox
的生成。即使您设置 e.Handled = False
.
幸运的是,FlowDocumentReader
公开了一个 OnFindCommand
- except, of course, it's a Protected
method. So I finally gave in and decided to inherit FlowDocumentReader
. OnFindCommand
works as a reliable trigger, but it turns out the TextBox
isn't created until after the sub finishes. I was forced to use Dispatcher.BeginInvoke
,以便在实际添加 TextBox
之后为 运行 安排一个方法。
使用OnFindCommand
作为触发器,我终于能够可靠地获得对“查找”TextBox
的引用,它实际上被命名为FindTextBox
.
现在我可以获得参考,我们可以应用我们自己的 Style
。除了:FindTextBox
已经有一个 Style
,所以除非我们想覆盖它,否则我们将不得不合并两个 Style
。对此没有可公开访问的方法(尽管 WPF 在某些地方在内部执行此操作),但幸运的是我已经有了一些代码。
工作代码
首先,Module
和我使用的辅助方法:
FindVisualChild
用于循环遍历可视化树并获取对 FindTextBox
.
的引用
MergeStyles
用于将现有的 Style
与我们提供的 Style
结合起来,一旦我们有了该参考。
Module OtherMethods
<Extension()>
Public Function FindVisualChild(obj As DependencyObject, Name As String) As FrameworkElement
For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(obj) - 1
Dim ChildObj As DependencyObject = VisualTreeHelper.GetChild(obj, i)
If TypeOf ChildObj Is FrameworkElement AndAlso DirectCast(ChildObj, FrameworkElement).Name = Name Then Return ChildObj
ChildObj = FindVisualChild(ChildObj, Name)
If ChildObj IsNot Nothing Then Return ChildObj
Next
Return Nothing
End Function
Public Function MergeStyles(ByVal style1 As Style, ByVal style2 As Style) As Style
Dim R As New Style
If style1 Is Nothing Then Throw New ArgumentNullException("style1")
If style2 Is Nothing Then Throw New ArgumentNullException("style2")
If style2.BasedOn IsNot Nothing Then style1 = MergeStyles(style1, style2.BasedOn)
For Each currentSetter As SetterBase In style1.Setters
R.Setters.Add(currentSetter)
Next
For Each currentTrigger As TriggerBase In style1.Triggers
R.Triggers.Add(currentTrigger)
Next
For Each key As Object In style1.Resources.Keys
R.Resources(key) = style1.Resources(key)
Next
For Each currentSetter As SetterBase In style2.Setters
R.Setters.Add(currentSetter)
Next
For Each currentTrigger As TriggerBase In style2.Triggers
R.Triggers.Add(currentTrigger)
Next
For Each key As Object In style2.Resources.Keys
R.Resources(key) = style2.Resources(key)
Next
Return R
End Function
End Module
然后是StyleableFlowDocumentReader
,也就是我给继承FlowDocumentReader
的扩展控件命名的:
Public Class StyleableFlowDocumentReader
Inherits FlowDocumentReader
Protected Overrides Sub OnFindCommand()
MyBase.OnFindCommand()
Dispatcher.BeginInvoke(Sub() GetFindTextBox(), DispatcherPriority.Render)
End Sub
Private Sub GetFindTextBox()
findTextBox = Me.FindVisualChild("FindTextBox")
ApplyFindTextBoxStyle()
End Sub
Private Sub ApplyFindTextBoxStyle()
If findTextBox IsNot Nothing Then
If findTextBox.Style IsNot Nothing AndAlso FindTextBoxStyle IsNot Nothing Then
findTextBox.Style = MergeStyles(findTextBox.Style, FindTextBoxStyle)
Else
findTextBox.Style = If(FindTextBoxStyle, findTextBox.Style)
End If
End If
End Sub
Private findTextBox As TextBox
Public Property FindTextBoxStyle As Style
Get
Return GetValue(FindTextBoxStyleProperty)
End Get
Set(ByVal value As Style)
SetValue(FindTextBoxStyleProperty, value)
End Set
End Property
Public Shared ReadOnly FindTextBoxStyleProperty As DependencyProperty =
DependencyProperty.Register("FindTextBoxStyle",
GetType(Style), GetType(StyleableFlowDocumentReader),
New PropertyMetadata(Nothing, Sub(d, e) DirectCast(d, StyleableFlowDocumentReader).ApplyFindTextBoxStyle()))
End Class
然后,最后,一个用法示例:
<local:StyleableFlowDocumentReader x:Name="Reader">
<local:StyleableFlowDocumentReader.FindTextBoxStyle>
<Style TargetType="TextBox">
<Setter Property="Foreground" Value="Blue"/>
</Style>
</local:StyleableFlowDocumentReader.FindTextBoxStyle>
<FlowDocument/>
</local:StyleableFlowDocumentReader>