一个TreeView的问题。给TTreeView或是VC的SysTreeView32加上背景图。在拖动滚动条时控件背景就一塌糊涂。找了好久没找到解决方法。希望有

pp616 2003-10-09 08:14:50
我的代码
void __fastcall TForm1::TreeView1CustomDraw(TCustomTreeView *Sender,
const TRect &ARect, bool &DefaultDraw)
{
if(!ImgBk->Empty)//Graphics::TBitmap *ImgBk为一个背景bmp
{
TreeView1->Canvas->Brush->Bitmap=ImgBk;
TreeView1->Canvas->FillRect(ARect);
}

void __fastcall TForm1::TreeView1CustomDrawItem(TCustomTreeView *Sender,
TTreeNode *Node, TCustomDrawState State, bool &DefaultDraw)
{
.....
  //代码比较多,为哥们看起来方便我就不贴了。
}
基本上和Examples\CustomDraw里的代码大同小异。
解决了闪烁和其他一些问题。但是就是在节点很多的时候拖动ScrollBar的时候背景图就一塌糊涂了。只有TreeView再次收到WM_PAINT的时候才会重画背景。好象网络蚂蚁和网际快车都解决了这问题。请知道答案的兄弟帮帮忙。万分感谢!!
...全文
1012 21 打赏 收藏 转发到动态 举报
写回复
用AI写文章
21 条回复
切换为时间正序
请发表友善的回复…
发表回复
pp616 2003-10-12
  • 打赏
  • 举报
回复
谢谢大家捧场
问题一定解决了。
我这么做的。原来的画的代码我就不多说了。相信这个大家都会。
.h中加上
TWndMethod oldWndProc;
void __fastcall TreeViewWndProc(TMessage& message);
.cpp
在FormCreate中加上
oldWndProc=TV->WindowProc;
TV->WindowProc=TreeViewWndProc;
然后子类化TreeView
void __fastcall TCustomDrawForm::TreeViewWndProc(TMessage& message)
{
switch (message.Msg)
{
case WM_VSCROLL:
TV->Invalidate();
oldWndProc(message);
break;
case WM_MOUSEWHEEL:
TV->Invalidate();
oldWndProc(message);
break;
case WM_ERASEBKGND:
message.Result = TRUE;
return;
default:
oldWndProc(message);
}
}

FormDestroy中加上
TV->WindowProc=oldWndProc;
domustdo 2003-10-12
  • 打赏
  • 举报
回复
我是从TTreeView下继承一个新的TreeView来实现的生成一个新的包,不过觉得很奇怪,函数TransparentBlt是winnt目录下的msimg32.dll里的输出函数,在delphi中可以使用,但是在BCB程序却链接不到。
该包的程序如下:

/* MyTreeView.h */

//-------------------------------------------------------------------------------
//---------------------------------------------------------------------------

#ifndef MyTreeViewH
#define MyTreeViewH
//---------------------------------------------------------------------------
#include <SysUtils.hpp>
#include <Classes.hpp>
#include <ComCtrls.hpp>
#include <Controls.hpp>
//---------------------------------------------------------------------------
class PACKAGE MyTreeView : public TTreeView
{
private:
Graphics::TBitmap *FBitmap;
void __fastcall SetBitmap(Graphics::TBitmap*);
void __fastcall PaintBkGrd();
void __fastcall MyWMPaint(TMessage &);
protected:
void __fastcall WndProc(TMessage &);
public:
__fastcall MyTreeView(TComponent* Owner);
__property Graphics::TBitmap* TVImage = {read = FBitmap, write = SetBitmap};
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_PAINT, TMessage, MyWMPaint);
END_MESSAGE_MAP(TComponent)
__published:
};
//---------------------------------------------------------------------------
#endif



/* MyTreeView.cpp */
//---------------------------------------------------------------------------

#include <vcl.h>
#include <wingdi.h>

#pragma hdrstop

#include "MyTreeView.h"
#pragma package(smart_init)
//---------------------------------------------------------------------------
// ValidCtrCheck is used to assure that the components created do not have
// any pure virtual functions.
//

