Is there a use for THeaderControl in Delphi - delphi

I've been producing videos on using delphi components for my website LearnDelphi.tv. I'm looking to cover THeaderControl but can't find any use for it - is this component now not required - surpassed by other components such as TListView (with the report viewstyle) or is there some way of using it that I've overlooked?
Edit:
I recorded a segment on THeaderControl for one of my commercial videos, but I have decided to release this small section (20 minutes out of 6 hours) for free. Watch it on YouTube. Thanks to everyone who has contributed.

In general: THeaderControl can be used as header for tabular data. Of course, often a list view is used for that. But for an exotic layout of different components in each column that would not be easy to create by using a list view or similar, or for even complete different layouts for each column, the header control could be usefull. It simply offers more flexibility there where it is needed. Compare it with TPageControl offering more flexibility than TTabControl.
And about a specific niche case: for example, I use the header control as part of a planning grid component. The header control gets his captions via a data source, and the header sections are in sync with the columns and the scroll bar. Indeed, this requires some code, but not more than when implementing the different events designtime:
TPlanGridHeader = class(TCustomHeaderControl)
private
FSectionWidth: Integer;
procedure SetSectionWidth(Value: Integer);
procedure WMMouseMove(var Message: TWMMouseMove); message WM_MOUSEMOVE;
protected
function CreateSection: THeaderSection; override;
procedure SectionResize(Section: THeaderSection); override;
procedure SectionTrack(Section: THeaderSection; Width: Integer;
State: TSectionTrackState); override;
property SectionWidth: Integer read FSectionWidth write SetSectionWidth;
public
procedure AddSection(const AText, AHint: String);
constructor Create(AOwner: TComponent); override;
end;

Related

Display a warning when dropping a component on a form at design time

I'm tidying up components used in a large legacy project, I've eliminated about 90 of 220 custom components, replacing them with standard Delphi controls. Some of the remaining components require a significant amount work to remove which I don't have available. I would like to prevent anyone from making additional use of some of these components and was wondering if there was a way of showing a message if the component is dropped on the form at design time - something like "Don't use this control, use x or y instead".
Another possibility would to hide the control on the component pallet (but still have the control correctly render on the form at design time).
There is protected dynamic method TComponent.PaletteCreated, which is called only in one case: when we add this component to a form from component palette.
Responds when the component is created from the component palette.
PaletteCreated is called automatically at design time when the component has just been created from the component palette. Component writers can override this method to perform adjustments that are required only when the component is created from the component palette.
As implemented in TComponent, PaletteCreated does nothing.
You can override this method to show warning, so it will alert the user just one time, when he tries to put it to form.
UPDATE
I couldn't make this procedure work in Delphi 7, XE2 and Delphi 10 Seattle (trial version), so it seems that call to PaletteCreated from IDE is not implemented.
I sent report to QC:http://qc.embarcadero.com/wc/qcmain.aspx?d=135152
maybe developers will make it work some day.
UPDATE 2
There are some funny workarounds, I've tried them all this time, works normally. Suppose that TOldBadButton is one of components that shouldn't be used. We override 'Loaded' procedure and WMPaint message handler:
TOldBadButton=class(TButton)
private
fNoNeedToShowWarning: Boolean; //false when created
//some other stuff
protected
procedure Loaded; override;
procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
//some other stuff
end;
and implementation:
procedure TBadOldButton.Loaded;
begin
inherited;
fNoNeedToShowWarning:=true;
end;
procedure TOldBadButton.WMPaint(var Message: TWMPAINT);
begin
inherited;
if (csDesigning in ComponentState) and not fNoNeedToShowWarning then begin
Application.MessageBox('Please, don''t use this component','OldBadButton');
fNoNeedToShowWarning:=true;
end;
end;
The problem is, this works only for visual components. If you have custom dialogs, imagelists etc, they never get WMPaint message. In that case we can add another property, so when it is shown in object inspector, it calls getter and here we display warning. Something like this:
TStupidOpenDialog = class(TOpenDialog)
private
fNoNeedToShowWarning: boolean;
function GetAawPlease: string;
procedure SetAawPlease(value: string);
//some other stuff
protected
procedure Loaded; override;
//some other stuff
published
//with name like this, probably will be on top in property list
property Aaw_please: string read GetAawPlease write SetAawPlease;
end;
implementation:
procedure TStupidOpenDialog.Loaded;
begin
inherited;
fNoNeedToShowWarning:=true; //won't show warning when loading form
end;
procedure TStupidOpenDialog.SetAawPlease(value: string);
begin
//nothing, we need this empty setter, otherwise property won't appear on object
//inspector
end;
function TStupidOpenDialog.GetAawPlease: string;
begin
Result:='Don''t use this component!';
if (csDesigning in ComponentState) and not fNoNeedToShowWarning then begin
Application.MessageBox('Please, don''t use this component','StupidOpenDialog');
fNoNeedToShowWarning:=true;
end;
end;
Older versions of Delphi always scroll object inspector to the top when new component is added from palette, so our Aaw_please property will surely work. Newer versions tend to start with some chosen place in property list, but non-visual components usually have quite a few properties, so it shouldn't be a problem.
To determine when the component is first created (dropped on the form)?
Override "CreateWnd" and use the following if statement in it:
if (csDesigning in ComponentState) and not (csLoading in ComponentState) then
// We have first create
More detail here >>
Link

