TAdvSmoothExpanderButtonPanel glitches...any help?

I try to describe what i'm doing, and what problem i have, hoping to have some solution or workaround for them.

I have a SmoothPanel client aligned in a TFrame. Call this panel "panContainer".
panContainer contains

  • a TAdvSmoothTrackBar aligned right, which i use like a scrollbar 'cause its better visual customization: call it "ScrollBar".
  • a TAdvSmoothPanel Client aligned, called "ScrollPanel"
ScrollPanel contains a TAdvSmoothExpanderButtonPanel, called formatsPanel.

This whole thing is a Format selector (maybe "Brands" is a better word than Formats?) for an automation machinery. At runtime I read a file, then i add a button in the formatsPanel for each entry found in that file, with the picture also drawn at runtime to show the Format.
If the formatsPanel height is more than the ScrollPanel ClientHeight  then i show the ScrollBar and i use its position to change the formatsPanel top, basically emulating a TScrollBox.
I hope you get it.

Now, the problems:
  1. the formatsPanel is slow: when it have around 80 buttons in it, there is almost 1 second between the mouse click and the clicked button repaint showing the "down" state. I don't know why it requires so much time to paint a single button when they are a lot.... anyway it is unacceptable: even if it does not affect the functionality, in can't show to my customers an app with a lag of one second between the click and the "button down" repaint.
  2. There's a way to prevent the formatsPanel from repainting itself until i've finished the loop where i add the buttons?
  3. "Scrolling" the panel generates a lot of flickering. What's the best method to avoid it? I tried to set the Doublebuffered property to the formatsPanel itself, or to the scrollPanel (its container) without any result.
I want to add also some suggestion/complaints, to give my feedback about tthis component:
  • Feature: Buttons lacks a Tag property. Sometime the index is'nt just enough, and EVERY component i have have a Tag property.
  • Naming: OnButtonClick and OnButtonClicked imho are wrong names for the events they represent. The OnButtonClick is fired at the mouse down. so its name should be "OnButtomMouseDown", while the OnButtonClicked is really a OnButtonClick. It also lacks (always, IMHO) for a OnButtonMouseUp event.
Thanks for any suggestion.

I am not able to reproduce these errors (included sample), everything works as expected. Please send us a sample to demonstrate the slow performance and / or describe the exact steps and / or properties to reproduce this issue.


About suggestions/complaints:

I have added the Tag property which will be available in the next update.
The remaining issues are added on our todolist for investigation.


Kind Regards, 
Scheldeman Pieter

Pieter Scheldeman2010-07-15 08:48:06

Here it is:
http://dl.dropbox.com/u/2651363/GroupPanelTest.zip

There is the whole CBuilder project as long as the compiled exe.

Generating 100 buttons, and enlarging the windows to my full desktop (1920 x 1280) lead to 56 - 80 msec between the mouse action and the button OnDraw event.

Any news?

We have investigated this but the controls are not designed to perform with equal power to normal windowed applications. Its is normal that Maximized applications and smooth controls combination performance will result in slower behavior. The Smooth Controls use complex gradients and calculations to provide the best look with GDI+.


Using the same app, with100 buttons, with the app window large enough to show only one button leads to 650msec between the click and the redraw. This lag is more related to the amount of buttons added to the panel than to the windows size.
So i don't really understand your reply: whatever the complex gradient routines do, it is always a matter of drawing a single button in its changed state. I have not see any "lag" in any of the TAdvSmooth(Toggle)Button i use in my project. Why they should become slower when "grouped" on a panel?

The buttons inside the panel are cached, only the selected button is redrawn. if no caching was applied the repaint and reponsiveness would be worse.

Investigating your sample shows indeed that the time clicking a button is slow. This is due to the panels maximized and the shadow of the panels. Creating fast and reponsive applications can be done with smaller buttons, smaller panels, less shadows and less gradient colored appearances. Please note that using 3 or more TAdvSmoothPanels aligned inside the client area will result in slower applications. Using heavy color / shadow / gradients in GDI+ reduces the reponsiveness.



I removed from the test app you have everything but the ExpanderButtonPanel, It is not aligned to anything. Fill property have every color set to none but the StartColor set to gray. GradientType = solid. All other entries (rounding, radius, shadows etc) set to 0 or none. It just have the caption visible as shown in that app.

This is the final code, to show what it remains in the app:
GroupPanelTest.h


//---------------------------------------------------------------------------
#ifndef GroupPanelTestH
#define GroupPanelTestH
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include "AdvSmoothExpanderButtonPanel.hpp"
#include "AdvSmoothExpanderPanel.hpp"
#include "AdvSmoothPanel.hpp"
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:    // IDE-managed Components
    TAdvSmoothExpanderButtonPanel *formatPanel;
    void __fastcall formatPanelButtonClick(TObject *Sender, int ButtonIndex);
    void __fastcall formatPanelButtonClicked(TObject *Sender, int ButtonIndex);
    void __fastcall formatPanelDraw(TObject *Sender, TCanvas *Canvas, TRect &Rect);