static inline void ValidCtrCheck(MyTreeView *)
{
new MyTreeView(NULL);
}
//---------------------------------------------------------------------------
__fastcall MyTreeView::MyTreeView(TComponent* Owner)
: TTreeView(Owner)
{
FBitmap = new Graphics::TBitmap();
}
//---------------------------------------------------------------------------
namespace Mytreeview
{
void __fastcall PACKAGE Register()
{
TComponentClass classes[1] = {__classid(MyTreeView)};
RegisterComponents("Samples", classes, 0);
}
}
//---------------------------------------------------------------------------
void __fastcall MyTreeView::SetBitmap(Graphics::TBitmap* bitmap)
{
FBitmap = bitmap;
}

void __fastcall MyTreeView::MyWMPaint(TMessage &Msg)
{
PaintBkGrd();
}

void __fastcall MyTreeView::PaintBkGrd()
{
PAINTSTRUCT ps;
BeginPaint(Handle, &ps);

int width = ClientWidth;
int height = ClientHeight;
HDC myhdc, hdc1, hdc2;
HBITMAP bmp1, bmpbk1, bmp2, bmpbk2;
myhdc = ps.hdc;
hdc1 = CreateCompatibleDC(myhdc);
bmp1 = CreateCompatibleBitmap(myhdc, width, height);
bmpbk1 = SelectObject(hdc1, bmp1);
SendMessage(Handle, WM_PAINT, (unsigned)hdc1, 0);
hdc2 = CreateCompatibleDC(myhdc);
bmp2 = CreateCompatibleBitmap(myhdc, width, height);
bmpbk2 = SelectObject(hdc2, bmp2);

int bmpWidth, bmpHeight;
bmpWidth = TVImage->Width;
bmpHeight = TVImage->Height;
int i = ClientWidth / bmpWidth;
int j = ClientHeight / bmpHeight;
for (int m = 0; m <= i; ++i)
for (int n = 0; n <=j; ++n)
BitBlt(hdc2, m * bmpWidth, n * bmpHeight, bmpWidth, bmpHeight,
TVImage->Canvas->Handle, 0, 0, SRCCOPY);
TransparentBlt(hdc2, 0, 0, width, height,
hdc1, 0, 0, width, height,
ColorToRGB(clWindow));
BitBlt(myhdc, 0, 0, width, height, hdc2, 0, 0, SRCCOPY);

SelectObject(hdc1, bmpbk1);
DeleteObject(hdc1);
DeleteObject(bmp1);
SelectObject(hdc2, bmpbk2);
DeleteObject(hdc2);
DeleteObject(bmp2);

EndPaint(Handle, &ps);
}

void __fastcall MyTreeView::WndProc(TMessage &Msg)
{
switch(Msg.Msg)
{
case WM_ERASEBKGND:
Msg.Result = 1;
break;
case WM_HSCROLL:
case WM_VSCROLL:
case WM_MOUSEWHEEL:
case WM_LBUTTONDOWN:
case WM_LBUTTONDBLCLK:
case WM_MBUTTONDOWN:
case WM_MBUTTONDBLCLK:
case WM_KEYUP:
case WM_KEYDOWN:
InvalidateRect(Handle, NULL, false);
}
Dispatch(&Msg);
}
Siney 2003-10-11
  • 打赏
  • 举报
回复
对了,再节点展开的合并的时候也会出现背景变花的现象 ,解决方法同样是再
OnExpanded & OnCollapsed里加入
TreeView1->Invalidate();
Siney 2003-10-11
  • 打赏
  • 举报
回复
截获WM_VScroll和WM_MOUSEWHEEL消息后,调用:
TreeView1->Invalidate();

然后加入:
void __fastcall TForm1::TreeView1CustomDraw(TCustomTreeView *Sender,
const TRect &ARect, bool &DefaultDraw)
{
Sender->Canvas->Draw(0,0,Image1->Picture->Graphic);
DefaultDraw=true;
}

这样略微有一些闪烁,还是没有FlashGet做的那么流畅,我用MySpy跟踪消息,发现他随时都在接收TVM_GETITEMINDEX和TVM_GETITEM等消息,不知道为什么?
warton 2003-10-11
  • 打赏
  • 举报
回复
你可以到网上去下载别的组件。

要不自己重载treeview,自己把那些子节点画上去??
xrfei 2003-10-11
  • 打赏
  • 举报
