AvaloniaUI / Avalonia

Develop Desktop, Embedded, Mobile and WebAssembly apps with C# and XAML. The most popular .NET UI client technology
https://avaloniaui.net
MIT License
24.76k stars 2.15k forks source link

Render Bug / frozen until resize #8123

Open JaggerJo opened 2 years ago

JaggerJo commented 2 years ago

To Reproduce Steps to reproduce the behavior:

Run attached project. In essence this should display a updating counter:

[<AllowNullLiteral>]
type RenderTestControl () =
    inherit Control ()

    let mutable counter = 0

    override this.Render (context: DrawingContext) =
        Dispatcher.UIThread.Post(Action(this.InvalidateMeasure), DispatcherPriority.Background);
        Dispatcher.UIThread.Post(Action(this.InvalidateVisual), DispatcherPriority.Background);

        let formattedText = FormattedText(
            $"{counter}",
            Typeface.Default,
            48,
            TextAlignment.Center,
            TextWrapping.Wrap,
            this.Bounds.Size
        )

        counter <- counter + 1

        context.DrawText (Brushes.Blue, Point(0, 0), formattedText)

Expected behavior Control is re-rendered, but updates are not displayed

Screenshots CleanShot 2022-05-12 at 19 56 41 fa

Desktop (please complete the following information):

MacOS 12.3.1 (21E258)

      <PackageReference Include="Avalonia" Version="0.10.14" />
      <PackageReference Include="Avalonia.Desktop" Version="0.10.14" />

Repro.zip 2]

Additional context Add any other context about the problem here.

JaggerJo commented 2 years ago

Checked @jmacato's GifImage and altered it to see why it works - and my control does not.

If I only swap the contents of the Render function with my implementation it stops working.

Adding the following line to my render function fixes it.

image
Gillibald commented 2 years ago

Try to invalidate the visual after you have added something to the scene graph. Ideally your formatted text is constructed in the measure override and just reused in the render override.

Measure -> Arrange -> Render -> Repeat

JaggerJo commented 2 years ago

Try to invalidate the visual after you have added something to the scene graph

@Gillibald Don't really get what you mean.

Ideally your formatted text is constructed in the measure override and just reused in the render override.

✅ Check

[<AllowNullLiteral>]
type RenderTestControl () =
    inherit Control ()

    let mutable counter = 0

    let mutable formattedText = null

    override this.MeasureOverride (availableSize: Size) =
        formattedText <- FormattedText(
            $"{counter}",
            Typeface.Default,
            48,
            TextAlignment.Center,
            TextWrapping.Wrap,
            this.Bounds.Size
        )

        counter <- counter + 1

        availableSize

    override this.Render (context: DrawingContext) =
        // works only with this line added
        // context.FillRectangle(Brushes.Yellow, this.Bounds); 

        context.DrawText (Brushes.Blue, Point(0, 0), formattedText)

        Dispatcher.UIThread.Post(Action(this.InvalidateMeasure), DispatcherPriority.Background);
        Dispatcher.UIThread.Post(Action(this.InvalidateVisual), DispatcherPriority.Background);

Noticed that if I make the window small enough it re-renders properly for some reason? CleanShot 2022-05-13 at 11 53 57

Gillibald commented 2 years ago

Your control probably never reports its boundaries properly. That's why I suggested to override MeasureOverride. Your FormattedText bounds should be used as the control's bounds.

https://github.com/AvaloniaUI/Avalonia/blob/master/src/Avalonia.Controls/TextBlock.cs#L601

timunie commented 2 years ago

I've tried it in latest master but with C# and it just works for me.

public class TestControl : Control
{
    private int counter = 0;

    public override void Render(DrawingContext context)
    {
        Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background);

        var formattedText = new FormattedText(counter.ToString(), CultureInfo.InvariantCulture,
            FlowDirection.LeftToRight, Typeface.Default, 48, Brushes.Blue);

        counter++;

        context.DrawText(formattedText, new Point(100, 100));
    }
}

Seems to be not working in 10.0.14. If you can update, give it a chance. https://github.com/AvaloniaUI/Avalonia/wiki/Using-nightly-build-feed

Happy coding Tim

JaggerJo commented 2 years ago

@timunie so you were able to reproduce it using my code?

Fixed this by using a custom drawing operation. Still think there's a bug in the Avalonia drawing context.

timunie commented 2 years ago

@JaggerJo I also think there was a bug. It seems to be fixed in the latest version, which is not yet released.