I want to replace the Expand/Collapse icon with my icons in wxDataViewCtrl.
I am not able to find a way to set my expand/collapse icons.
Is there any API available for the same?
or
Do I need to modify the wxDataViewCtrl source directly? your prior experience can help me.
The main point of having wxDataViewCtrl in the first place is that it wraps the corresponding native control, so changing its look and feel is not supported.
This being said, if you're using its generic implementation, as is always the case under MSW, you can actually change it by defining a custom wxRendererNative-derived class and overriding its DrawTreeItemButton() method. But this would affect all the other controls drawing tree-like buttons, including, obviously, generic wxTreeCtrl itself but also wxPropGrid. Generally speaking I wouldn't recommend doing this.
I do not know whether it is right approach or wrong, Here I found a way to achieve my thing based on VZ.suggestion.
class MyRenderer : public wxDelegateRendererNative
{
public:
MyRenderer() : wxDelegateRendererNative(wxRendererNative::GetDefault()) { }
virtual void DrawTreeItemButton(wxWindow *win,
wxDC& dc,
const wxRect& rect,
int flags = 0)
{
MyCustomCtrl* pWin = dynamic_cast<MyCustomCtrl*>(win->GetParent());
if (pWin != nullptr)
{
// Draw my choice of expand/collapse button.
}
else
{ // Do not affect other controls that are using this drawing.
wxRendererNative::GetDefault().DrawTreeItemButton(win, dc, rect, flags);
}
}
};
This way I can override the
virtual void DrawItemSelectionRect(wxWindow *win,
wxDC& dc,
const wxRect& rect,
int flags)
and many more native drawings.
Related
I would like to have a vertical progressbar with vertical (top-to-bottom) text inside it. I wonder if and how can this be achieved with style sheets. I can`t change style of the whole application or completely change it for the widget ( no "apply plastic style solution available). Could someone provide me with some example? If you know any other way to achieve this it will be helpful as well.
Proposed answer is to complicated. It can be done in much more simple way (less code).
First of all you should subclass QProxyStyle.
This should be done more or less like that (override drawControl):
class MyProxyStyle : public QProxyStyle
{
public:
void QStyle::drawControl(ControlElement element, const QStyleOption * option, QPainter * painter, const QWidget * widget = 0) const
{
if (element == CE_ProgressBarLabel) {
// create coppy of style option:
QStyleOptionProgressBar op(*static_cast<QStyleOptionProgressBar*>(option));
// prepare style option for rotation
op.rect = QTransform().rotate(-90).mapRect(op.rect);
// optional: op.orientation = (op.orientation==Qt::Horizontal)?Qt::Vertical:Qt::Horizontal;
painter->rotate(90); // rotate painter
QProxyStyle::drawControl(element, &op, painter, widget);
painter->rotate(-90); // restore painter state - its a must
return;
}
QProxyStyle::drawControl(element, option, painter, widget);
}
};
It is possible that I've messed up with angles, but general concept should be clear.
Note that this is much better approach since:
code is simple
you do not break style, if style will be changed all should look an fill right (you are not using hard coded painting of label).
I think the best way to do it is just to subclass QProgressBar, not using stylesheet.
You have a similar example here with QScrollbar, hope it will help ;)
Draw text on scrollbar
A small parenthesis, if you want to apply a stylesheet to only one component, you have to write something like this:
widgetType#widgetName
{
...
}
I have a code that continuously draws lines. The problem is that the lines keep flickering every time form is refreshed. I heard I should use double buffering, but how to do it? I'm using c++ builder 2010. Here is my code:
void __fastcall TForm2::PaintBox1Paint(TObject *Sender)
{
Form2->Refresh();
TPoint P;
::GetCursorPos( &P );
P = ScreenToClient( P );
int XX;
int YY;
if (P.x<240)
{
XX=15;
YY= ((445-P.y)*(XX-P.x)/(240-P.x)+P.y);
}
else if(P.x==240)
{
XX=240;YY=-5;
}
else
{
XX=465;
YY= ((445-P.y)*(XX-P.x)/(240-P.x)+P.y);
}
int delta=2*(445-YY);
this->Canvas->MoveTo(241,445);
this->Canvas->LineTo(XX,YY);
while(0<YY&&YY<480&&YY!=445)
{
XX=abs(480-XX);
YY-=delta;
this->Canvas->LineTo(XX,YY);
}
}
Certainly you need to remove the call to Form2->Refresh. That asks the form to repaint itself immediately. That cannot help.
Secondly, your code handles the OnPaint event of a TPaintBox control. You are expected to paint on the canvas of TPaintBox rather than the form's canvas. Change all references to this->Canvas to instead refer to PaintBox1->Canvas. You may also need to correct the coordinates used to when painting.
Alternatively you could keep your existing code, and remove the paint box altogether. In which case take your current code and attach it to the form's OnPaint handler instead.
As for double buffering, you could let the VCL do it for you. Set the form's DoubleBuffered property to true. That is all you need to do, although be warned that it can have undesirable consequences on the visual appearance of certain control in certain themes. Be alert for any problems.
If you want to do the double buffering your self it is easy enough. Create a TBitmap. Set its size appropriately. Draw your lines to the Canvas of the bitmap. Then draw the bitmap onto the paint box canvas, or the form's canvas.
I am trying to dynamically change the text of a CStatic control. My member variable is called mStatic of the type CStatic. I have changed the ID to IDC_MYSTATIC instead of IDC_STATIC.
I am calling mStatic.SetWindowText("asdfasdf") when I want to change the text of the control. I do this periodically in a timer.
Now I have the problem that the previous text is not erased after I call the SetWindowText(). It just keeps piling up until I get a mess on the screen.
The parent window has the layered property with a bitmap background. I have also set the color_key property so a certain color of the bitmap is viewed as transparent (I.e. It will not be drawn and will let mouse messages through). The mStatic control is drawn on the parts not transparent, that have a bitmap background.
Why isn't the window invalidating?
Had the same issue. The following code fixed it:
mStatic.SetWindowText("New text");
CRect rect;
mStatic.GetWindowRect(&rect);
ScreenToClient(&rect);
InvalidateRect(&rect);
UpdateWindow();
Perhaps your static text control have a SS_SIMPLE style enabled. You can check style flags on resource file or using GetStyle().
Static control with SS_SIMPLE style displays text faster, but also - as MSDN describes -
"SS_SIMPLE static controls do not clear the control's display area when displaying text. If a shorter string is displayed, the part of the original string that is longer than the new shorter string is displayed."
Clear SS_SIMPLE from style flags and CStatic will behave 'normally'.
This knowledge base support article describes the same problem when the SetWindowText() call is made from another thread. Is that what your timer is doing?
If so the solution could simply be to:
mStatic.SetWindowText("asdfasdf");
CRect clientRect;
mStatic.GetClientRect(clientRect);
mStatic.InvalidateRect(clientRect);
As mentioned by others already, a static control doesn't necessarily erase its background prior to drawing the text.
I find it a much better solution to subclass the static control and force the invalidation of the control from there. This enables one to easily implement it on all static texts with transparent background, without having to do extra calls to invalidate the control from its parent class.
One way to catch a change of the control's text from within the control itself is to react to the WM_SETTEXT message and force the invalidation from there:
int CStaticT::OnSetText(LPCTSTR text)
{
LRESULT res = Default();
Invalidate();
UpdateWindow();
return res;
}
Here is a brief example, extracted from one of my classes, of how such a subclassed control could look like:
//////////////////////////////////////////////////////////////////////////
// Header
//////////////////////////////////////////////////////////////////////////
class CStaticT : public CStatic
{
DECLARE_DYNAMIC(CStaticT)
public:
CStaticT();
virtual ~CStaticT();
protected:
afx_msg int OnSetText(LPCTSTR text);
DECLARE_MESSAGE_MAP()
private:
BOOL m_InitialSet;
};
//////////////////////////////////////////////////////////////////////////
// Implementation
//////////////////////////////////////////////////////////////////////////
IMPLEMENT_DYNAMIC(CStaticT, CStatic)
CStaticT::CStaticT()
{
m_InitialSet = FALSE;
}
CStaticT::~CStaticT()
{
}
BEGIN_MESSAGE_MAP(CStaticT, CStatic)
ON_WM_SETTEXT()
END_MESSAGE_MAP()
int CStaticT::OnSetText(LPCTSTR text)
{
LRESULT res = Default();
// I've noticed issues when this forces the invalidation
// of the static control before the parent's background
// is painted completely.
// This is a cheap workaround, skipping the initial setting
// of the text by the subclassing call.
// You have to test if this works out for your environment.
if (!m_InitialSet)
{
m_InitialSet = TRUE;
return res;
}
// Force of the invalidation
Invalidate();
UpdateWindow();
return res;
}
The MFC ribbon bar has a menu item labelled 'Minimize the ribbon'. When you select it, only the headers of each category are shown, and the ribbon pops up when the headers are clicked. I'd like to programmatically force a ribbon into this state. Unfortunately, the only method I can find is ToggleMimimizeState() [sic], which will either put it into this state or take it out depending on its current state.
Looking at the MFC source code, the way the menu command works is through this code:
case idMinimize:
if (m_pActiveCategory != NULL)
{
ASSERT_VALID(m_pActiveCategory);
m_pActiveCategory->ShowElements(FALSE);
RedrawWindow();
}
m_pActiveCategory can be obtained from outside of the CMFCRibbonBar class through the GetActiveCategory() method, but unfortunately the category's ShowElements() method is protected and I cannot see a way of achieving the same effect with the public methods.
Neither does there seem to be an obvious way of determining whether the ribbon is currently minimized.
Is there something I'm missing, or do I just have to guess at the current state?
Derive two new classes from CMFCRibbonBar and CMFCRibbonCategory
class MyCMFCRibbonCategory: public CMFCRibbonCategory
{
public:
void force_ShowElements(BOOL todo)
{
ShowElements(todo);
}
};
class MyRibbonBar: public CMFCRibbonBar
{
public:
BOOL is_minimized()
{
return m_dwHideFlags == AFX_RIBBONBAR_HIDE_ELEMENTS;
}
void minimize_me(BOOL show_minimized)
{
MyCMFCRibbonCategory* cc = (MyCMFCRibbonCategory*)GetActiveCategory();
if (cc != NULL)
{
cc->force_ShowElements(!show_minimized);
RedrawWindow();
}
}
};
then change in your CMainframe from
CMFCRibbonBar m_wndRibbonBar;
to
MyRibbonBar m_wndRibbonBar;
Now in your code you can use the new two members:
BOOL MyRibbonBar::is_minimized()
void MyRibbonBar::minimize_me(BOOL show_minimized)
Basic example:
void CMainFrame::OnButton2()
{
if( m_wndRibbonBar.is_minimized() )
m_wndRibbonBar.minimize_me(FALSE);
else
m_wndRibbonBar.minimize_me(TRUE);
}
Hope it can help.
A combination of the above worked for me. That is, I wanted to use the Ribbon as a tabbed set of extra functions on a main menu. However, I didn't want the ribbon to have the ability to stay maximized. I only wanted the user to click, see a few actions and after that, disappear.
In short, prevent the ribbon from docking, or staying maximized. Whatever you want to call it. Click a tab, then and icon on the ribbon and disappear.
Instructions:
I derived my own CMyRibbon class by inheriting from CMFCRibbonBar. (Done using Class wizard and making an MFC class)
Create an event handler for WM_SIZE in our new CMyRibbon class (ClassWizard)
void CMyRibbon::OnSize(UINT nType, int cx, int cy)
{
CMFCRibbonBar::OnSize(nType, cx, cy);
if (!(GetHideFlags() & AFX_RIBBONBAR_HIDE_ELEMENTS))
ToggleMimimizeState();
}
In CMainFrm.h add this change:
CMyRibbon m_wndRibbonBar;
Use m_wndRibbonBar.ToggleMimimizeState();
Simply check (m_wndRibbonBar.GetHideFlags() & AFX_RIBBONBAR_HIDE_ELEMENTS) value.
I am using CWinFormsControl to host a Windows Forms UserControl in an MFC dialog. I have set the property DoubleBufferd to true. According to the docs this results in AllPaintingInWmPaint and UserPaint to be set to true too (not sure if this matters). How can I force (or fake) the UserControl to draw its background transparent?
This is what I have set in the contructor of my UserControl:
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.BackColor = Color.Transparent;
this.DoubleBuffered = true;
I have a potential solution that may work, although I would need more information on how your animated controls work to be sure. There is one unfortunate side effect in my solution, and that is that the DoubleBuffering property only works correctly in .NET control containers. When hosted in MFC, your controls will flicker on resize and other similar display-tearing refreshes. This may cause issues with animated controls, depending on how they are performing drawing work.
To start, I first looked for issues when hosting a .NET UserControl in MFC. After quite a while of reading through the instantiation code of CWinFormsControl::CreateControl() and everything beneath, nothing out of the ordinary came up. In fact, aside from the quirks of loading managed references, the code is identical to how transparent ActiveX controls are loaded.
After learning that piece of information, I used Spy++ to look at whether the .NET control is instantiated with a windowed container. Indeed, it is. After a rather lengthy investigation, this control container appears to be controlled by an instance of a utility class, System.Windows.Forms.Control.AxSourcingSite, which has no documentation and almost no visibility. This was a little bit surprising to me, as usually it is the reverse. MFC and the lesser used WTL have great support for in-place activation, and usually controls can work with whatever the host has setup, whether windowed or not.
From here, I checked on whether this same container exists when the .NET control is hosted in a .NET control container. I assumed that perhaps the control would have its own window, without any special adapters. Turns out, I was wrong. The control works the same way as in-place non-windowed controls. This means that in order to preserve behavior, a solution must allow the regular .NET activation to proceed as normal, and when windowed, it should do something else.
A close look at the MFC hosted version reveals an off-white background drawn in by the .NET UserControl. After more spading and testing, this off-white background is definitely drawn in by a hidden layer in the window message handling chain. This means we can hack together a solution by using AllPaintingInWmPaint.
To demonstrate this, here is the source code for a UserControl that can be hosted in both .NET and the MFC managed container. This control relies on the following things to work around the transparency issues.
Add a member variable, m_ReroutePaint, to allow us to know when we need to override the default WM_PAINT behavior.
Override base.CreateParams and add the WS_EX_TRANSPARENT flag. When this property is called, set m_ReroutePaint to true. This property was not called when the Control is activated in a .NET container.
Override the WndProc() method, and patch up WM_PAINT to our liking if we are rerouting painting activities.
Use BeginPaint()/EndPaint() via Interop to setup/teardown WM_PAINT. Use the provided HDC as the initializer for a Graphics object.
Here are some caveats:
The background color of the control cannot be changed through the BackColor .NET property after the control has been instantiated. One can add workarounds for this, but to keep the sample short and simple, I left out code to do this as the intended goal is for transparent controls. However, if you start with a background color that isn't transparent, the workaround is unnecessary. I did leave code in for this case.
In attaching a HDC to a Graphics object in the WM_PAINT handler via Graphics.FromHdc(), the documentation suggests that Graphics.ReleaseHdc() should be called. However, by doing this, a GDI handle leak occurs. I have left it commented out here, but perhaps someone with GDI+ internals knowledge can figure this out.
This UserControl was created in a project named 'UserCtrlLibrary1'. The DebugPrintStyle() items may be safely removed. Also, handlers were added for resize and paint, which are both in a separate designer file, but trivial to add. AllPaintingInWmPaint should be true through the lifetime of the control.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace UserCtrlLibrary1
{
public partial class CircleControl : UserControl
{
public CircleControl()
{
InitializeComponent();
DebugPrintStyle(ControlStyles.SupportsTransparentBackColor, "initial");
DebugPrintStyle(ControlStyles.AllPaintingInWmPaint, "initial");
DebugPrintStyle(ControlStyles.UserPaint, "initial");
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.UserPaint, true);
DebugPrintStyle(ControlStyles.SupportsTransparentBackColor, "current");
DebugPrintStyle(ControlStyles.AllPaintingInWmPaint, "current");
DebugPrintStyle(ControlStyles.UserPaint, "current");
}
public void DebugPrintStyle(ControlStyles cs, string prefix)
{
Debug.Print("{0}: {1}={2}", prefix, cs.ToString(), this.GetStyle(cs).ToString());
}
bool m_ReroutePaint;
const int WS_EX_TRANSPARENT = 0x0020;
protected override CreateParams CreateParams
{
get
{
if (this.BackColor == Color.Transparent)
{
m_ReroutePaint = true;
CreateParams cp = base.CreateParams;
cp.ExStyle |= WS_EX_TRANSPARENT;
return cp;
}
else
{
return base.CreateParams;
}
}
}
private void CircleControl_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
using (SolidBrush b = new SolidBrush(Color.Orange))
{
g.FillEllipse(b, 0, 0, this.Width, this.Height);
}
}
private void CircleControl_Resize(object sender, EventArgs e)
{
this.Invalidate();
}
const int WM_PAINT = 0x000F;
[DllImport("user32.dll")]
static extern IntPtr BeginPaint(IntPtr hwnd, out PAINTSTRUCT lpPaint);
[DllImport("user32.dll")]
static extern bool EndPaint(IntPtr hWnd, [In] ref PAINTSTRUCT lpPaint);
[Serializable, StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
[StructLayout(LayoutKind.Sequential)]
struct PAINTSTRUCT
{
public IntPtr hdc;
public bool fErase;
public RECT rcPaint;
public bool fRestore;
public bool fIncUpdate;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public byte[] rgbReserved;
}
protected override void WndProc(ref Message m)
{
if ((m.Msg == WM_PAINT) && (m_ReroutePaint))
{
PAINTSTRUCT ps = new PAINTSTRUCT();
BeginPaint(this.Handle, out ps);
using (Graphics g = Graphics.FromHdc(ps.hdc))
{
using (PaintEventArgs e = new PaintEventArgs(g, new Rectangle(ps.rcPaint.Left, ps.rcPaint.Top, ps.rcPaint.Right - ps.rcPaint.Left, ps.rcPaint.Bottom - ps.rcPaint.Top)))
{
this.OnPaint(e);
}
// HACK: This is supposed to be required...
// but it leaks handles when called!
//g.ReleaseHdc(ps.hdc);
}
EndPaint(this.Handle, ref ps);
return;
}
base.WndProc(ref m);
}
}
}
In case anyone other than the OP would like to test this, here are the details to get this up and running in MFC. I created a MFC SDI project, without document-view architecture, with ActiveX control support. This results in generation of typical «project-name» class, ChildView class, and MainFrm classes.
Inside the ChildView.h header, add the following header material before the class (but after #pragma once). Alter the name of the .NET control library if yours is different.
#include <afxwinforms.h>
#using "UserCtrlLibrary1.dll"
using namespace UserCtrlLibrary1;
Add a member variable for the .NET control host. Arbitrarily, I placed mine under the Attributes section.
// Attributes
public:
CWinFormsControl<CircleControl> m_Circle;
Also, I added handlers for OnCreate() and OnSize(). public/protected visibility may be adjusted as you need.
// Generated message map functions
protected:
afx_msg void OnPaint();
DECLARE_MESSAGE_MAP()
public:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnSize(UINT nType, int cx, int cy);
In ChildView.cpp, I added function bodies for all the items listed above. The message map also needs updates if you didn't use ClassWizard to add the windows message handlers.
BEGIN_MESSAGE_MAP(CChildView, CWnd)
ON_WM_PAINT()
ON_WM_CREATE()
ON_WM_SIZE()
END_MESSAGE_MAP()
void CChildView::OnPaint()
{
CPaintDC dc(this); // device context for painting
RECT rt;
this->GetClientRect(&rt);
rt.right = (rt.right + rt.left)/2;
dc.FillSolidRect(&rt, RGB(0xFF, 0xA0, 0xA0));
}
int CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CWnd::OnCreate(lpCreateStruct) == -1)
return -1;
RECT rt;
this->GetClientRect(&rt);
m_Circle.CreateManagedControl(WS_VISIBLE, rt, this, 1);
return 0;
}
void CChildView::OnSize(UINT nType, int cx, int cy)
{
CWnd::OnSize(nType, cx, cy);
RECT rt;
this->GetClientRect(&rt);
m_Circle.MoveWindow(rt.left, rt.top, rt.right - rt.left, (rt.bottom - rt.top)/2, TRUE);
}
These changes create an instance of the UserControl, and anchor it against the top half of the view. The OnPaint() handler draws a pink band in the left half of the view. Together, transparency should be apparent in the top left quadrant of the view.
To get the MFC project to compile and run, a copy of the UserCtrlLibrary1 output needs to be placed in the same location as the executables for UserCtrlMFCHost. Also, another copy needs to be placed in the same directory as the project source code files for the #using statement. Last, the MFC project should be modified to use the /clr compilation script. In the Configuration Properties section, General subsection, this switch is listed under Project Defaults.
One interesting thing of note, is that this allows the ^ suffix for access to managed classes. At some points in developing this solution, I debated adding methods to be called only when instantiated from MFC, but given that there are ways to detect windowed/non-windowed activation, this wasn't necessary. Other implementations may need this, though, so I feel it is good to point this out.
How to: Compile MFC and ATL code with /clr