回复
to:ThinkX(思·秋天的树·求职中)
编译时出现错误:
[C++ Error] Unit1.h(21): E2303 Type name expected
[C++ Error] Unit1.h(21): E2139 Declaration missing ;
[C++ Error] Unit1.cpp(21): E2451 Undefined symbol '_tvbChanger'
请教怎么解决?

whitetiger8 2003-10-11
  • 打赏
  • 举报
回复
强烈关注
HUANG_JH 2003-10-11
  • 打赏
  • 举报
回复
step 6: handle tvn_itemexpanding
we handle the tvn_itemexpanding message for the same reason that we handle the scroll messages.

void ctreectrlx::onitemexpanding(nmhdr* pnmhdr, lresult* presult)
{
nm_treeview* pnmtreeview = (nm_treeview*)pnmhdr;

if( m_bitmap.m_hobject != null )
invalidaterect(null);

*presult = 0;
}

step 7: add handler for wm_erasebkgnd
since we are already drawing the background../di2001.jpgn the onpaint() function handling this function and simply returning true ensures that the default window procedure does not erase the background../di2001.jpg adding this handler prevents extra updates to the control's client area and thus reduces flicker.

bool ctreectrlx::onerasebkgnd(cdc* pdc)
{
if( m_bitmap.m_hobject != null )
return true;
return ctreectrl::onerasebkgnd(pdc);
}

step 8: handle wm_querynewpalette & wm_palettechanged
the wm_querynewpalette message is sent to a window when it is about to receive input focus. it gives the window an oppurtunity to realize its logical palette so that it can present itself in the best form. the wm_palettechanged message is sent to a window whenever that system palette is changed. if we do not handle these messages and another application changes the system palette then the colors in our background../di2001.jpge will look terrible. unfortunately both these messages are sent to top level windows. we will deal with that in the next step.

the onquerynewpalette() function first checks whether it needs to reselect the palette. once it realizes the logical palette it invalidates the window if any of the color were remapped. the onpalettechanged() function returns without any further processing if the tree view control itself was responsible for the message because it changed the palette. it then calls onquerynewpalette() to rerealize the palette.

bool ctreectrlx::onquerynewpalette()
{
cclientdc dc(this);
if( dc.getdevicecaps(rastercaps) & rc_palette && m_pal.m_hobject != null )
{
dc.selectpalette( &m_pal, false );
bool result = dc.realizepalette();
if( result )
invalidate();
return result;
}

return ctreectrl::onquerynewpalette();
}

void ctreectrlx::onpalettechanged(cwnd* pfocuswnd)
{
ctreectrl::onpalettechanged(pfocuswnd);

if( pfocuswnd == this )
return;

onquerynewpalette();
}

step 9: forward palette messages from top level window
as i've already mentioned in the previous step, the wm_querynewpalette & wm_palettechanged messages are sent only to top level windows. since the list view control had changed the palette we have to forward these messages to the list view control. i had used a dialog based application to test this so here's what the handlers look like.

void ctreeviewdlg::onpalettechanged(cwnd* pfocuswnd)
{
cdialog::onpalettechanged(pfocuswnd);

m_tree.sendmessage( wm_palettechanged, (wparam)pfocuswnd->m_hwnd );
}

bool ctreeviewdlg::onquerynewpalette()
{
cdialog::onquerynewpalette();

return m_tree.sendmessage( wm_querynewpalette );
}

HUANG_JH 2003-10-11
  • 打赏
  • 举报
回复
step 4: add handler for wm_paint
the onpaint() function is where the action is. there are two distinct situation that the onpaint() function has to handle. no image might have been specified, in which case the control should display in the default manner. the other situation is of course when an image has been specified by calling the setbkimage() function set out in the previous step. this is where we have to go through extra gyrations to get the task done. it is easier and more efficient to let the default window handle the painting completely when no image has been specified. however, if you want further specialization of the control, such as displaying different items in different colors and font, then it is easier to plug in that code into the one listed below.

one of the first things we do is create a memory device context that is compatible with the paint dc. we let the default window procedure of the control draw in the memory dc. if not image has been specified then we simply copy the content of the memory dc to the paint dc.

