I want to draw my own TabControl with a different style. I have a TabControl in my form for which i have appended the event handler,
MainTabControl.DrawMode = TabDrawMode.OwnerDrawFixed
AddHandler MainTabControl.DrawItem, AddressOf TabControlPaint
TabControlPaint() is a method where i paint my own graphics using Bitmaps amd Buffer variables. (see here)
I'm not inheriting from TabControl because my Application has many forms and windows and that would force me to go to each form and change the TabControl for my CustomTabControl. I would like to be able to draw my own tabs overwriting the DrawItem event.
I came up with this result Screenshot, where you can see the red border, a different background color for the tabs, even different color schemes for a tab went selected.
What i can't figure out is to erase that white 3D border that appears only on the selected tab. I'm guessing is drawn on top when the OnPaint() method is called after the drawItem event (correct me if i'm wrong). If i debug, that white 3D border appears after the method TabControlPaint (the one i'm calling for the DrawItem event) has finished execution.
To make the question more clear, i want to design my own TabControl without overwriting the OnPaint method. What i have come up so far was not succesfull just because of that windows drawn border. Is there a way to take it off? Or do you know any alternatives?
I managed to solve the problem by,
For Each page As TabPage In MainTabControl.TabPages
AddHandler page.Paint, AddressOf test
Next
then in my test function i draw whatever i want. In this case i just drew a rectangle. It is not efficient but i think it can be improved by double buffering.
Private Sub test(ByVal sender As System.Object, ByVal e As PaintEventArgs)
Dim ScreenGraphic As Graphics = Graphics.FromHwnd(MainTabControl.Handle)
Dim myRect As Rectangle = MainTabControl.GetTabRect(MainTabControl.SelectedIndex)
Dim borderColor As Pen = New Pen(Color.FromArgb(42, 38, 37), 5)
myRect.X -= 1
myRect.Y -= 1
ScreenGraphic.DrawRectangle(borderColor, myRect)
End Sub
Related
I have a function which takes a rectangular region of a bitmap image, rescales it to different dimensions, and draws it at some offset inside of a window within my dialog-box application:
void DrawImage(HANDLE hImageBitmap,
CDC* pDstDC,const CRect& dstRect,
CDC* pSrcDC,const CRect& srcRect)
{
pSrcDC->SelectObject(hImageBitmap);
pDstDC->SetStretchBltMode(HALFTONE);
pDstDC->StretchBlt
(
dstRect.left,dstRect.top,dstRect.Width(),dstRect.Height(),pSrcDC,
srcRect.left,srcRect.top,srcRect.Width(),srcRect.Height(),SRCCOPY
);
}
I create and maintain the window using a CWnd m_cImageWindow member variable.
I perform the drawing from the dialog-box's OnPaint handler as follows:
CDC* pDC = m_cImageWindow.GetDC();
CDC cDC;
cDC.CreateCompatibleDC(pDC);
CRect srcRect = ...;
CRect dstRect = ...;
DrawImage(m_hImageBitmap,pDC,dstRect,&cDC,srcRect);
cDC.DeleteDC();
m_cImageWindow.ReleaseDC(pDC);
I have two problems:
I see flickering whenever I change the drawing parameters. The standard way to solve this, from what I have read here and there, is by using a temporary DC for double-buffering. But as far as I understand, this is exactly what I am already doing.
If some of the destination region falls outside the window, it is painted over other controls within the dialog box. I am able to partially solve this by calling MoveWindow or SetWindowPos for each one of these controls. But I can still see the image flickering behind them. I have tried calling SetWindowPos in various different ways, hoping in vain that it would dictate a strict Z-order of the controls.
Thank you.
The painting of the image into the child window should be done in the WM_PAINT handler for that child window, not for the dialog. Your child window may need remember information provided by the parent dialog so that it can paint independently. By painting the window from the dialog's WM_PAINT handler, you're possibly painting more often than necessary (and possibly aren't causing a validation to occur in the image window).
The dialog should probably have the WS_CLIPCHILDREN window style and your image window should probably have WS_CLIPSIBLINGS. This will prevent the dialog controls from drawing over each other, and it can reduce flicker by allowing for more minimal updates.
If the image will always completely cover the entire image window, then you want to make sure there's no background erasing happening for the image window, as that can cause a flash of the background color which looks like painting. There are several ways to do this, but the easiest is probably to provide a WM_ERASEBKGND handler that just returns TRUE.
I found OnEraseBkgnd to be the right place to minimize flickering of drawn bitmaps.
I'm faced with a problem where I need to display some characters in a tree-view item (those belonging to the Symbol charset) using Symbol font while others in the default System font (Segoi UI on my Windows 7).
Custom draw allows us to draw different items using different fonts, but I would like to draw the same item string using different fonts as it applies to each character in the string as told above.
So, what I've done with not-so-pleasing results w.r.t. drawing performance upon a horizontal scroll when the number of items is more so far is this:
I disabled horizontal scrolling in my tree-view control using TVS_NOHSCROLL style (since I'm using my own scroll bar control inside the tree-view window to handle all horizontal scrolling)
I sub-classed the treeview control and in the sub-classed winproc, I handle the horizontal scroll notification and mouse notification (where I do my own hittesting and send message like TVM_EXPAND and TVM_SELECT as a result of mosue clicks/double-clicks). Also the scroll bar range is set based on how wide my custom drawn string is (the maximum length amongst all items).
I draw the string for each item upon receiving CDDS_ITEMPOSTPAINT using my own fonts for each character in the item.
The above approach (I left out some details for the sake of brevity) works BUT there are some problems which makes me post this question here and look for an alternare way:
Problems:
The horizontal scroll bar control I create is hosted "inside" the tree-view control at the bottom of the tree-view window. However, when the number of items goes beyond what the tree-view client area can accommodate vertically, the last visible tree-view item gets obscured by the scroll bar control. This can be solved by not making the scroll bar a child of the tree-view and hosting it outside the tree-view window just below it. But I don't want to do this since the scroll bar should typically be a child window of the tree-view.
This is the major one. Since I draw the items myself at each horizontal scroll, the drawing performance upon horizontal scrolling is very slow and also leads to flicker upon scrolling.
Any ideas will be much appreciated as I've been grappling with this for the last one week without success.
I can also post the relevant code here if you want to see the approach I took but I'm sure there shoould be a better approach to this and there must be some other people who would've faced this problem and solved it in the past.
Thanks in advance.
Custom-draw allows you to draw items however you want. You are not limited to a single font per item. When you receive the NM_CUSTOMDRAW notification, draw whatever you want on the provided HDC for the specified item. You can draw pieces of text in one font, pieces of text in a different font, etc. Be sure to return CDRF_SKIPDEFAULT so the TreeView itself will not try to draw anything on the item.
#Anurag S Sharma: I tried to edit this into Remy's answer. It's incomplete as is, but addresses your comment/concerns and answers this particularly vexing/useful question...
The problem is that ff I return CDRF_SKIPDEFAULT, Windows does not even draw the +/- buttons (expanding/collapsing) nor the indent lines in the control which I do want Windows to draw. – Anurag S Sharma
To retain the lines, buttons, and icons you can use ExcludeClipRect to mask only the text region and instead of returning CDRF_SKIPDEFAULT, return 0 as if you didn't draw anything. This itself would not be necessary if the text of the tree item was empty, except that the margins of the text will always be drawn by the default handler (note that Microsoft's controls do not always respect clipping shapes, but in this case they do.)
To replicate the classic TreeView label style in your custom draw procedure you need to do something like the following:
HTREEITEM item = (HTREEITEM)p->dwItemSpec;
TreeView_GetItemRect(p->hdr.hwndFrom,item,&p->rc,1);
RECT cr, rc = p->rc; GetClientRect(p->hdr.hwndFrom,&cr);
DrawTextW(p->hdc,text,-1,&rc,DT_CALCRECT|DT_NOPREFIX|DT_NOCLIP);
rc.right+=4; rc.bottom+=2; IntersectRect(&rc,&cr,&rc);
ExtTextOutW(p->hdc,rc.left+2,rc.top+1,ETO_CLIPPED|ETO_OPAQUE,&rc,text,wcslen(text),0);
My Windows Application in VB 6 is having a form that contains hell lot of controls. And if the screen size at client's environment is smaller, most of the controls do not appear.
What I want to do is to provide users a vertical and horizontal scrollbar so that user can scroll across all controls.
Anyone having any idea of how to implement this?
P.S. Please do not provide examples showing just labels to display scroll value :)
I like David's answer, but if you want to do this with the scrollbars, first, you need to put all of your controls into a frame that fits them. You want to move 1 control not a "hell of a lot of controls". Second put your scroll bars into the form and in the Form_Resize event add some code to resize the scroll bars with the form. After resizing the scrollbar you need to do some math to set the Max, SmallChange, and LargeChange properties. I am showing the Min property just so you know it never changes, just set it in the designer. This example uses only a horizontal scrollbar because I am too lazy to include a vertical scrollbar too. Finally, add code to the scrollbar Change event to move the frame around.
Private Sub Form_Resize()
HScroll1.Move 0, Me.ScaleHeight - HScroll1.Height, Me.ScaleWidth
HScroll1.Min = 0
HScroll1.Max = Frame1.Width - Me.ScaleWidth
HScroll1.SmallChange = HScroll1.Max / 100
HScroll1.LargeChange = HScroll1.Max / 10
End Sub
Private Sub HScroll1_Change()
Frame1.Left = -HScroll1.Value
DoEvents ' this is not strictly necessary, but smooths the scolling some
End Sub
You also need error handling code. I am a lazy example coder.
One way is to turn on the scroll bars of your form using Windows API calls. This is different from using ScrollBar controls; turning on the form's own scroll bars keeps the scroll bars from interfering with the tab order, for example.
Here is a good page explaining how to do this, along with a helper class:
http://www.vbaccelerator.com/home/VB/Code/Libraries/Subclassing/Adding_Scroll_Bars_To_Forms__PictureBoxes_and_UserControls/article.asp
I think I have found a bug.
I am developing a add-in for Powerpoint 2010. The event WindowSelectionChange is fired when a shape (e.g. a picture) is selected/deselected.
However, if I use this event to change the Visibility property of a Custom Task Pane, then the shape moves left/right on the slide. Example:
Private Sub Application_WindowSelectionChange(Sel As Microsoft.Office.Interop.PowerPoint.Selection) Handles Application.WindowSelectionChange
cTaskPane.Visible = Not cTaskPane.Visible
End Sub
I have tried to monitor the Left property of the shape, and that does not change from the beginning to the end of the WindowSelectionChange sub. Thus, it must happen afterwards.
How can I avoid this?
Any workaround?
It's because your mouse is holding the shape, and the shape gets moved to the right when the window is shrunk.
In more detail, the moving occurs in 4 steps:
You press mouse, caused window selection change, then the pane becomes visible, which makes the slide view window shrink;
Since the slide view window shrunk while your mouse's position remains to be the same, your mouse is moved right w.r.t the slide;
Since your mouse is pressing, the shape is anchored with your mouse, thus move to the current position of the mouse;
When pane becomes invisible again, the slide view window changes its size back, the shape is moving accordingly again.
To avoid this, I suggest you use WindowSelectionChange event and check if the selection is shape (code is in c#):
private void WindowSelectionChangedHandler(PowerPoint.Selection selection)
{
if (selection.Type == PowerPoint.PpSelectionType.ppSelectionShapes)
{
//do your stuff
}
}
I have a CTabCtrl subclass which I'm trying overriding WM_PAINT to perform custom drawing. The only problem is, when I change the selected tab, I get artifacts left on the dialog where the old paint code hasn't been erased before the new code is painted on top. (The standard tab controls have the selected tab appear 2 pixels bigger than non-selected tabs, so when you change from selected to non-selected, you are left with the previous paint artifacts).
What is the best way of "repainting" this area? I've tried getting the parent control's DC and BitBlt'ing that onto the child's DC, but that doesn't work because the parent DC already contains an image of this control.
Do you override the WM_ERASEBACKGROUND message as well? You should probably do that and erase the area in the control.
You could use DrawThemeParentBackground to draw the background (XP and later) if you don't want to replace all the drawing logic.