Custom Legend Position Issues
Posted: Tue Mar 08, 2011 6:15 pm
Hi,
I'm capturing the OnGetLegendRect event so as to position it according to my users' GUI-dictated whims. It mostly seems to work but I'm seeing some graphical issues that I suspect are caused by my not refreshing correctly or something similar. The user defines the desired location by specifying a base position (Top or bottom and left or right) and then an offset from that position. This position can either be relative to the plot boundary or the entire window.
First, if I switch the legend position from left to right (or vice-versa) the *rectangle* changes position but the text and legend symbols do not. See figure A. The legend appears correctly the next time the plot is repainted.
Secondly, if the user chooses to place the legend outside of the plot boundary then only the portion of the legend still drawn *inside* of it is displayed. The rest appears as a black rectangle. See figure B.
Finally, it's possible to break the legend completely but I don't know how it's happening. It usually occurs when I switch from inside the plot boundary to outside then back again. The legend disappears and cannot be recovered because the OnGetLegendRect event is no longer received. I've TRACEd the rectangle returned for the legend position and it always makes sense; i.e. Top < Bottom, Left < Right, Top >= 0, Left >= 0, and neither Bottom nor Right exceed the window boundary. No exceptions seems to be thrown either.
Whenever the legend settings are changed, I call Repaint on the parent plot to get it to fire the event. I've tried GetEnvironment().InternalRepaint() but it doesn't always work. In fact, it usually doesn't. Sometimes the window paints and sometimes it doesn't.
Unfortunately, I can't easily provide any compilable sample code because our plots are built as a DLL module loaded by another program and I can't give you anything from the parent. If it would help, I could to try to put together a small test program out of the relevant sections. For now, here's the function called by the OnGetLegendRect handler. Our windows contain multiple plots so the event is actually received by the parent and sent down to the proper individual plot.
I'm capturing the OnGetLegendRect event so as to position it according to my users' GUI-dictated whims. It mostly seems to work but I'm seeing some graphical issues that I suspect are caused by my not refreshing correctly or something similar. The user defines the desired location by specifying a base position (Top or bottom and left or right) and then an offset from that position. This position can either be relative to the plot boundary or the entire window.
First, if I switch the legend position from left to right (or vice-versa) the *rectangle* changes position but the text and legend symbols do not. See figure A. The legend appears correctly the next time the plot is repainted.
Secondly, if the user chooses to place the legend outside of the plot boundary then only the portion of the legend still drawn *inside* of it is displayed. The rest appears as a black rectangle. See figure B.
Finally, it's possible to break the legend completely but I don't know how it's happening. It usually occurs when I switch from inside the plot boundary to outside then back again. The legend disappears and cannot be recovered because the OnGetLegendRect event is no longer received. I've TRACEd the rectangle returned for the legend position and it always makes sense; i.e. Top < Bottom, Left < Right, Top >= 0, Left >= 0, and neither Bottom nor Right exceed the window boundary. No exceptions seems to be thrown either.
Whenever the legend settings are changed, I call Repaint on the parent plot to get it to fire the event. I've tried GetEnvironment().InternalRepaint() but it doesn't always work. In fact, it usually doesn't. Sometimes the window paints and sometimes it doesn't.
Unfortunately, I can't easily provide any compilable sample code because our plots are built as a DLL module loaded by another program and I can't give you anything from the parent. If it would help, I could to try to put together a small test program out of the relevant sections. For now, here's the function called by the OnGetLegendRect handler. Our windows contain multiple plots so the event is actually received by the parent and sent down to the proper individual plot.
Code: Select all
void CTChartEx::GetLegendRect(CRect &legRect)
{
CRect baseRect;
COleControlSite *kludge;
long height = legRect.Height(), width = legRect.Width();
m_Settings.LegendOffsetX = __max(0, m_Settings.LegendOffsetX);
m_Settings.LegendOffsetY = __max(0, m_Settings.LegendOffsetY);
if(m_Settings.bExternal)
{
/* Okay, let me explain this crap. ScreenToClient calls GetExStyle(). *
* If m_pCtrlSite is not NULL then the COleControlSite version of that *
* function asks the control for its DISPID_APPEARANCE property. This *
* causes a DISP_E_MEMBERNOTFOUND exception to be thrown by TeeChart. *
* So I trick Windows into calling the regular version which avoids the *
* exception and still gets me the correct rectangle. -MST, 04Mar2011 */
kludge = m_pCtrlSite;
m_pCtrlSite = NULL;
GetWindowRect(baseRect);
ScreenToClient(baseRect);
m_pCtrlSite = kludge;
}
else
{
TeeRectToCRect(GetGetChartRect(), baseRect);
}
if(m_Settings.bBottom)
{
if(m_Settings.bLeft)
{
// Bottom-Left
legRect.top = baseRect.bottom - height - m_Settings.LegendOffsetY;
legRect.left = baseRect.left + m_Settings.LegendOffsetX;
legRect.bottom = legRect.top + height;
legRect.right = legRect.left + width;
}
else
{
// Bottom-Right
legRect.top = baseRect.bottom - height - m_Settings.LegendOffsetY;
legRect.left = baseRect.right - width - m_Settings.LegendOffsetX;
legRect.bottom = legRect.top + height;
legRect.right = legRect.left + width;
}
}
else
{
if(m_Settings.bLeft)
{
// Top-Left
legRect.top = baseRect.top + m_Settings.LegendOffsetY;
legRect.left = baseRect.left + m_Settings.LegendOffsetX;
legRect.bottom = legRect.top + height;
legRect.right = legRect.left + width;
}
else
{
// Top-Right
legRect.top = baseRect.top + m_Settings.LegendOffsetY;
legRect.left = baseRect.right - width - m_Settings.LegendOffsetX;
legRect.bottom = legRect.top + height;
legRect.right = legRect.left + width;
}
}
}