Changing size does not re-layout html

Sep 4, 2013 at 8:09 AM
I have an issue regarding resizing the HtmlLabel control, which doesn't relayout the html after the control size changes.

I have the HtmlLabel placed inside another control, docked to the top. It is set to only autosize the height. When the contents of the HtmlLabel changes, the width of the parent control might change (to accommodate room for a custom scrollbar). This will of course change the width of the HtmlLabel (through the docking), but the contents is still rendered using the previous with.

I've debuged this a bit now and can see that the OnLayout method does not get called when the size changes. I've also implemented a the following workarround in the HtmlLabel, which makes it work - at least for my purpose.
private int lastwidth = 0;
protected override void OnResize(EventArgs e)
{
  base.OnResize(e);
  if (lastwidth!=Width)
  {
    OnLayout(new LayoutEventArgs(this,""));
    lastwidth = Width;
  }
}
I hope you can either add the above code in some way or maybe implement a better and more generic solution.
Developer
Sep 8, 2013 at 4:25 PM
Are you sure AutoSizeHeightOnly property is set to 'true'?
I have just tested it and it works fine...
Sep 8, 2013 at 8:45 PM
Yes, I'm positive that the AutoSizeHeightOnly is set to true (and AutoSize = false). Otherwise the label would never change height and thus never require the custom scroller to kick in.

I works fine when then resize is caused by changing the window size. When the resize is tiggered by change of HtmlLabel.Text, I can see by debugging that the HtmlLabel changes width, precisely as it should, but it does not relayout the contents. Also, looking at the HtmlLabel code, I can't see how it handles resizing (there's no resizing methods other than the one I've added for my workarround). I might have missed something, I didn't spend that much time after I got the OnResize method to solve my issue - at least for the time being.
Developer
Sep 9, 2013 at 8:16 AM
The label doesn't need to handle resize as it is a simple control, it is the parent control responsibility to call layout when appropriate.
My guess is that in your custom control you override the OnLayout and don't call base.OnLayout.
If not please provide a sample project as I'm unable to reproduce it myself.
Sep 9, 2013 at 10:06 AM
My custom control do call the base function. Otherwise I would not see the HtmlLabel control changing size. As I wrote, the HtmlLabel gets the correct size but does not update the contents accordingly.

I've made a small test application to illustrate, taking my original code and cutting out as much a possible. You can get it from the link below (binary and full source):

ftp://ftp.greenwood.dk/download/HtmlRendererTest.zip

Run the application and reduce the height of the window until a red bar appears (this bar is a placeholder for the custom scrollbar, which I've not included). Now press the "Short text" button. The only this it does, is assign HtmlLabel.Text with a shorter text, which do not require the scrollbar. You'll see that the red bar disappears, The HtmlLabel control fills out the entire width, but the text is layouted using the previous width yielding a white margin.

If you then press the "Long text" button, again the HtmlLabel.Text is changed (back to the original longer text), requiring the scrollbar. The red bar appears, but parts of the text is hidden behind the bar, even though the HtmlLabel gets the correct size (can be verified by debugging/breakpoints)
Developer
Sep 9, 2013 at 1:07 PM
I get timeout from the ftp
Sep 9, 2013 at 2:06 PM
Weird, I can download without problems.

Try this instead: https://www.dropbox.com/s/vd6z98ushm87e71/HtmlRendererTest.zip
Developer
Sep 9, 2013 at 5:49 PM
Ok... the issue is that you are changing the width of the label as a result of it's changing its height so its still inside the OnLayout method.

Update the OnLayout method of HtmlLabel to this code (will include the fix in next version):
protected override void OnLayout(LayoutEventArgs levent)
{
if (_htmlContainer != null)
{
    if (AutoSize)
        _htmlContainer.MaxSize = SizeF.Empty;
    else if (AutoSizeHeightOnly)
        _htmlContainer.MaxSize = new SizeF(Width, 0);
    else
        _htmlContainer.MaxSize = Size;

    using (Graphics g = CreateGraphics())
    {
        _htmlContainer.PerformLayout(g);

        if (AutoSize || _autoSizeHight)
        {
            if (AutoSize)
            {
                Size = Size.Round(_htmlContainer.ActualSize);
                if (MaximumSize.Width > 0 && MaximumSize.Width < _htmlContainer.ActualSize.Width)
                {
                    // to allow the actual size be smaller than max we need to set max size only if it is really larger
                    _htmlContainer.MaxSize = MaximumSize;
                    _htmlContainer.PerformLayout(g);

                    Size = Size.Round(_htmlContainer.ActualSize);
                }
                else if (MinimumSize.Width > 0 && MinimumSize.Width > _htmlContainer.ActualSize.Width)
                {
                    // if min size is larger than the actual we need to re-lyout so all 100% layouts will be correct
                    _htmlContainer.MaxSize = new SizeF(MinimumSize.Width, 0);
                    _htmlContainer.PerformLayout(g);

                    Size = Size.Round(_htmlContainer.ActualSize);
                }
            }
            else if (_autoSizeHight)
            {
                var prevWidth = Width;
                Height = (int) _htmlContainer.ActualSize.Height;

                // handle if changing the height of the label affects the desired width and those require re-layout
                if(prevWidth != Width)
                    OnLayout(levent);
            }
        }
    }
}
base.OnLayout(levent);
}
Sep 10, 2013 at 9:19 AM
Thanks a lot. Works just as expected and definitely a more elegant solution than my workaround.