private:    // User declarations
public:        // User declarations
    __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif



GroupPanelTest.cpp


//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "GroupPanelTest.h"
#pragma resource "*.dfm"
//---------------------------------------------------------------------------
#pragma package(smart_init)
TForm1 *Form1;
//---------------------------------------------------------------------------
LARGE_INTEGER    startTime64, endTime64, frequency64;
Double    elapsedSeconds;
bool    MeasuringTime;
AnsiString sTime;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner): TForm(Owner)
{
    TAdvSmoothExpanderButton*    bp;
    Graphics::TBitmap*            bmp;
    TRect                        ARect;
    int i;
    AnsiString capt;

    QueryPerformanceFrequency(&frequency64);    // Read timer frequency

    bmp = new Graphics::TBitmap();
    bmp->SetSize(180,120);
    bmp->Transparent = false;

    formatPanel->Buttons->Clear();
    formatPanel->Top = 0;
    formatPanel->Height = 50;

    for (i=0; i<100; i++) {
        bp = formatPanel->Buttons->Add();
        capt.printf("Format %d",i);
        bp->Caption = capt;

        bmp->Canvas->Brush->Color = RGB(Random(255), Random(255), Random(255));
        bmp->Canvas->FillRect(ARect);

        bp->Picture->Assign(bmp);

    }
    delete bmp;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::formatPanelButtonClick(TObject *Sender, int ButtonIndex)
{
    OutputDebugString("ButtonClick");
    MeasuringTime = true;
    QueryPerformanceCounter(&startTime64);    // Read current time
}
//---------------------------------------------------------------------------
void __fastcall TForm1::formatPanelButtonClicked(TObject *Sender,
      int ButtonIndex)
{
    OutputDebugString("ButtonClicked");
    MeasuringTime = true;
    QueryPerformanceCounter(&startTime64);    // Read current time
}
//---------------------------------------------------------------------------
void __fastcall TForm1::formatPanelDraw(TObject *Sender, TCanvas *Canvas, TRect &Rect)
{
    if (MeasuringTime) {
        // Time measuring: how much seconds between the mouse event (startTime64) and the first OnDraw event call
        QueryPerformanceCounter(&endTime64);    // Read current time
        MeasuringTime = false;
        elapsedSeconds = (Double)(endTime64.LowPart - startTime64.LowPart) / frequency64.QuadPart;
        sTime.printf("%f sec",elapsedSeconds);
        formatPanel->Caption->Text = sTime;
        OutputDebugString("OnDraw");
    }
}
//---------------------------------------------------------------------------


Window size: 600x500
panel size 550x450

What i get  from the click to the redraw:

0.85ms no matter the window size (this make sense: the panel width is fixed, the panel height depends on the number of buttons

If i make the panel Align = alClient:
  at 600x500 - 897ms (and it fails to draw all the buttons until i resize the form)
  maximized at 1980x1024 - 578ms
  reduced to 275x122 - 606ms

Removing the picture inside the buttons help
The lag become more stable in every situation and assets it around 450ms.

Now, some though:
this all is more or less like debugging your components for you.
Shadows, panel alignement or gradient fills does not change the lag a bit.
Is it obvious that your component are made to aesthetic in mind. While i understand that they require more resources and time to render, they also have to work.
Reducing the button size is not an option: they have to contains that pictures and they have to run on a full hd touchscreen.
I'm sorry if this seems harsh, i'm a programmer myself and i understand that thing can become "hard", but telling that i have to tune down all the candy features of a candy component to make it works (which have instead no effect) make me think that you have not investigate anything.
So, I think you should look inside your component, stress them a bit and find where all this time (800ms are a whole lot of time for modern PCs) goes.

As a final note, while i'm strongly complaining about this lag and the kind of answers i've got about it, i don't want to "hurt" anyone: my english is not so good, so i don't know how it "sounds". We are all working, we all deserve respect.
Sorry if i said something that makes you think different.

The height of the panel is over 6800 pixels (due to the large amount and size of the buttons). This causes the slow responsiveness when clicking a button. I have improved the panel to only draw the visible buttons so this should be a major improvement in the next version, that is if the size of the panel has normal values.


The height of the panel in combination with the TAdvSmoothTrackBar is a good approach, unfortunately the TAdvSmoothExpanderButtonPanel is originally not designed to work with such dimensions, therefore no scrollbar of any kind has been implemented. You could experiment by setting IncreaseHeight to false in the AutoSize property. After the update the reponsiveness will improve dramatically. Then again, the scrolling has to be handled in a different way.

Hoping for your understanding.

Kind Regards, 
Scheldeman Pieter