Designing a logging system [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
I have a program that needs to process files at batch. Instead of showing errors in message boxes on screen (which will pause the execution of the program) I need to show those error messages in a Log that the user can see as the program executes.
So I DON'T need a program-execution log like this Which logging library is better?
I am using now something derived from TRichEdit. Basically, a rich edit with few extra methods like AddError(s), AddWarn(s), AddVerbose(s), etc.
TRichLog = class(TMyRichEdit)
private
protected
Indent: Integer; { Indent new added lines x spaces }
public
constructor Create(AOwner: TComponent); override;
procedure AddBold (CONST Mesaj: string);
procedure AddMsg (CONST Mesaj: string);
procedure AddMsgLvl (CONST Mesaj: string; MsgType: Integer);
procedure AddColorMsg (CONST Mesaj: string; Culoare: TColor);
procedure AddVerb (CONST Mesaj: string);
procedure AddHint (CONST Mesaj: string);
procedure AddInfo (CONST Mesaj: string);
procedure AddPath (CONST Mesaj: string);
procedure AddWarn (CONST Mesaj: string);
procedure AddError (CONST Mesaj: string);
procedure AddMsgInt (CONST Mesaj: string; i: Integer); { Adds a message text followed by an integer }
procedure AddInteger (CONST i: Integer);
procedure AddFromFile (CONST FileName: string; MsgType: Integer); { Reads lines from the specified file and adds them to the log using the specified color. }
procedure AddEmptyRow;
procedure AddDateStamp;
procedure Append (RamLog: TObject); { RamLog will be typecased to TRamLog }
procedure SaveAsRtf (CONST FullPath: string);
procedure AppendToFile(CONST FullPath: string);
function VerbosityAsString: string;
published
property InsertTime: Boolean read FInsertTime write FInsertTime default FALSE;
property InsertDate: Boolean read FInsertDate write FInsertDate default FALSE;
property AutoScroll: Boolean read FAutoScroll write FAutoScroll default TRUE; { Automatically scroll to show the last line }
property Verbosity : Integer read FVerbosity write FVerbosity default vHints;
property OnLogError: TNotifyEvent read FLogError write FLogError; { Can be used to inform the application to automatically switch to log when an error is listed }
property OnLogWarn : TNotifyEvent read FLogWarn write FLogWarn;
But I would like to let user dynamically filter the context. For example the user should be able to hide all Verbose messages and keep only the warnings and errors. And if the user changes his mind, to put back the verbose messages.
Can the (existing) text in RichEdit be filtered this way? If not, I would like to get some pointers about how to reimplement it. I am thinking about writing my own format to keep the lines/messages. For example:
Cannot open file,#msgErr,#Bold
The I would have a TStringGrid to display only a limited number of lines (the ones that are visible on screen). This way I could have millions of lines without actually rendering all of them on screen. Time wasted parsing won't matter since I only have to parse the visible lines.
Requirements:
Support for colors as in RichEdit (red for errors, etc)
Lightweight
Should have two classes: a visual one (based on TStringGrid) and one non-visual for console programs (log to RAM and save the log later or display the messages as simple text in the console).
No hard dependencies (Delphi edition, Indy, DB engines, 3rd party controls etc)
Small (TRichEdit increases the size of the EXE file quite a lot)
One PAS file
An alternative would be not to use the Grid and to draw the text myself (for example in a TPanel-derived component). Or maybe such control already exists.
Any constructive critics of my ideas would be welcome. Do you have a better idea than using a Grid?
IMHO we may make a difference between:
Low-level logging, which targets developers and support;
High-level logging, which targets end-users.
From your comments, sounds like if you need the 2nd kind, which is usually also called "Audit Trail", especially in terms or regulation.
We usually implement high-level "Audit Trail" by storing the events in a database. For instance, a local high-performance SQLite3 database, or a MongoDB centralized instance.
Using a RDBMS (or NoSQL DB) has several advantages:
Its structure could be at the same time fixed (e.g. by defining categories), and evolutive (via some text fields, or even some foreign keys with other tables);
It is easy to interface with your existing UI, e.g. using powerful third-party grids, with sorting and filtering abilities;
You could use SQL to search within the log content, then even define a dedicated UI for most useful cases;
It is easy to maintain (a DELETE FROM ... would get rid of older undeded entries, and potentially keeping errors in the database).
We usually do this on production, using our Open Source SOA framework: all service calls can be directly written in a SQlite3 or MongoDB instance, without any code to write! Then you could even search within the parameters using JSON queries. And you still have integrated low-level logging available.

Component property not considered on runtime in Delphi XE2

I am developing a component but I can't make it consider a property set at design-time.
The following is an excerpt of the component:
TRVEditFrame = class(TFrame)
...
private
{ Private declarations }
FRVEditor:TCustomRichView;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
protected
function GetRVEditor:TCustomRichView;
procedure SetRVEditor(Editor:TCustomRichView);
published
property RVEditor:TCustomRichView read GetRVEditor write SetRVEditor;
end;
...
constructor TRVEditFrame.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
SetRVEditor(FRVEditor);
...
end;
function TRVEditFrame.GetRVEditor:TCustomRichView;
begin
Result:=FRVEditor;
end;
procedure TRVEditFrame.SetRVEditor(Editor:TCustomRichView);
begin
if Assigned(Editor) then begin
FRVEditor:=Editor;
end;
end;
I can register the component, place it in ther form and set FRVEditor on design-time.
Problem is when I run the application the code inside SetRVEditor() is not executed because Editor=nil.
If I was able to set FRVEditor on design-time, how come that it is=nil on run time ? How can I fix this ?
I add here my further comments because the explanation is too long
#Kenneth, thank you for your reply
TCustomRichView is part of a third part component set that manages
hypertext documents and has 4 more specialized descendents and you
are right, TCustomRichView shouldn't be used in a real application.
TRVEditFrame is the component I am developing.
The idea behind my component is to create one single frame (hence the choice of the component TFrame) with menus, shortcuts, popup menus etc to manage each of the 4 TCustomRichView descendents.
This is exactly the reason why I use TCustomRichView: I can "slot" any of the 4 descendents into my component-frame. This is the same principle of TDatasource that can be connected with TTAble and TQuery (they have the same ancestor).
I suppose the reason why the VCL doesn't link RVEditor to the TCustomRichView descendent I set on design-time is because TFrame has no OnCreate event, like TForm for instance.
So far I managed to solve the issue by calling TRVEditFrame.SetRVEditor manually in the TForm.OnCreate that hosts TRVEditFrame but I was wondering if there are better methods to do so and that is why I have asked advice here.
I know you can create a OnCreate event for TFrames as well, maybe I can place TRVEditFrame.SetRVEditor in there but, again, I was wondering if there was a better method.
Regarding the last part of your comment, I am aware of the register procedure but take into account the component is under development. When I develope components I never install them in the IDE because I prefer to keep the test stuff outside the "official" one.
I use this method and as soon as the component is ready then I register it with the procedure you mention. If I want to implement other features to the same component I can work on the test ones and keep on using the "official" one I have in the IDE at the same time.
My suggestion is to change the design in order to get the frame component referencing the custom editor by using the Notification method, instead of manually setting a property. So, change your frame class to
TRVEditFrame = class(TFrame)
...
private
{ Private declarations }
FRVEditor: TCustomRichView;
protected
procedure Notification(aComponent: TComponent; aOperation: TOperation); override;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
public
property RVEditor:TCustomRichView read FRVEditor;
end;
The implementation of the Notification method does the magic of connecting/disconnecting the frame to the custom editor
procedure TRVEditFrame.Notification(aComponent: TComponent;
aOperation: TOperation);
begin
inherited;
if aComponent is TCustomRichView then
if aOperation=opRemove then begin
if aComponent=FRVEditor then
FRVEditor := nil;
end else
FRVEditor := TCustomRichView(aComponent);
end;
I donĀ“t know if you need any special handling when an editor is set/reset to the frame, so this code does nothing in special, just assigns the component or nil to the FRVEditor data member at the proper moment.
You should create child components.
constructor TRVEditFrame.Create(AOwner: TComponent);
begin
inherited; // Create(AOwner);
FRVEditor := TRichView.Create(self);
end;
DFM streaming engine may load properties of already created class, but it cannot create the class for you for two reasons:
1) DFM can not know of any special tuning done on created component, like what should be constructor parameters (if any), what constructor to use (of many), which niotifications and event handlers to attach and so on.
2) DFM can not know which type the property should be. For example i published TStrings oroperty - object of which class should be created to feel in ? TStrings? TStringList? TRichEditStringList? THashedStringList ? they all were inherited from TStrings and thus any of them is fine choice for DFM - but not for the ocmponent you write.
Thus DFM streaming subsystem is responsible for saving and loading properties of objects, but creating those object is purely your responsibility.
I believe you also may cut the corners by learning how IDE Designer creates your forms and making RVEdit a variable rather than property:
TRVEditFrame = class(TFrame)
...
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
published
var RVEditor:TCustomRichView; // just like users' forms are created by IDE
end;
Then hopefully TFrame constructor would create the content for this variable for you. But this design is fragile because any outer code would be able by any stupid mistake to make something like MyEditFrame.RVEditor := ... and cause a memory leak and unexpected failures of all the attached connections between the editor and the frame.

