⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ch23.htm

📁 好书《C++ Builder高级编程技术》
💻 HTM
📖 第 1 页 / 共 5 页
字号:
public:

  virtual __fastcall TPentiumPro(TComponent 
*AOwner): TChip(AOwner) {}

  virtual __fastcall TPentiumPro(TComponent *AOwner, int ACol, int ARow)

    : TChip(AOwner, ACol, ARow) {}

};

void __fastcall TPentium::Paint()

{

  Canvas->Brush->Color = clPurple;

  Canvas->Rectangle(0, 0, 
ClientWidth, ClientHeight);

  Canvas->Brush->Color = clRed;

  Canvas->Ellipse(0, 0, ClientWidth, ClientHeight);

}



void __fastcall TPentiumPro::Paint()

{

  Canvas->Brush->Color = clGreen;

  Canvas->Rectangle(0, 0, 
ClientWidth, ClientHeight);

  Canvas->Brush->Color = clBlue;

  Canvas->Ellipse(0, 0, ClientWidth, ClientHeight);

}

</FONT></PRE>
<P>In other words, my implementation doesn't do much to differentiate these objects
other than to leverage 
polymorphism to make them appear differently when they are
shown on-screen. A nice touch to add to this project would be to show a bitmap of
a processor rather than the simple graphic I include here.</P>
<P>As you saw in earlier chapters, all 
<TT>TWidget</TT> descendants also have the
capability to report on their hierarchy. This feature will play a role in this program
when a user right-clicks on a component with the mouse. In particular, if you right-click
on any of the <TT>Widget</TT> 
objects, a form that shows their hierarchy pops up.
I will discuss that aspect of the program later in the chapter.</P>
<P>Because the <TT>TWidget</TT> components are descendants of <TT>TCustomControl</TT>,
they all can be hung on the Component 
pallet. The following code from the <TT>Widgets</TT>
unit registers the <TT>Widget</TT> and <TT>Pallet</TT> controls:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall Register()

  {

    TComponentClass classes[5] = {__classid(TWidget),

       
__classid(TPentium), __classid(TPentiumPro),

       __classid(TPallet ), __classid(TDataPallet ) };

    RegisterComponents(&quot;Unleash&quot;, classes, 4);

</FONT></PRE>
<PRE><FONT COLOR="#0066FF"> }

</FONT></PRE>
<P>That's all that needs to be 
said about the technical aspect of the <TT>Widget</TT>
components. When all is said and done, the most important fact about these simple
objects is that they all descend from the same parent. As a result, the program will
be able to use polymorphism 
when handling them. This turns out to be very useful,
particularly during drag-and-drop operations. The drag-and-drop aspect of the program
will be covered over the next few sections of this chapter.
<H3><A NAME="Heading34"></A><FONT 
COLOR="#000077">Introducing the Pallet Controls</FONT></H3>
<P>The <TT>TPallet</TT> controls are a little more complex than the <TT>Widget</TT>
controls in that they can support drag and drop. In particular, they know how to
respond when a user drops 
a <TT>TWidget</TT> control onto them.</P>
<P>Before describing the drag-and-drop operation, I should spend a moment covering
the heritage of the pallet controls. The <TT>TCustomPallet</TT> control from which
the <TT>TDataPallet</TT> descends is pretty 
straightforward:</P>
<PRE><FONT COLOR="#0066FF">class TCustomPallet : public TCustomControl

{

private:

  int FPalletNumber;

  TListHierarchy *FHierarchy;

protected:

  virtual void __fastcall Paint(void);

public:

  virtual __fastcall 
TCustomPallet(TComponent *AOwner);

  virtual __fastcall ~TCustomPallet(void)

    { delete FHierarchy; }

  TStringList *GetHierarchy() { return FHierarchy-&gt;GetHierarchy(this); }

  __property int PalletNumber={read=FPalletNumber, 
write=FPalletNumber};

};

</FONT></PRE>
<P>This object has a field for storing the number of the pallet and, through aggregation,
the capability to report on its hierarchy.

<DL>
	<DT></DT>
</DL>



<BLOCKQUOTE>
	<P>
<HR>
<FONT 
COLOR="#000077"><B>NOTE:</B></FONT><B> </B>One of my goals in these chapters
	is to convince some readers of the merits of aggregation. Multiple inheritance is
	a powerful tool, but it adds considerable complexity to your program. In the last
	few 