if a background../di2001.jpge has been specified we create a mask bitmap from the content of the memory device context. we have to use a new device context for the mask bitmap. a mask bitmap is essentially a monochrome bitmap in which one color indicates the background../di2001.jpgd color in the source bitmap and the other color indicates all the bits that are not the background../di2001.jpg.

we create another device context to hold the image bitmap. we create yet another device context to hold the tiled image of the bitmap. the 'imagedc' should have the proper palette selected into it before the tiled image is drawn on it. forgetting to do so results in loss of color information on displays with 256 or fewer colors. the palette is also selected into, and realized for the main device context. since only the invalidated items are repainted, it is important to offset the tiled image properly so that it appears continuous rather than as image strips. this is where the call to getitemrect() and getscrollpos() comes in.

the tiled image is drawn onto the 'imagedc', then the image from the memory dc is copied transparently onto the 'imagedc' and finally the result is copied onto the screen.to draw the image transparently we create a mask bitmap using yet another device context. a mask bitmap is a monochrome bitmap in which one color indicates the background../di2001.jpgd color in the source bitmap and the other color indicates all the bits that are not the background../di2001.jpg. we need the mask bitmap to copy only the foreground color from the memory dc to the image dc.

once we have the mask bitmap, we draw the background../di2001.jpgr using the paint dc and then draw the image in the memory dc transparently over the paint dc. i had initially used maskblt() for drawing the image transparently but found out that it was supported on nt only and not windows 95. here's what we do. the image in memdc is the foreground image. when drawing this image we have to somehow make the background../di2001.jpgr have no effect. we achieve this by setting the background../di2001.jpgo black using the mask bitmap. when we later use the srcpaint raster operation, the black color has no effect on the destination color. similarly we use the mask bitmap to set the foreground color of the image in the paint dc to black. we finally combine the two images.

we could have painted the tiled image directly onto the paint dc, and then drawn the content of the memory dc onto it. the reason for not doing so is because we avoid the excess flickering that this would have caused.

