无法基于DependencyProperty将样式应用于RichTextBox元素

时间:2017-02-09 21:06:14

标签: c# wpf xaml richtextbox dependency-properties

我有一个控件继承RichTextBox(我将其称为MyRichTextBox),其中DependencyProperty定义了某些文本元素类型的样式。与ItemsControl.ItemContainerStyle类似,此样式将基于每个实例应用于其中一些子项。

我尝试过这两种不同的方法,两种方法都不起作用:

  1. 创建文本元素(继承Button并托管在InlineUIContainer中的控件;我们将调用此MyTextElement)时,我会创建一个基于的新样式MyRichTextBox.ItemContainerStyle并指定MyTextElement新款式。
  2. MyRichTextBox.ItemContainerStyle更改时,请根据MyRichTextBox.ItemContainerStyle创建新样式并添加到MyRichTextBox的资源。
  3. 这两种方法都会导致以下意外异常:

    An unhandled exception of type 'MS.Internal.PtsHost.UnsafeNativeMethods.PTS.SecondaryException' occurred in PresentationFramework.dll
    

    没有给出其他信息,在研究此异常时,我没有找到与RichTextBox或以编程方式分配样式有关的任何内容。一些文章表明错误是一个线程问题;但是,我不是试图在不同的线程上创建样式,并且在MyRichTextBox.ItemContainerStyle 工作的基础上创建/分配样式而不

    这就是控件使用方法#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>
    

    这应该允许我为MyTextElementMyRichTextBox类型的所有元素定义样式。方法#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>
    

1 个答案:

答案 0 :(得分:1)

您只能将{em>一个资源的密钥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>

enter image description here

修改

  

MyTextElement继承Button,并被指定为InlineUIContainer的孩子;最终,样式需要适用于MyTextElement,而不是Run。您的MyTextElement继承了Run并且不是任何人的孩子

这也有效:

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));

enter image description here