chapters, you have had several opportunities to look at aggregation. This technique
	takes a little more work to implement than multiple inheritance, but I believe it
	often ends up saving you time in the long run, because it is so much easier to 
understand
	and maintain than multiple inheritance. <BR>
	<BR>
	My point here is not to criticize multiple inheritance, but rather to point out that
	aggregation is a valuable tool in its own right. There are certain settings in which
	each technology 
shines, and good programmers should explore the benefits of both
	aggregation and multiple inheritance so that they will know when to favor one method
	over the other. <BR>
	<BR>
	When you are in doubt, I would suggest using aggregation, because it 
almost never
	causes trouble. Multiple inheritance is easier to implement, but under certain circumstances
	it can cause enormous confusion that leaves even the best programmers feeling frustrated.
	This is literally true. Some of the best programmers 
I have ever met have spent days,
	sometimes even weeks, trying to untie the knots caused by someone else's unintelligent
	or ill-advised use of multiple inheritance. <BR>
	<BR>
	Of course, if you use multiple inheritance intelligently, it is a great 
tool. But
	one of the prerequisites for using it intelligently is knowing when not to use it.
	If you are not going to use it, you need to understand the alternatives. There is
	really only one good alternative to multiple inheritance, and that is 
aggregation.
	As a result, all programmers should understand both multiple inheritance and aggregation.
	
<HR>


</BLOCKQUOTE>

<P>In <TT>Widgets.h</TT>, I declare a direct descendant of <TT>TCustomPallet</TT>
called <TT>TPallet</TT>:</P>
<PRE><FONT 
COLOR="#0066FF">class TPallet : public TCustomPallet

{

public:

  virtual __fastcall TPallet(TComponent *AOwner): TCustomPallet(AOwner)

    { Width = 25; Height = 25; }

__published:

  __property PalletNumber;

  __property OnDragDrop;

  
__property OnDragOver;

  __property Color;

};

</FONT></PRE>
<P>This object adds no functionality to <TT>TCustomPallet</TT> but only publishes
certain of its properties. I place this object here for no other reason than to point
out the correct way 
to design objects from which others may descend. If you want
to be sure that you are following the best techniques, you might want to create two
classes when it might at first appear that one will do:

<DL>
	<DD><B>1.</B> The first class provides all 
the functionality needed by an object.<BR>
	<B><BR>
	2.</B> The second class descends from the first and publishes the properties you
	think you will need to use for your particular version of the object.
</DL>

<P>When you do things this way, others 
can descend from the first class you created
but get the option of deciding which classes they want to publish.</P>
<P>An example of this technology in action is shown by the <TT>TDataPallet</TT>:</P>
<PRE><FONT COLOR="#0066FF">class TDataPallet : 
public TCustomPallet

{

private:

  TTable *FWidgetsTable;

  TQuery *FWidgetsQuery;

protected:

  void __fastcall PalletDragDrop(TObject *Sender,

    TObject *Source, int X, int Y);

  void __fastcall PalletDragOver(TObject *Sender, TObject 
*Source,

    int X, int Y, TDragState State, bool &amp;Accept);

  void virtual EnterWidgets(int Total, TWidget *W);

public:

  virtual __fastcall TDataPallet(TComponent *AOwner);

  void __fastcall QueryPallet(TQuery *Query);

  float __fastcall 
QueryPalletSum(TQuery *Query);

__published:

  __property TTable *WidgetsTable={read=FWidgetsTable, write=FWidgetsTable};

  __property TQuery *WidgetsQuery={read=FWidgetsQuery, write=FWidgetsQuery};

  __property PalletNumber;

  __property Hint;

  
__property Color;

  __property TabOrder;

  __property OnMouseDown;

  __property PopupMenu;

};

