Создайте пользовательский FrameworkContentElement для добавления диагональной линии над текстом в WPF - PullRequest
7 голосов
/ 25 марта 2011

Есть ли способ создать пользовательский FrameworkContentElement (или Inline), который рисует диагональную линию поверх своего содержимого?

Что-то похожее на зачеркнутое украшение, но диагональной формы: example of strike-through and diagonal decorations

Невозможно присваивать TextDecoration или TextEffect (они запечатаны).

Есть идеи?

Ответы [ 3 ]

6 голосов
/ 26 марта 2011

ОБНОВЛЕНИЕ :

Я попытался создать пример как можно меньше.В более сложных сценариях вам придется расширить это.Вот как это выглядит:

enter image description here

это соответствующий xaml:

<AdornerDecorator>
    <StackPanel>
        <TextBlock>
            <Run>this is normal Text</Run><LineBreak/>
            <Run local:DiagonalStrikeThroughAdorner.StrikeThroughBrush="Red">Some text with diagonal decoration</Run><LineBreak/>
            <Run>more normal text yeah</Run>
        </TextBlock> 
        <FlowDocumentScrollViewer>
            <FlowDocument>
                <Paragraph>
                    <Run>this is normal Text</Run>
                    <LineBreak/>
                    <Run local:DiagonalStrikeThroughAdorner.StrikeThroughBrush="Red">Some text with diagonal decoration</Run>
                    <LineBreak/>
                    <Run>more normal text yeah</Run>
                </Paragraph>
            </FlowDocument>
        </FlowDocumentScrollViewer>
    </StackPanel>
</AdornerDecorator>

, и это код:

public class DiagonalStrikeThroughAdorner : Adorner
{
    private readonly Inline _inline;
    private readonly Pen _pen;

    public DiagonalStrikeThroughAdorner(UIElement adornedElement, Inline inline, Brush brush) : base(adornedElement)
    {
        _inline = inline;
        _pen = new Pen(brush, 2);
    }

    protected override void OnRender(DrawingContext drawingContext)
    {

        if(!(_inline.ContentStart.HasValidLayout && _inline.ContentEnd.HasValidLayout))
            return;
        var startrect = _inline.ContentStart.GetCharacterRect(LogicalDirection.Forward);
        var endrect = _inline.ContentEnd.GetCharacterRect(LogicalDirection.Backward);

        drawingContext.DrawLine(_pen,startrect.BottomLeft,endrect.TopRight);
    }

    public static Brush GetStrikeThroughBrush(DependencyObject obj)
    {
        return (Brush)obj.GetValue(StrikeThroughBrushProperty);
    }

    public static void SetStrikeThroughBrush(DependencyObject obj, Brush value)
    {
        obj.SetValue(StrikeThroughBrushProperty, value);
    }

    public static readonly DependencyProperty StrikeThroughBrushProperty =
        DependencyProperty.RegisterAttached("StrikeThroughBrush", typeof(Brush), typeof(DiagonalStrikeThroughAdorner), new UIPropertyMetadata((o, args) =>
            {
                if(!(o is TextElement)) return;
                var parent = ((TextElement)o).Parent;
                while (parent is FrameworkContentElement)
                    parent = ((FrameworkContentElement) parent).Parent;
                if (parent == null || !(parent is Visual)) return;
                var adornerLayer = AdornerLayer.GetAdornerLayer((Visual) parent);
                if(adornerLayer == null) return;
                adornerLayer.Add(new DiagonalStrikeThroughAdorner((UIElement) parent,o as Inline,(Brush) args.NewValue));                    
            }));

}

весело провести время!

оригинальное сообщение :

это обычно довольно сложно.Мне удалось прикрепить рекламное объявление к конкретным элементам в flowdocuments, но есть много угловых случаев для рассмотрения.например: что должно произойти, если этот Inline обернут?далее: если этот потоковый документ находится в текстовом поле richtextbox, его внутренние компоненты продолжают перестраивать прогоны (объединяя или разделяя их), что в значительной степени портит все.Вы должны тщательно все настроить.

Пожалуйста, опишите подробнее, где будет находиться эта строка.Внутри FlowdocumentScrollviewer?Или текстовый блок?Или Richtextbox?Так как вам нужно прикрепить рекламодателя к управляющему FrameworkElement (как вы, вероятно, уже заметили, вы не можете напрямую прикрепить рекламодатель к FrameworkContentElement), нам нужно знать, где находится встроенный элемент.

Я опишу общий маршруто том, как этого добиться: создайте прикрепленное свойство, которое будет создавать рекламодатель.прикрепленное свойство устанавливается на встроенном, который будет украшен.владелец сохраняет ссылку на встроенный элемент и присоединяется к управляющему элементу FrameworkElement.подпишитесь на макет, обновленный на этом элементе кадра, и сделайте визуальный недействительный на Adorner.Украшающие OnRender рисуют линию с координатами в зависимости от прямоугольников Inlines ContentStart и ContentEnd GetCharacterRect .сделано.

1 голос
/ 18 ноября 2011
<TextBlock>
        <Run>this is normal Text</Run><LineBreak/>
        <Run local:DiagonalStrikeThroughAdorner.StrikeThroughBrush="Red">Some text with diagonal decoration</Run><LineBreak/>
        <Run>more normal text yeah</Run>
    </TextBlock>

Adorner не будет отображаться, потому что

var adornerLayer = AdornerLayer.GetAdornerLayer((Visual) parent);
            if(adornerLayer == null)

всегда возвращает ноль, мы должны установить присоединенное свойство после того, как Run Loaded.

0 голосов
/ 25 марта 2011

Поместите ваш текст в Canvas или Grid (что позволяет элементам управления перекрываться) и добавьте объект Line с точками X / Y, привязанными к вашей позиции TextBlock

Примерно так:

<Canvas HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Yellow">
    <TextBlock x:Name="TestText" Text="This is a Test"  />
    <Line X1="{Binding ElementName=TestText, Path=ActualWidth}"
            Y1="{Binding ElementName=TestText, Path=ActualHeight}"
            X2="{Binding ElementName=TestText, Path=Canvas.Left}"
            Y2="{Binding ElementName=TestText, Path=Canvas.Top}"
            Stroke="Red" StrokeThickness="2" />
</Canvas>
...