📄 cb200006dc_f.asp.htm
字号:
<HTML>
<HEAD>
<TITLE>Exploring Possibilities</TITLE>
</HEAD>
<BODY>
<TABLE border=0 width="100%" cellpadding=0 cellspacing=0>
<TR valign=top>
<TD width="100%">
<p class=ColumnTitle><font size="2">In
Development</font></p>
<p class=ColumnSubtitle>Development
Strategies / Component Design and Creation / User Interface Issues</p>
<p class=BodyText> </p>
<p class=Byline>By Damon
Chandler</p>
<p class=BodyText> </p>
<p class=StoryTitle><font size="2"><b>Exploring
Possibilities</b></font></p>
<p class=StorySubtitle><font size="2">Part
II: Wrapping Up Our List View Component</font></p>
<p class=BodyText> </p>
<p class=BodyText> Before
we continue our examination of the process of creating an Explorer-style list
view control, let's recap the main concepts from Part I of this series. Our
goal is to create a familiar, intuitive means for users of our component to
interact with the Windows shell. To this end, we chose to create a <i
style='mso-bidi-font-style:normal'>TCustomListView</i> descendant class that
emulates the folder view (i.e. the left pane) of Windows Explorer. Moreover, we
decided that such a class was ideally suited for presentation as a VCL
component. We also covered some of the initial design strategies, and prepared
a prioritized list of features to implement. </p>
<p class=BodyText> </p>
<p class=BodyText> Again,
our first, and fundamental task, is to fill the list view control with shell
objects. That is, we need to populate our list view with items that represent
the files, folders, and virtual objects of the shell namespace. However, a key
realization made in Part I is that we cannot use the traditional approach
involving the <i>FindFirst</i> and <i
style='mso-bidi-font-style:normal'>FindNext</i> functions. Recall that these
functions will only work with those shell objects that are part of the physical
file system. To overcome this limitation, we examined several shell API
structures, functions, and interfaces. Specifically, we related a file object's
name and path to the SHITEMID and ITEMIDLIST structures, respectively. We also
discussed the use of the <i>IShellFolder</i>
and <i>IEnumIDList</i> interfaces, and
examined how these interfaces can be used to enumerate the contents of a
folder. Again, the advantage of these interfaces over the traditional approach
lies in the fact that they can be used with virtual folders (e.g. Control Panel),
as well as file-system folders. </p>
<p class=BodyText> </p>
<p class=BodyText> Finally,
recall that during the process of populating the list view, we ran into another
problem. Namely, we discovered that our enumeration process was not nearly as
fast as that of Windows Explorer. Let's examine some techniques to help us
overcome this speed limitation. </p>
<p class=BodyText> </p>
<p class=Subheads>Breaking
the Speed Limit</p>
<p class=BodyText> With the
introduction of COMCTL32.DLL version 4.70, Microsoft presented the
LVS_OWNERDATA list view style. Use of this style creates what is known as a
virtual list view, available in the <i>TCustomListView</i>
class via the <i>OwnerData</i> property.
This style affords significant speed improvements, but presents several
limitations of its own. For example, virtual list views don't support multiple
work areas, specification of item position (LVM_SETITEMPOSITION; <i
style='mso-bidi-font-style:normal'>TListItem::Position</i>), or "snap-to-grid" arrangement, among others. </p>
<p class=BodyText> </p>
<p class=BodyText> Another
way of decreasing the amount of time necessary to adding an item, is to specify
LPSTR_TEXTCALLBACK and I_IMAGECALLBACK as values for the <i>pszText</i> and <i>iImage</i>
parameters of the LVITEM structure, respectively. Use of these flags simply
frees the list view from storing the data associated with each of these fields.
Accordingly, the list view's parent window is required to handle the
LVN_GETDISPINFO notification message to supply the necessary data. This is the
same approach taken by the <i>TCustomListView</i>
class. </p>
<p class=BodyText> </p>
<p class=BodyText> Since
the virtual list view suffers from too many limitations, let's take advantage
of the second approach. Namely, we'll emulate Windows Explorer by providing
information on a need-to-know basis. For example, when browsing a directory
containing a large amount of items, only a small fraction is initially visible.
As the LVN_GETDISPINFO notification message is sent only for those items that
are visible, it would make sense to retrieve an item's information in response
to this message. Moreover, this information only needs be retrieved once,
because we can store the results and supply the cached information upon
request. To this end, let's create a simple class that will hold the relevant
information for each shell object. Such a class is shown in <a
href="#ListingOne">Listing One</a>.</p>
<p class=BodyText> </p>
<p class=BodyText> While we
now have an element class, we still need an associated container class. Here,
we'll simply use the <i>TListItem::Data</i> property. Note, however,
that the <i>TList</i> VCL class, or one of
the container classes from the Standard C++ Library, is also a viable, more
robust, candidate. Let us now revise the <i>FillListEx</i>
by implementing its component-based counterpart, the virtual <i
style='mso-bidi-font-style:normal'>DoFillListEx</i> member function, as shown
in <a href="#ListingTwo">Listing Two</a>.</p>
<p class=BodyText> </p>
<p class=BodyText> The <i>TExpListView::DoFillListEx</i>
member function is similar to the <i>FillListEx</i>
function, except we've eliminated the expensive <i>SHGetFileInfo</i> function call. Instead, we create an instance of the <i
style='mso-bidi-font-style:normal'>TListShellInfo</i> class, then query the
component user for permission to add the item. In this way, the component user
can appropriately filter out certain items. Once an item is added, we use the <i
style='mso-bidi-font-style:normal'>DisplayNameFromPIDL</i> utility function to
retrieve the associated display name of the shell object, and store a pointer
to the <i>TListShellInfo</i> instance in the
<i>Data</i> property of the newly added <i>TListItem</i>.</p>
<p class=BodyText> </p>
<p class=BodyText> Our next
task is to handle the LVN_GETDISPINFO notification message. This message is
sent to the parent of the list view in the form of a WM_NOTIFY message. To handle
this message in our component class, we provide a handler for the CN_NOTIFY
message. This latter message is simply a reflected version of WM_NOTIFY that is
sent back to the list view itself. In response to the LVN_GETDISPINFO
notification message, we determine the item for which information is being
requested, then simply fill its associated <i>TListShellInfo</i>
instance with relevant shell information. This latter task is delegated to the
virtual <i>DoFillShellInfoIcon</i> and <i
style='mso-bidi-font-style:normal'>DoFillShellInfoDetails</i> member functions,
for icon and details view (e.g. size, type, date) information retrieval,
respectively. The implementations of these member functions, along with the
CN_NOTIFY message handler, are shown in <a href="#ListingThree">Listing Three</a>.</p>
<p class=BodyText> </p>
<p class=BodyText> It's
important to note that we only need to extract the shell information for a
certain item if that item is visible in the list view. This part is handled
automatically by the LVN_GETDISPINFO message handler. That is, the notification
message is only sent for visible items. Once this information is obtained,
there's no need to extract it again. To this end, the <i>TListShellInfo</i>::<i>ValidIcon</i>
and <i>ValidDetails</i> properties serve as
flags, indicating whether the shell information for a particular item has
already been retrieved. </p>
<p class=BodyText> </p>
<p class=BodyText> Finally,
there's somewhat of a cause-and-effect paradox when using the <i
style='mso-bidi-font-style:normal'>TListItem</i> class. Namely, in response to
the LVN_GETDISPINFO message, we extract, then assign, the proper icon index to
the <i>TListItem::ImageIndex </i>property. Yet, when the <i>ImageIndex</i> property is changed, the <i>TListItem</i> class calls the <i>TCustomListView::UpdateItems</i> member function,
causing our list view to receive yet another LVN_GETDISPINFO notification.
Figure 1 illustrates this conundrum. </p>
<p class=BodyText> </p>
<p class=Captions><img width=330 height=241
src="images/cb200006dc_f_image001.gif" tppabs="http://www.cbuilderzine.com/features/2000/06/cb200006dc_f/cb200006dc_f_image001.gif"> <br>
<b>Figure 1:</b> The "cause-and-effect" paradox. </p>
<p class=BodyText> </p>
<p class=BodyText> While
our <i>ValidIcon</i> property effectively
prevents an infinite cycle, this is certainly not efficient. To prevent this,
we simply trap the first LVN_GETDISPINFO notification, and provide the
information to the list view in response to the incited version, i.e. the
LVN_GETDISPINFO notification caused by manipulating the <i>ImageIndex</i> property. This is effectively handled with the following
statement from the <i>CNNotify</i> message
handler: </p>
<p class=BodyText> </p>
<p class=Code><span class=Code><b>if</b> (DoFillShellInfoIcon(RequestItem)) </span></p>
<p class=Code><span class=Code> <b> return</b>;</span></p>
<p class=BodyText> </p>
<p class=BodyText> We
implement the <i>DoFillShellInfoIcon</i>
member function to return <b>true</b> if the <i>TListItem</i>::<i>ImageIndex</i>
property has been set. Because we know this manipulation will incite another
LVN_GETDISPINFO message, we can simply trap the first message. A similar
technique is used for the details information, where manipulation of the <i
style='mso-bidi-font-style:normal'>TListItem::SubItems </i>property invokes an LVN_GETDISPINFO notification
message as well. Figure 2 depicts the corrected scheme. </p>
<p class=BodyText> </p>
<p class=Captions><img width=330 height=241
src="images/cb200006dc_f_image002.gif" tppabs="http://www.cbuilderzine.com/features/2000/06/cb200006dc_f/cb200006dc_f_image002.gif"> <br>
<b>Figure 2:</b> The "cause-and-effect" solution. </p>
<p class=BodyText> </p>
<p class=Subheads>Handling
the Details View</p>
<p class=BodyText> Notice
from the <i>DoFillShellInfoDetails</i>
implementation of Listing Three that we use the <i>IShellDetails</i> interface to retrieve the "details view" information
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -