无法基于 DependencyProperty 将样式应用于 RichTextBox 元素
Can't apply style to RichTextBox elements based on DependencyProperty
我有一个继承 RichTextBox
(我将其称为 MyRichTextBox
)的控件,其中 DependencyProperty
定义了某些文本元素类型的样式。与 ItemsControl.ItemContainerStyle
类似,此样式将基于每个实例应用于它的某些子项。
我试过两种不同的方法,但两种方法都不起作用:
- 在创建文本元素(继承
Button
并托管在 InlineUIContainer
中的控件;我们称之为 MyTextElement
)时,我会创建一个新样式基于 MyRichTextBox.ItemContainerStyle
并指定 MyTextElement
新样式。
- 当
MyRichTextBox.ItemContainerStyle
改变时,在MyRichTextBox.ItemContainerStyle
的基础上创建一个新样式并添加到MyRichTextBox
的资源中。
这两种方法都会导致以下意外异常:
An unhandled exception of type 'MS.Internal.PtsHost.UnsafeNativeMethods.PTS.SecondaryException' occurred in PresentationFramework.dll
没有提供其他信息,在研究此异常时,我没有发现任何与 RichTextBox
或以编程方式分配样式相关的内容。一些文章表明错误是线程问题;但是,我并没有尝试在不同的线程上创建样式 creating/assigning 样式 without 基于 MyRichTextBox.ItemContainerStyle
does 工作。
这是控件使用方法 #2 的样子(为简洁起见,我排除了方法 #1,因为它做同样的事情,只是方式不同):
public class MyRichTextBox : RichTextBox
{
public static DependencyProperty ItemContainerStyleProperty = DependencyProperty.Register("ItemContainerStyle", typeof(Style), typeof(MyRichTextBox), new FrameworkPropertyMetadata(default(Style), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnItemContainerStyleChanged));
public Style ItemContainerStyle
{
get
{
return (Style)GetValue(ItemContainerStyleProperty);
}
set
{
SetValue(ItemContainerStyleProperty, value);
}
}
static void OnItemContainerStyleChanged(DependencyObject Object, DependencyPropertyChangedEventArgs e)
{
(Object as MyRichTextBox).OnItemContainerStyleChanged((Style)e.OldValue, (Style)e.NewValue);
}
protected virtual void OnItemContainerStyleChanged(Style OldValue, Style NewValue)
{
//Make sure the old style is gone
if (OldValue != null)
Resources.Remove(OldValue.TargetType);
if (NewValue != null)
{
//This line does not attempt to utilize the specified style, but is stable
Resources.Add(NewValue.TargetType, new Style(NewValue.TargetType));
//This line attempts to utilize the specified style, but is unstable
Resources.Add(NewValue.TargetType, new Style(NewValue.TargetType, NewValue));
}
}
}
最终,我希望能够做到这一点:
<Controls:MyRichTextBox>
<Controls:MyRichTextBox.ItemContainerStyle>
<Style TargetType="{x:Type Controls:MyTextElement}">
<!-- Whatever... -->
</Style>
</Controls:MyRichTextBox.ItemContainerStyle>
</Controls:MyRichTextBox>
这应该允许我为 MyRichTextBox
中类型为 MyTextElement
的所有元素定义样式。方法 #1 会给出相同的结果,但两种方法都失败并出现相同的错误。
因为分配新样式 而不是 基于 MyRichTextBox.ItemContainerStyle
是可行的,所以不清楚我做错了什么导致了这个错误。
编辑:
MyTextElement
看起来像这样:
public class MyTextElement : Button
{
public MyTextElement() : base()
{
}
}
MyRichTextBox
的有效逻辑结构如下所示:
<Controls:MyRichTextBox>
<FlowDocument>
<Paragraph>
<InlineUIContainer>
<Controls:MyTextElement Content="My Content"/>
</InlineUIContainer>
</Paragraph >
</FlowDocument>
</Controls:MyRichTextBox>
您只能将 一个 键为 NewValue.TargetType
的资源添加到 ResourceDictionary
。
以下示例代码对我来说工作正常:
public class MyTextElement : Run { }
public class MyRichTextBox : RichTextBox
{
public static DependencyProperty ItemContainerStyleProperty = DependencyProperty.Register("ItemContainerStyle", typeof(Style), typeof(MyRichTextBox), new FrameworkPropertyMetadata(default(Style), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnItemContainerStyleChanged));
public Style ItemContainerStyle
{
get
{
return (Style)GetValue(ItemContainerStyleProperty);
}
set
{
SetValue(ItemContainerStyleProperty, value);
}
}
static void OnItemContainerStyleChanged(DependencyObject Object, DependencyPropertyChangedEventArgs e)
{
(Object as MyRichTextBox).OnItemContainerStyleChanged((Style)e.OldValue, (Style)e.NewValue);
}
protected virtual void OnItemContainerStyleChanged(Style OldValue, Style NewValue)
{
//Make sure the old style is gone
if (OldValue != null)
Resources.Remove(OldValue.TargetType);
if (NewValue != null)
{
//This line does not attempt to utilize the specified style, but is stable
//Resources.Add(NewValue.TargetType, new Style(NewValue.TargetType));
//This line attempts to utilize the specified style, but is unstable
Resources.Add(NewValue.TargetType, new Style(NewValue.TargetType, NewValue));
}
}
}
<Controls:MyRichTextBox>
<Controls:MyRichTextBox.ItemContainerStyle>
<Style TargetType="{x:Type Controls:MyTextElement}">
<Setter Property="Foreground" Value="Red" />
</Style>
</Controls:MyRichTextBox.ItemContainerStyle>
<FlowDocument>
<Paragraph>
<Run Text="default" />
<Controls:MyTextElement Text="red" />
</Paragraph>
</FlowDocument>
</Controls:MyRichTextBox>
编辑:
MyTextElement
inherits Button
and is assigned as the child of InlineUIContainer
; ultimately, the style needs to apply to MyTextElement
, NOT a Run
. Your MyTextElement
inherits Run
and isn't a child of anything
这也有效:
public class MyTextElement : Button
{
public MyTextElement() : base()
{
}
}
public class MyRichTextBox : RichTextBox
{
public static DependencyProperty ItemContainerStyleProperty = DependencyProperty.Register("ItemContainerStyle",
typeof(Style), typeof(MyRichTextBox), new FrameworkPropertyMetadata(default(Style),
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnItemContainerStyleChanged));
public Style ItemContainerStyle
{
get
{
return (Style)GetValue(ItemContainerStyleProperty);
}
set
{
SetValue(ItemContainerStyleProperty, value);
}
}
static void OnItemContainerStyleChanged(DependencyObject Object, DependencyPropertyChangedEventArgs e)
{
(Object as MyRichTextBox).OnItemContainerStyleChanged((Style)e.OldValue, (Style)e.NewValue);
}
protected virtual void OnItemContainerStyleChanged(Style OldValue, Style NewValue)
{
//Make sure the old style is gone
if (OldValue != null)
Resources.Remove(OldValue.TargetType);
if (NewValue != null)
{
Resources.Add(NewValue.TargetType, new Style(NewValue.TargetType, NewValue));
}
}
}
<Controls:MyRichTextBox IsDocumentEnabled="True">
<Controls:MyRichTextBox.ItemContainerStyle>
<Style TargetType="{x:Type Controls:MyTextElement}">
<Setter Property="Foreground" Value="Red" />
</Style>
</Controls:MyRichTextBox.ItemContainerStyle>
<FlowDocument>
<Paragraph>
<InlineUIContainer>
<Controls:MyTextElement Content="My Content"/>
</InlineUIContainer>
</Paragraph >
</FlowDocument>
</Controls:MyRichTextBox>
请注意,我已从 OnItemContainerStyleChanged
方法中删除了以下行:
Resources.Add(NewValue.TargetType, new Style(NewValue.TargetType));
我有一个继承 RichTextBox
(我将其称为 MyRichTextBox
)的控件,其中 DependencyProperty
定义了某些文本元素类型的样式。与 ItemsControl.ItemContainerStyle
类似,此样式将基于每个实例应用于它的某些子项。
我试过两种不同的方法,但两种方法都不起作用:
- 在创建文本元素(继承
Button
并托管在InlineUIContainer
中的控件;我们称之为MyTextElement
)时,我会创建一个新样式基于MyRichTextBox.ItemContainerStyle
并指定MyTextElement
新样式。 - 当
MyRichTextBox.ItemContainerStyle
改变时,在MyRichTextBox.ItemContainerStyle
的基础上创建一个新样式并添加到MyRichTextBox
的资源中。
这两种方法都会导致以下意外异常:
An unhandled exception of type 'MS.Internal.PtsHost.UnsafeNativeMethods.PTS.SecondaryException' occurred in PresentationFramework.dll
没有提供其他信息,在研究此异常时,我没有发现任何与 RichTextBox
或以编程方式分配样式相关的内容。一些文章表明错误是线程问题;但是,我并没有尝试在不同的线程上创建样式 creating/assigning 样式 without 基于 MyRichTextBox.ItemContainerStyle
does 工作。
这是控件使用方法 #2 的样子(为简洁起见,我排除了方法 #1,因为它做同样的事情,只是方式不同):
public class MyRichTextBox : RichTextBox
{
public static DependencyProperty ItemContainerStyleProperty = DependencyProperty.Register("ItemContainerStyle", typeof(Style), typeof(MyRichTextBox), new FrameworkPropertyMetadata(default(Style), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnItemContainerStyleChanged));
public Style ItemContainerStyle
{
get
{
return (Style)GetValue(ItemContainerStyleProperty);
}
set
{
SetValue(ItemContainerStyleProperty, value);
}
}
static void OnItemContainerStyleChanged(DependencyObject Object, DependencyPropertyChangedEventArgs e)
{
(Object as MyRichTextBox).OnItemContainerStyleChanged((Style)e.OldValue, (Style)e.NewValue);
}
protected virtual void OnItemContainerStyleChanged(Style OldValue, Style NewValue)
{
//Make sure the old style is gone
if (OldValue != null)
Resources.Remove(OldValue.TargetType);
if (NewValue != null)
{
//This line does not attempt to utilize the specified style, but is stable
Resources.Add(NewValue.TargetType, new Style(NewValue.TargetType));
//This line attempts to utilize the specified style, but is unstable
Resources.Add(NewValue.TargetType, new Style(NewValue.TargetType, NewValue));
}
}
}
最终,我希望能够做到这一点:
<Controls:MyRichTextBox>
<Controls:MyRichTextBox.ItemContainerStyle>
<Style TargetType="{x:Type Controls:MyTextElement}">
<!-- Whatever... -->
</Style>
</Controls:MyRichTextBox.ItemContainerStyle>
</Controls:MyRichTextBox>
这应该允许我为 MyRichTextBox
中类型为 MyTextElement
的所有元素定义样式。方法 #1 会给出相同的结果,但两种方法都失败并出现相同的错误。
因为分配新样式 而不是 基于 MyRichTextBox.ItemContainerStyle
是可行的,所以不清楚我做错了什么导致了这个错误。
编辑:
MyTextElement
看起来像这样:
public class MyTextElement : Button
{
public MyTextElement() : base()
{
}
}
MyRichTextBox
的有效逻辑结构如下所示:
<Controls:MyRichTextBox>
<FlowDocument>
<Paragraph>
<InlineUIContainer>
<Controls:MyTextElement Content="My Content"/>
</InlineUIContainer>
</Paragraph >
</FlowDocument>
</Controls:MyRichTextBox>
您只能将 一个 键为 NewValue.TargetType
的资源添加到 ResourceDictionary
。
以下示例代码对我来说工作正常:
public class MyTextElement : Run { }
public class MyRichTextBox : RichTextBox
{
public static DependencyProperty ItemContainerStyleProperty = DependencyProperty.Register("ItemContainerStyle", typeof(Style), typeof(MyRichTextBox), new FrameworkPropertyMetadata(default(Style), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnItemContainerStyleChanged));
public Style ItemContainerStyle
{
get
{
return (Style)GetValue(ItemContainerStyleProperty);
}
set
{
SetValue(ItemContainerStyleProperty, value);
}
}
static void OnItemContainerStyleChanged(DependencyObject Object, DependencyPropertyChangedEventArgs e)
{
(Object as MyRichTextBox).OnItemContainerStyleChanged((Style)e.OldValue, (Style)e.NewValue);
}
protected virtual void OnItemContainerStyleChanged(Style OldValue, Style NewValue)
{
//Make sure the old style is gone
if (OldValue != null)
Resources.Remove(OldValue.TargetType);
if (NewValue != null)
{
//This line does not attempt to utilize the specified style, but is stable
//Resources.Add(NewValue.TargetType, new Style(NewValue.TargetType));
//This line attempts to utilize the specified style, but is unstable
Resources.Add(NewValue.TargetType, new Style(NewValue.TargetType, NewValue));
}
}
}
<Controls:MyRichTextBox>
<Controls:MyRichTextBox.ItemContainerStyle>
<Style TargetType="{x:Type Controls:MyTextElement}">
<Setter Property="Foreground" Value="Red" />
</Style>
</Controls:MyRichTextBox.ItemContainerStyle>
<FlowDocument>
<Paragraph>
<Run Text="default" />
<Controls:MyTextElement Text="red" />
</Paragraph>
</FlowDocument>
</Controls:MyRichTextBox>
编辑:
MyTextElement
inheritsButton
and is assigned as the child ofInlineUIContainer
; ultimately, the style needs to apply toMyTextElement
, NOT aRun
. YourMyTextElement
inheritsRun
and isn't a child of anything
这也有效:
public class MyTextElement : Button
{
public MyTextElement() : base()
{
}
}
public class MyRichTextBox : RichTextBox
{
public static DependencyProperty ItemContainerStyleProperty = DependencyProperty.Register("ItemContainerStyle",
typeof(Style), typeof(MyRichTextBox), new FrameworkPropertyMetadata(default(Style),
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnItemContainerStyleChanged));
public Style ItemContainerStyle
{
get
{
return (Style)GetValue(ItemContainerStyleProperty);
}
set
{
SetValue(ItemContainerStyleProperty, value);
}
}
static void OnItemContainerStyleChanged(DependencyObject Object, DependencyPropertyChangedEventArgs e)
{
(Object as MyRichTextBox).OnItemContainerStyleChanged((Style)e.OldValue, (Style)e.NewValue);
}
protected virtual void OnItemContainerStyleChanged(Style OldValue, Style NewValue)
{
//Make sure the old style is gone
if (OldValue != null)
Resources.Remove(OldValue.TargetType);
if (NewValue != null)
{
Resources.Add(NewValue.TargetType, new Style(NewValue.TargetType, NewValue));
}
}
}
<Controls:MyRichTextBox IsDocumentEnabled="True">
<Controls:MyRichTextBox.ItemContainerStyle>
<Style TargetType="{x:Type Controls:MyTextElement}">
<Setter Property="Foreground" Value="Red" />
</Style>
</Controls:MyRichTextBox.ItemContainerStyle>
<FlowDocument>
<Paragraph>
<InlineUIContainer>
<Controls:MyTextElement Content="My Content"/>
</InlineUIContainer>
</Paragraph >
</FlowDocument>
</Controls:MyRichTextBox>
请注意,我已从 OnItemContainerStyleChanged
方法中删除了以下行:
Resources.Add(NewValue.TargetType, new Style(NewValue.TargetType));