Skinning of TOpenDialog and TOpenPictureDialog with VCL styles

Is it possible to skin the TOpenDialog and the TOpenPictureDialog with VCL syles?
The short answer is No, currently using Delphi XE2 or XE3 is not possible apply directly the Vcl Styles to a non VCL form (or to forms created outside of a VCL Application).
Now the long answer, is technically possible apply the Vcl Styles to these kind of dialogs, but this require a lot of work (believe me is a lot of work).
The key is using a WH_CBT Hook, detecting the HCBT_CREATEWND code and then checking if the class of the window is #32770 (the class for a dialog box.) from here you can replace the window procedure using the SetWindowLong function with the GWL_WNDPROC index.
That was the easy part, now which you have the control of the messages sent by the windows dialog
you must iterate over the child controls and replace the window procedure again using the
GWL_WNDPROC index. This can be done creating Wrapper class (this is the hard work) for each control used in a windows dialog (button, syslistview32, Combobox and so on)
This a sample of definition for a Wrapper class for the syslistview32 windows class.
TListViewWnd = class(TCustomListView)
private
FNewWndProc : Pointer;
FOrgWndProc : Pointer;
Fhwnd: THandle;
procedure CreateParams(var Params: TCreateParams); override;
procedure CreateWnd; override;
procedure DestroyWnd; override;
procedure WndProc(var Message: TMessage); override;
public
constructor Create(hwnd: THandle);
destructor Destroy;override;
end;
Finally you can use the already existing VCL Styles hooks defined for the Vcl Controls like the TListView on this way
TStyleManager.Engine.RegisterStyleHook(TListViewWnd, TListViewStyleHook);
I' ve already done part of the tasks described above, but is not finished yet, due which this kind of project require a lot of time.
For any interested the VCL Styles Utils Project now supports dialogs
You can find more information about this feature on this blog post.