</FONT></PRE>
<P><TT>TDataPallet</TT> descends from <TT>TCustomPallet</TT>, thereby inheriting
certain useful functionality. It then adds its own 
methods and publishes the properties
that it wants to expose in the Object Inspector. The key point here is that <TT>TDataPallet</TT>
should have the right to decide which properties it wants to expose!</P>
<P>The <TT>TDataPallet</TT> is smart enough 
to accept drag and drop, and also to
work with objects of type <TT>TTable</TT> and <TT>TQuery</TT>. These are both relatively
complex subjects, so I will handle them in their own sections of this chapter.
<H4><A NAME="Heading36"></A><FONT 
COLOR="#000077">TDataPallet, and Drag and Drop</FONT></H4>
<P>Drag and drop is a flashy interface element of the type that I have a natural
tendency to resist. I just tend to think that the really hot technology involves
the language itself, and 
particularly the construction of objects and components.
However, there is a lot to be said for building programs that are easy to use, and
drag and drop can aid in that process.</P>
<P>The one problem with drag and drop is that there are few simple 
ways to tell the
user that the feature is available. When you open up the Warehouse program, there
is no way to show users that they can drag components onto the pallets. You have
to tell them this in the online help or in some kind of tutorial. The 
same problem
exists in the Explorer, in many of the Zip utilities I've seen, and elsewhere in
the application development world.</P>
<P>Lately, I've even seen some programs just write the words drag and drop in their
captions. Even though the words 
appear out of context and with no specific reference,
this is often enough to allow me to figure out how to use the application. I attempt
this same thing with the Warehouse program, as shown in Figure 23.1.</P>
<P>After you get over the initial 
training period, drag and drop gets a chance to
come into its own. It can then provide a very powerful addition to your programs,
especially if used in the right context.</P>
<P>The VCL makes drag and drop simple to implement. To make it work, you 
need to
respond to <TT>WM_MOUSEDOWN</TT> events on the component you want to drag:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall TForm1::WidgetMouseDown(TObject *Sender, TMouseButton Button,

  TShiftState Shift, int X, int Y)

{

  if 
(!dynamic_cast&lt;TWidget *&gt;(Sender))

    return;

  TWidget *W = (TWidget *)(Sender);



  if (Shift.Contains(ssLeft))

  {

    W-&gt;BeginDrag(False);

  }

  else

  {

    HierarchyDlg-&gt;ListBox1-&gt;Items = W-&gt;GetHierarchy();

    
HierarchyDlg-&gt;ShowModal();

  }

}

</FONT></PRE>
<P>The relevant code in this example is only three lines long. Stripped of the context
of the particular example used in this program, it would look like this:</P>
<PRE><FONT COLOR="#0066FF">if 
(dynamic_cast&lt;TWidget *&gt;(Sender))

{

  TWidget *W = (TWidget *)(Sender);

  W-&gt;BeginDrag(False);

}

</FONT></PRE>
<P>Checking to see that the component is really a <TT>Widget</TT> is probably overkill,
because you would only associate this 
code with the <TT>OnMouseMove</TT> event of
a control you wanted to drag. Nevertheless, it is often better to be safe than sorry.
In particular, you want to know immediately if you accidentally associate the wrong
code with the wrong component. In 
short, error checking is often as much about detecting
programmer error as it is about detecting user error. If you decide later that you
have a performance problem, you can strip this kind of error checking from your program,
or else use assertions, 
conditional compilation, or some other technology.

<DL>
	<DT></DT>
</DL>



<BLOCKQUOTE>
	<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>If you use the Object Inspector
	to examine <TT>TWidget</TT> or its descendants, you will notice that 
one of the two
	events it publishes is <TT>OnMouseDown</TT>. Obviously I published this event because
	I knew it would be needed. I also published the <TT>OnDragDrop</TT> event, but only
	because I think the user might need it under certain 
circumstances never exploited
	in this program. 
<HR>


</BLOCKQUOTE>

<P>After you have safely typecast the object as a <TT>Widget</TT>, you can begin
the drag operation:</P>
<PRE><FONT COLOR="#0066FF">W-&gt;BeginDrag(False);

</FONT></PRE>
<P>You 
should set the parameter <TT>BeginDrag</TT> to <TT>false</TT> if you want
the cursor to change only after a drag operation is begun. For instance, if the user
wants to click on a component for some other reason than dragging, you would want
to set the 
parameter to <TT>false</TT>, so that the cursor will not change if users
merely click on a component but only if they click and then start dragging.</P>
<P>Starting a drag-and-drop operation is obviously fairly trivial. The other side
of the operation 
is a bit trickier but still not rocket science:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall TDataPallet::PalletDragOver(TObject *Sender,

  TObject *Source, int X, int Y, TDragState State, bool &amp;Accept)

{

  Accept = dynamic_cast&lt;TWidget 
*&gt;(Source);

}

void __fastcall TDataPallet::PalletDragDrop(TObject *Sender,

  TObject *Source, int X, int Y)