void ctreectrlx::onpaint()
{
// remove comments from next five lines if you don't need any
// specialization beyond adding a background../di2001.jpgd image
// if( m_bitmap.m_hobject == null )
// {
// ctreectrl::onpaint();
// return;
// }

cpaintdc dc(this);

crect rcclip, rcclient;
dc.getclipbox( &rcclip );
getclientrect(&rcclient);

// create a compatible memory dc
cdc memdc;
memdc.createcompatibledc( &dc );

// select a compatible bitmap into the memory dc
cbitmap bitmap, bmpimage;
bitmap.createcompatiblebitmap( &dc, rcclient.width(), rcclient.height() );
memdc.selectobject( &bitmap );


// first let the control do its default drawing.
cwnd::defwindowproc( wm_paint, (wparam)memdc.m_hdc, 0 );

// draw bitmap in the background../di2001.jpgf one has been set
if( m_bitmap.m_hobject != null )
{
// now create a mask
cdc maskdc;
maskdc.createcompatibledc(&dc);
cbitmap maskbitmap;

// create monochrome bitmap for the mask
maskbitmap.createbitmap( rcclient.width(), rcclient.height(),
1, 1, null );
maskdc.selectobject( &maskbitmap );
memdc.setbkcolor( ::getsyscolor( color_window ) );

// create the mask from the memory dc
maskdc.bitblt( 0, 0, rcclient.width(), rcclient.height(), &memdc,
rcclient.left, rcclient.top, srccopy );


cdc tempdc;
tempdc.createcompatibledc(&dc);
tempdc.selectobject( &m_bitmap );

cdc imagedc;
cbitmap bmpimage;
imagedc.createcompatibledc( &dc );
bmpimage.createcompatiblebitmap( &dc, rcclient.width(),
rcclient.height() );
imagedc.selectobject( &bmpimage );

if( dc.getdevicecaps(rastercaps) & rc_palette && m_pal.m_hobject != null )
{
dc.selectpalette( &m_pal, false );
dc.realizepalette();

imagedc.selectpalette( &m_pal, false );
}

// get x and y offset
crect rcroot;
getitemrect( getrootitem(), rcroot, false );
rcroot.left = -getscrollpos( sb_horz );

// draw bitmap in tiled manner to imagedc
for( int i = rcroot.left; i
step 5: handle the scroll messages
the only reason we need to handle the scroll messages is because it helps in reducing the flicker caused by the control update. the default handling the wm_hscroll and the wm_vscroll messages is that the control is scrolled by the window proc and then the exposed area is invalidated. by calling invalidaterect() we make sure that the control gets updated only once.

void ctreectrlx::onvscroll(uint nsbcode, uint npos, cscrollbar* pscrollbar)
{
if( m_bitmap.m_hobject != null )
invalidaterect(null);
ctreectrl::onvscroll(nsbcode, npos, pscrollbar);
}

void ctreectrlx::onhscroll(uint nsbcode, uint npos, cscrollbar* pscrollbar)
{
if( m_bitmap.m_hobject != null )
invalidaterect(null);
ctreectrl::onhscroll(nsbcode, npos, pscrollbar);
}

HUANG_JH 2003-10-11
  • 打赏
  • 举报
回复
VC的例子,看看, 会有帮助的
Using a bitmap as a background image

--------------------------------------------------------------------------------

the tree view control does not support an owner drawn control - not yet. this makes it somewhat difficult to display an image as a background../di2001.jpg however, it can be done and we will discuss how. the basic approach is to let the control draw in a memory device context, draw this transparently over the background../di2001.jpge and then draw the final image onto the control client area. off course, there are a few details involved.



one good use of having an image in the background../di2001.jpgs to display the company logo. make sure though that the image is such that it does not make the text difficult to read.

the technique given below uses a 256 color (16 color would also be fine) image that has been added as a bitmap resource. if the image is smaller than the control, then the image is tiled to cover the client area. for faster redraws the image scrolls along with the items.

step 1: add bitmap to resource
add a bitmap that you want to use as the background../di2001.jpge to the resource file. use the import feature of the resource editor to get the image in. a copy-paste operation usually results in the colors of the bitmap getting messed up. you may add more than one bitmap and allow the user to choose one of the images or decide not to use any.

step 2: add member variables
it is not efficient to reload the bitmap or recreate the logical palette each time an item needs to be repainted. we therefore add member variables to store the bitmap, the logical palette and the dimensions of the bitmap. declare these as protected members since we will provide a function to set the background../di2001.jpg.

protected:
cpalette m_pal;
cbitmap m_bitmap;
int m_cxbitmap, m_cybitmap;

step 3: add member functions to set background../di2001.jpgd image
we add two overloaded member functions to set the background../di2001.jpg. these functions should be public member functions. the first function takes the resource id as an argument and the second takes the resource name as an argument.

these functions can be called to change the image if one has already been specified. the first thing the function does is delete the bitmap and palette gdi object if one has been created. it then loads the bitmap and attaches it to the cbitmap object. we use a call to the global ::loadimage() rather than to the cbitmap::loadbitmap(). the reason for this is that we want to be able to access the dibsection of the bitmap and the reason why we want the dibsection is because we want to create a logical palette that matches the colors used by the bitmap. my guess is you already know why we need a logical palette. without going into too much detail lets just say that if you do not set up and use a logical palette then the image is likely to appear very dull on a 256 color display. you'd be fine if the display supported 64k or more colors. we also save the dimensions of the bitmap for later use.

once we have the bitmap set, we start working on creating the logical palette. we determine the number of colors used by the bitmap by getting access to the dibsection by calling the cbitmap::getobject() function. note that the documentation for this function does not mention the dibsection, you'd have to look up the documention of the ::getobject() function in the api section instead. sometimes the bitmapinfoheader which is part of the dibsection does not specify how many colors it uses. if this is the case we infer the color count from the number of bits it uses for the each pixel. for example, 8 bits can represent 256 different values and therefore indicates 256 colors. similarly, 16 bits indicates 64k colors.

a bitmap that uses more than 256 colors does not have a color table. in this situation we simply create a halftone palette compatible with the device context. a halftone palette is basically a palette that contains a sampling of all the different colors. this is certainly not the best solution but it is the simplest.

if the bitmap has 256 colors or less, we do create the palette. we allocate enough space to hold the color table of the bitmap and call the function ::getdibcolortable() to retrieve it from the bitmap. we also allocate enough memory to create a logical palette and copy the color entries from the bitmap's color table. the palversion field should be 0x300.

after creating the cpalette object, we deallocate the memory blocks allocated earlier and invalidate the window so that it can be redrawn using the new image.

bool ctreectrlx::setbkimage(uint nidresource)
{
return setbkimage( (lpctstr)nidresource );
}

bool ctreectrlx::setbkimage(lpctstr lpszresourcename)
{

// if this is not the first call then delete gdi objects
if( m_bitmap.m_hobject != null )
m_bitmap.deleteobject();
if( m_pal.m_hobject != null )
m_pal.deleteobject();


hbitmap hbmp = (hbitmap)::loadimage( afxgetinstancehandle(),
lpszresourcename, image_bitmap, 0,0, lr_createdibsection );

if( hbmp == null )
return false;

m_bitmap.attach( hbmp );
bitmap bm;
m_bitmap.getbitmap( &bm );
m_cxbitmap = bm.bmwidth;
m_cybitmap = bm.bmheight;


// create a logical palette for the bitmap
dibsection ds;
bitmapinfoheader &bminfo = ds.dsbmih;
m_bitmap.getobject( sizeof(ds), &ds );

int ncolors = bminfo.biclrused ? bminfo.biclrused : 1 << bminfo.bibitcount; // create a halftone palette if colors> 256.
cclientdc dc(null); // desktop dc
if( ncolors > 256 )
m_pal.createhalftonepalette( &dc );
else
{
// create the palette

rgbquad *prgb = new rgbquad[ncolors];
cdc memdc;
memdc.createcompatibledc(&dc);

memdc.selectobject( &m_bitmap );
::getdibcolortable( memdc, 0, ncolors, prgb );

uint nsize = sizeof(logpalette) + (sizeof(paletteentry) * ncolors);
logpalette *plp = (logpalette *) new byte[nsize];

plp->palversion = 0x300;
plp->palnumentries = ncolors;

for( int i=0; i palpalentry[i].pered = prgb[i].rgbred;
plp->palpalentry[i].pegreen = prgb[i].rgbgreen;
plp->palpalentry[i].peblue = prgb[i].rgbblue;
plp->palpalentry[i].peflags = 0;
}

m_pal.createpalette( plp );

delete[] plp;
delete[] prgb;
}
invalidate();

return true;
}

飞翔的老虎 2003-10-11
  • 打赏
  • 举报
回复
sendmessage(listview1->handle,wm_paint,0,0) ;

sendmessage(listview1->handle,wm_paint,0,1) ;
mme 2003-10-11
  • 打赏
  • 举报
回复
我试了很多方法都还是闪烁或者支离破碎,所以,我怀疑那些节点是要靠自己画上去的。
只要你用系统的默认处理就会有问题。listview也是,不过listview好做一些,因为listview没有展开这个功能。拿张差不多的图就可以蒙骗过关^_^
pp616 2003-10-10
  • 打赏
  • 举报
回复
WM_MOUSEWHEEL是有的。但是如果知道滚动方向?和滚动多少?
佣工7001 2003-10-10
  • 打赏
  • 举报
回复
试过WM_MOUSEWHEEL吗
wenminghu 2003-10-10
  • 打赏
  • 举报
回复
用Virtual TreeView.:)
SysTreeView32不是VC特有的,而是系统级别的控件,bcb也可以直接用api调用的啊,注册一个这样的类就ok了.
ljianq 2003-10-10
  • 打赏
  • 举报
回复
回复人: pp616(傻小子) ( ) 信誉:101 2003-10-10 00:36:00 得分:0

WM_MOUSEWHEEL是有的。但是如果知道滚动方向?和滚动多少?
------------------------------


请问:你的背景也是滚动的吗?
pp616 2003-10-10
  • 打赏
  • 举报
回复
背景不滚动。如果是滚动的就很好做了。
kataboy 2003-10-10
  • 打赏
  • 举报
回复
gz
pp616 2003-10-09
  • 打赏
  • 举报
回复
WM_VSCROLL是可以截取的。
但是滚轮就没有消息了。我用spy++看过。
zihan 2003-10-09
  • 打赏
  • 举报
回复
那你当你拖动滚动条的时候就给它发送消息了,呵呵,不过有点不合理.
加载更多回复(1)

13,825

社区成员

发帖
与我相关
我的任务
社区描述
C++ Builder相关内容讨论区
社区管理员
  • 基础类社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