Is it possible? TCollection descendant to implement storage of TPanel containers with arbitrary content

I'm new to component development in Delphi, therefore want to know, is it possible to implement my task at all.
I need to create a visual component (user control) based on TScrollBox, which will represent a bunch of TPanel, all that panels will be aligned as "Top" inside that TScrollBox and can have different Height. It has to act as TCollection (add, delete. reorder), and must allow users to add other controls into these panels at designtime.
I've created these classes for component:
type
TPanelsGrid = class;
TPanelsGridItem = class(TCollectionItem)
private
FPanel: TPanel;
procedure SetPanel(Value: TPanel);
function GetGrid: TPanelsGrid;
protected
function GetDisplayName: string; override;
public
constructor Create(Collection: TCollection); override;
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
published
// This is my TPanel object that should be used at designtime
// I thought "stored True" will serialize it automatically but I was wrong
property Panel: TPanel read FPanel write SetPanel stored True;
end;
TPanelsGridItems = class(TCollection)
private
FPanelsGrid: TPanelsGrid;
protected
function GetItem(Index: Integer): TPanelsGridItem;
procedure SetItem(Index: Integer; Value: TPanelsGridItem);
function GetOwner: TPersistent; override;
procedure Update(Item: TCollectionItem); override;
public
property EditorsGrid: TPanelsGrid read FPanelsGrid;
property Items[Index: Integer]: TPanelsGridItem
read GetItem write SetItem; default;
constructor Create(PanelsGrid: TPanelsGrid);
function Add: TPanelsGridItem;
procedure Delete(Index: Integer);
end;
TPanelsGrid = class(TScrollBox)
private
FItems: TPanelsGridItems;
procedure SetItems(Value: TPanelsGridItems);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property Items: TPanelsGridItems read FItems write SetItems;
end;
This component is working ok at designtime, I can add-delete panels in stack, when I'm dropping some control (e.g. TCheckbox) on any panel, it's displayed as "owned by that panel": e.g. I can't drag this checkbox out of panel.
But this checkbox isn't stored in DFM-file and isn't displayed in "Structure" window.
I guess there must be some manual serialization-deserialization of TPanel's content, but I have no idea how to do that. Can't find any example on Internet. Plase give me some guideline, if such implementation is possible at all.
Addition:
This is how my DFM-file fragment looks like after adding 3 panels into grid:
object PanelsGrid1 : TPanelsGrid
Left = 8
Top = 8
Width = 536
Height = 382
Anchors = [akLeft, akTop, akRight, akBottom]
TabOrder = 0
Items = <
item
end
item
end
item
end>
end
As you can see, all items are empty but I dropped there a checkbox and radiobutton into item #3.
After all I decided to give up using TCollection, because during testing of DefineProperties method I has consistent IDE crash. I think TCollection just wasn't designed for such task.
I founded an appropriate implementation inside Delphi sources inside of control ExtCtrls.TCustomCategoryPanelGroup. It maintains the stack of panels which can be added or removed both at design time and runtime. I created my own classes, using the source code of TCustomCategoryPanelGroup and TCustomCategoryPanel and it works as I want.
I think you can look at the TMS Poly List control
The TMS Advanced Poly List components
offer an extremely versatile and
flexible architecture to create
virtually any possible lists of items
in user interfaces. This is seen
typically but not limited to the new
Office 2010 application menu. Contrary
to most user interface list controls,
where a list consists of items of the
same type or a collection of items of
the same type, the TMS Advanced Poly
List components can hold polymorph
items. All items just need to descend
from the base class TCustomItem and
any inherited items can be added. TMS
Advanced Poly List components come
with a large set of prebuilt list
items but custom item classes can be
added by either descending of the
TCustomItem base class or any of the
classes already provided. There are
item classes to show as list section
item, text item with HTML formatting,
text item with buttons, item with
expand/collaps behaviour, item with
image and many more. Items can be
added in the polymorph lists either at
design time, with a rich design time
editor and at runtime via code.
Make sure your child panels have names. You can override TCollection.Notify and if Action is cnAdded, make sure the panel has an name.

Resources