{

  if (WidgetsTable == NULL)

    ShowMessage(&quot;No table assigned to the WidgetsTable property&quot;);

  else

  {

    if 
(dynamic_cast&lt;TWidget *&gt;(Source))

    {

      TWidget *W = (TWidget *)(Source);

      AnsiString S = &quot;Enter number of &quot; + W-&gt;Name;

      AnsiString NumWidgets;



      if (InputQuery(&quot;Widget Number Dialog&quot;, S, 
NumWidgets))

      {

        EnterWidgets(NumWidgets.ToInt(), W);

      }

    }

  }

}

</FONT></PRE>
<P>The first of the two methods shown here is an event handler that is called when
the user drags something over the component. If you set the 
<TT>Accept</TT> parameter
to an <TT>OnDragOver</TT> event to <TT>true</TT>, the cursor on the mouse will change
to reflect that this object accepts the current type of drag-and-drop operation.
The code shown here sets <TT>Accept</TT> to <TT>true</TT> 
as long as I am sure the
object in question is a <TT>Widget</TT>:</P>
<PRE><FONT COLOR="#0066FF">Accept = dynamic_cast&lt;TWidget *&gt;(Source);

</FONT></PRE>
<P>This is where polymorphism plays such a key role in the program. The issue here
is that 
polymorphism allows me to safely typecast any object that descends <TT>TWidget</TT>
as a <TT>Widget</TT>. This is an enormously powerful concept. By now you are used
to the idea that <TT>TWidget</TT>, <TT>TPentium</TT>, and <TT>TPentiumPro</TT> are

closely related objects. Nevertheless, they are not the same object, and if I had
to write code that worked with three different types of objects, my application would
be much more difficult to write and maintain.

<DL>
	<DT></DT>
</DL>




<BLOCKQUOTE>
	<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>Polymorphism allows you to treat
	objects generically, as a type. I can talk not about a specific widget, but about
	all widgets, regardless of their subtype. <BR>
	<BR>
	When 
dealing with real-world objects, the same thing is done all the time. For instance,
	I explain how to do something to you, the reader of this book, without having to
	know your name, your race, your sex, or your age. I can just refer to readers in
	
general and know that you will be able to understand the explanation, given the context
	of the expected audience for this book. If I had to address each copy of this book
	to a specific person, it would be considerably less useful. <BR>
	<BR>
	
Polymorphism lets the programmer tell a whole class of objects what needs to be done.
	This book works with all programmers who understand C++ and the basics of OOP. The
	methods shown here work with all objects that descend from <TT>TWidget</TT>. 
This
	capability to generalize, to work with abstractions, is enormously powerful! 
<HR>


</BLOCKQUOTE>

<P>When the user actually drops a component on another component, an <TT>OnDragDrop</TT>
event occurs:</P>
<PRE><FONT COLOR="#0066FF">void 
__fastcall TDataPallet::PalletDragDrop(TObject *Sender,  TObject *Source,

                                           int X, int Y)

{

  if (dynamic_cast&lt;TWidget *&gt;(Source))

  {

    TWidget *W = (TWidget *)(Source);

</FONT></PRE>
<P>The code 
shown here is a stripped-down version of the response to this event.
The component that was dropped is stored in the <TT>Source</TT> parameter of the
<TT>OnDragDrop</TT> event. I can simply typecast it as a <TT>TWidget</TT> and then
begin calling its 
methods. Some of the actual methods called, such as <TT>EnterWidgets</TT>,
are actually more related to the database part of this equation, so I will explain
them in the next section of this chapter.</P>
<P>The final point to notice about these 
drag-and-drop operations is that the event
handlers shown here are not part of the main form of the application but are instead
methods of the <TT>TDataPallet</TT> itself! In particular, notice the constructor
for the object:</P>
<PRE><FONT 
COLOR="#0066FF">__fastcall TDataPallet::TDataPallet(TComponent *AOwner)

: TCustomPallet(AOwner)

{

  OnDragOver = PalletDragOver;

  OnDragDrop = PalletDragDrop;

}

</FONT></PRE>
<P>This code sets up event handlers for <TT>OnDragDrop</TT> and 
<TT>OnDragOver</TT>
events. In other words, when you drop a <TT>TDataPallet</TT> onto a form, it is automatically
ready to handle drag-and-drop events without any custom coding from the user.</P>
<P>To go any further on this subject would be to start 
wrestling with the tables
used in this project. My plan is to cover that material in the next section of the
chapter.
<H4><A NAME="Heading39"></A><FONT COLOR="#000077">TDataPallet an

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -