Delphi version: 10.1 update 2 (Berlin)
When applying a vcl theme to an application. When maximizing the form, it removes the borders causing border-aligned objects like: labels, menus, buttons to have a "cut" part.
In the example in question, the theme has a 6-pixel border (left, right, bottom).
You may also notice that the title bar appears with the top "cut off".
Is there any way to get around this situation, and make sure that the measures of borders and title bar are respected as specified in the theme?
Normal Window
Maximized Window
Unit1.pas
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls, Vcl.Menus;
type
TForm1 = class(TForm)
MainMenu1: TMainMenu;
este1: TMenuItem;
este2: TMenuItem;
este3: TMenuItem;
este4: TMenuItem;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Label4: TLabel;
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
end.
Unit1.dfm
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 441
ClientWidth = 704
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
Menu = MainMenu1
OldCreateOrder = False
Position = poScreenCenter
PixelsPerInch = 96
TextHeight = 13
object Label1: TLabel
Left = 0
Top = 0
Width = 704
Height = 13
Align = alTop
Caption = 'Novo Teste'
end
object Label2: TLabel
Left = 0
Top = 428
Width = 704
Height = 13
Align = alBottom
Caption = 'Novo Teste'
end
object Label3: TLabel
Left = 0
Top = 13
Width = 704
Height = 13
Align = alTop
Alignment = taRightJustify
Caption = 'Novo Teste'
end
object Label4: TLabel
Left = 0
Top = 415
Width = 704
Height = 13
Align = alBottom
Alignment = taRightJustify
Caption = 'Novo Teste'
end
object MainMenu1: TMainMenu
Left = 384
Top = 152
object este1: TMenuItem
Caption = 'Teste'
object este2: TMenuItem
Caption = 'Teste'
end
object este3: TMenuItem
Caption = 'Teste'
end
object este4: TMenuItem
Caption = 'Teste'
end
end
end
end
Related
I want to dynamically create some buttons in a TScrollBox (that has VertScrollBar.Vissible= False).
I want to programmatically bring some of those buttons in view, so I would like to use something like:
ScrollBox.VertScrollBar.Position:= i; //Does not work
However, the box won't scroll to the indicated position unless the VertScrollBar.Vissible= True.
Note: ScrollBy() works, but I don't want to use that.
How to circumvent this behavior?
(A "solution" would be to let the scrollbars visible and hide them outside the screen (place the scrollbox in a panel))
Code:
unit UnitVert;
interface
uses
System.SysUtils, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm3 = class(TForm)
ScrollBox1: TScrollBox;
Button1: TButton;
Button2: TButton;
procedure Button2Click(Sender: TObject);
end;
var
Form3: TForm3;
implementation {$R *.dfm}
procedure TForm3.Button2Click(Sender: TObject);
begin
ScrollBox1.VertScrollBar.Position:= -20;
//ScrollBox1.ScrollBy(0, -20); //Works
end;
end.
object Form3: TForm3
Left = 0
Top = 0
Caption = 'Form3'
ClientHeight = 336
ClientWidth = 635
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object ScrollBox1: TScrollBox
Left = 176
Top = 75
Width = 283
Height = 203
HorzScrollBar.Visible = False
VertScrollBar.Visible = False
TabOrder = 0
object Button1: TButton
Left = 188
Top = 132
Width = 123
Height = 99
Caption = 'Dummy'
TabOrder = 0
end
end
object Button2: TButton
Left = 26
Top = 50
Width = 111
Height = 51
Caption = 'Test'
TabOrder = 1
OnClick = Button2Click
end
end
Works as expected
Cannot reproduce your issue with D7 on Win7:
Scrollbox1.HorzScrollBar.Visible:= FALSE;
Scrollbox1.VertScrollBar.Visible:= FALSE;
Scrollbox1.ScrollBy( -30, -45 );
...moves the viewport 30 px to the left and 45 px to the top. Also note that the first parameter is X (horizontal) and the second parameter is Y (vertical) - actually anything I've seen in life was always in the X,Y order.
Why invisible scrollbars won't work
The method TWinControl.ScrollBy() includes this code:
IsVisible := (FHandle <> 0) and IsWindowVisible(FHandle);
if IsVisible then ScrollWindow(FHandle, DeltaX, DeltaY, nil, nil);
...which means: it is essentially using the WinAPI's ScrollWindow() function. Changing the position of one of the scrollbars executes TControlScrollBar.SetPosition(), which in turn calls .ScrollBy() again for just one dimension:
OldPos := FPosition;
if Kind = sbHorizontal then
FControl.ScrollBy(OldPos - Value, 0) else
FControl.ScrollBy(0, OldPos - Value);
...and since this time the parent control is the scrollbar (not the scrollbox) its invisibility prevents the WinAPI function from being called. Content wise there's no gain in using scrollbars - they just conveniently remember what you already scrolled.
I have a problem that docking form hides some other components inside of panel.
This is simple example of my problem: I have Panel1 with DockSite set to True. Inside Panel1 is Panel2. Panel2 alignts to client (Align=alClient). Inside Panel2 is memo field that is also aligned to client. It overlaps the entire Panel1.
I have another form (Form2) that I want to dock to Panel1. But it overlaps entire Panel1 and hides memo filed. I want to overlap only part of Panel1 (width of the form) and move Panel2 to the rigth or to te left.
Main form:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Panel1: TPanel;
Panel2: TPanel;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses Unit2;
procedure TForm1.Button1Click(Sender: TObject);
begin
Form2.Show;
end;
end.
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 333
ClientWidth = 754
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -12
Font.Name = 'Segoe UI'
Font.Style = []
PixelsPerInch = 96
TextHeight = 15
object Button1: TButton
Left = 288
Top = 271
Width = 153
Height = 41
Caption = 'Show dockable form'
TabOrder = 0
OnClick = Button1Click
end
object Panel1: TPanel
Left = 8
Top = 8
Width = 729
Height = 257
Caption = 'Panel1'
DockSite = True
TabOrder = 1
object Panel2: TPanel
Left = 1
Top = 1
Width = 727
Height = 255
Align = alClient
Caption = 'Panel2'
TabOrder = 0
ExplicitLeft = 360
ExplicitTop = 8
ExplicitWidth = 337
ExplicitHeight = 241
object Memo1: TMemo
Left = 1
Top = 1
Width = 725
Height = 253
Align = alClient
Lines.Strings = (
'Memo1')
TabOrder = 0
ExplicitWidth = 216
ExplicitHeight = 144
end
end
end
end
Docking form:
unit Unit2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm2 = class(TForm)
Label1: TLabel;
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
end.
object Form2: TForm2
Left = 0
Top = 0
Caption = 'Form2'
ClientHeight = 103
ClientWidth = 273
Color = clBtnFace
DragKind = dkDock
DragMode = dmAutomatic
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -12
Font.Name = 'Segoe UI'
Font.Style = []
PixelsPerInch = 96
TextHeight = 15
object Label1: TLabel
Left = 22
Top = 24
Width = 227
Height = 45
Caption = 'Dockable form'
DragKind = dkDock
DragMode = dmAutomatic
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -33
Font.Name = 'Segoe UI'
Font.Style = []
ParentFont = False
end
end
The dynamics soon become difficult to master with changing dock sites and alignment changes, but I think the following will do what you ask for.
Change the hierarchy of Form1 to the following and note that Panel2 and Memo1 both are childs of Panel1. Panel2 will act as the docking target. I have set Panel2.Width to 8, to have a visual area where to drop Form2. Here are the essential properties:
object Form1: TForm1
object Button1: TButton
object Panel1: TPanel
Caption = 'Panel1'
object Panel2: TPanel
Width = 8
Align = alRight
Caption = 'Panel2'
DockSite = True
OnDockDrop = Panel2DockDrop
OnDockOver = Panel2DockOver
OnUnDock = Panel2UnDock
end
object Memo1: TMemo
Align = alClient
end
end
end
When Form2 is dragged over Panel2 the OnDockOver event is triggered. Panel2 sets its width to half the width of Panel1, which in turn reduces the Memo1 width with the same amount. (Change as you need)
procedure TForm1.Panel2DockOver(Sender: TObject; Source: TDragDockObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
begin
Panel2.Width := Panel1.Width div 2;
end;
When Form2 is dropped on Panel2, the DockRect of the form is set to the rect of Panel2.
procedure TForm1.Panel2DockDrop(Sender: TObject; Source: TDragDockObject; X, Y: Integer);
begin
Source.DockRect := Rect(Panel2.Width, Panel2.Top, Panel2.Width, Panel2.Height);
end;
When Form2 is undocked from Panel2 it reduces its width to the 8 pixels, which again widen the Memo1 to its original width.
procedure TForm1.Panel2UnDock(Sender: TObject; Client: TControl; NewTarget: TWinControl;
var Allow: Boolean);
begin
Panel2.Width := 8;
end;
Here's the complete .dfm of Form1:
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 333
ClientWidth = 425
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -12
Font.Name = 'Segoe UI'
Font.Style = []
OldCreateOrder = True
PixelsPerInch = 96
TextHeight = 15
object Button1: TButton
Left = 120
Top = 284
Width = 153
Height = 41
Caption = 'Show dockable form'
TabOrder = 0
OnClick = Button1Click
end
object Panel1: TPanel
Left = 8
Top = 8
Width = 400
Height = 257
Caption = 'Panel1'
TabOrder = 1
object Panel2: TPanel
Left = 391
Top = 1
Width = 8
Height = 255
Align = alRight
Caption = 'Panel2'
DockSite = True
TabOrder = 0
OnDockDrop = Panel2DockDrop
OnDockOver = Panel2DockOver
OnUnDock = Panel2UnDock
ExplicitLeft = 394
end
object Memo1: TMemo
Left = 1
Top = 1
Width = 390
Height = 255
Align = alClient
Lines.Strings = (
'Memo1')
TabOrder = 1
ExplicitWidth = 725
ExplicitHeight = 253
end
end
end
I have written a small test VCL application with a monitor PPI of 96.
The application has a TGridPanel on it with a absolute pixel sized column.
On that column I placed a TComboBox and aligned it alClient.
Here is the DFM code:
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 182
ClientWidth = 514
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
OnShow = FormShow
PixelsPerInch = 96
TextHeight = 13
object GridPanel1: TGridPanel
Left = 0
Top = 0
Width = 514
Height = 182
Align = alClient
Caption = 'GridPanel1'
ColumnCollection = <
item
Value = 100.000000000000000000
end
item
SizeStyle = ssAbsolute
Value = 150.000000000000000000
end>
ControlCollection = <
item
Column = 0
Control = Button1
Row = 0
end
item
Column = 1
Control = ComboBox1
Row = 0
end
item
Column = 0
Control = Edit1
Row = 1
end>
RowCollection = <
item
Value = 50.000000000000000000
end
item
Value = 50.000000000000000000
end>
TabOrder = 0
object Button1: TButton
Left = 1
Top = 1
Width = 362
Height = 21
Align = alTop
Caption = 'Button1'
TabOrder = 0
OnClick = Button1Click
end
object ComboBox1: TComboBox
Left = 363
Top = 1
Width = 150
Height = 21
Align = alClient
TabOrder = 1
Text = 'ComboBox1'
end
object Edit1: TEdit
Left = 1
Top = 91
Width = 362
Height = 21
Align = alTop
TabOrder = 2
Text = 'Edit1'
end
end
end
and the PAS code:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls;
type
TForm1 = class(TForm)
GridPanel1: TGridPanel;
Button1: TButton;
ComboBox1: TComboBox;
Edit1: TEdit;
procedure Button1Click(Sender: TObject);
procedure FormShow(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
PPI: Integer;
begin
PPI := Integer.Parse(Edit1.Text);
GridPanel1.ScaleForPPI(PPI);
end;
procedure TForm1.FormShow(Sender: TObject);
begin
Edit1.Text := Screen.PixelsPerInch.ToString;
end;
end.
I then changed the custom scaling factor to 125 in Windows 10 in the advanced scaling settings.
After signing off and signing on again when I run the application again the drop down button of the combo box is not visible any more.
How do you deal with this problem?
I tried to call GridPanel1.ScaleForPPI(96) which restores the drop down button on the combo box. This kind of defeats the purpose of PPI scaling though, doesn't it?
The problem is gone in Delphi 10.3.1.
So this is a bug in at least Delphi 10.1 (and possible other older versions).
RichEdit control stop drawing text when it became a parent for other control.
Is this a feature or a bug?
Is it possible to make RichEdit to be a parent for other control?
Check out next app:
-- Form1.dfm ---
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 282
ClientWidth = 418
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object Button1: TButton
Left = 24
Top = 8
Width = 75
Height = 25
Caption = 'Button1'
TabOrder = 0
OnClick = Button1Click
end
object RichEdit1: TRichEdit
Left = 16
Top = 72
Width = 145
Height = 105
Font.Charset = RUSSIAN_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
Lines.Strings = (
'RichEdit1')
ParentFont = False
TabOrder = 1
end
end
-- Form1.dfm ---
--- Unit1.pas ---
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ComCtrls;
type
TForm3 = class(TForm)
Button1: TButton;
RichEdit1: TRichEdit;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form3: TForm3;
implementation
{$R *.dfm}
procedure TForm3.Button1Click(Sender: TObject);
begin
Button1.Parent := RichEdit1;
RichEdit1.Invalidate;
end;
end.
--- Unit1.pas ---
Test under Delphi XE5 + Win 7.
I want to create RichEdit with Edit button like this
This is the result that I want to get - RichEdit with DropDown Editor:
Use an interposer class that handles the WM_PAINT message like so:
type
TRichEdit = class(Vcl.ComCtrls.TRichEdit)
protected
procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
end;
procedure TRichEdit.WMPaint(var Message: TWMPaint);
begin
DefaultHandler(Message);
end;
For reasons lost in the mists of time, TCustomRichEdit does some special handling of WM_PAINT that was only actually needed for the original version of the rich edit DLL. Moreover, this special handling breaks normal painting when another control is parented to the rich edit. As such, fixing the issue requires re-establishing standard VCL/Windows paint handling, which is what the code above does.
That said, I doubt nesting a button inside a rich edit is really what you want - the text won't wrap around it, for example.
Is it possible to do LiveBinding between controls, i.e. take 2 edit boxes and get the result of adding their contents together into a label. I'm sure it is, I just don't know where to start
Thanks
Have a look at the samples. SVN repository URL: https://radstudiodemos.svn.sourceforge.net/svnroot/radstudiodemos/branches/RadStudio_XE2/LiveBindings
An example:
----- Unit1.dfm -----
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 286
ClientWidth = 426
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object Label1: TLabel
Left = 8
Top = 62
Width = 48
Height = 13
Caption = 'Edit1Edit2'
end
object Edit1: TEdit
Left = 8
Top = 8
Width = 121
Height = 21
TabOrder = 0
Text = 'Edit1'
OnChange = EditChange
end
object Edit2: TEdit
Left = 8
Top = 35
Width = 121
Height = 21
TabOrder = 1
Text = 'Edit2'
OnChange = EditChange
end
object BindingsList1: TBindingsList
Methods = <>
OutputConverters = <>
UseAppManager = True
Left = 20
Top = 5
object BindExpressionLabel11: TBindExpression
Category = 'Binding Expressions'
ControlComponent = Label1
SourceComponent = BindScope1
SourceExpression = 'Edit1.Text + Edit2.Text'
ControlExpression = 'Caption'
NotifyOutputs = False
Direction = dirSourceToControl
end
end
object BindScope1: TBindScope
Left = 192
Top = 16
end
end
----- Unit1.pas -----
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Data.Bind.EngExt, Vcl.Bind.DBEngExt,
System.Rtti, System.Bindings.Outputs, Vcl.Bind.Editors, Data.Bind.Components,
Vcl.StdCtrls;
type
TForm1 = class(TForm)
Edit1: TEdit;
Edit2: TEdit;
Label1: TLabel;
BindingsList1: TBindingsList;
BindExpressionLabel11: TBindExpression;
BindScope1: TBindScope;
procedure EditChange(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses
System.Bindings.Helper;
procedure TForm1.EditChange(Sender: TObject);
begin
TBindings.Notify(Sender, 'Text');
end;
end.
How to use the IDE designer to produce the result:
put two edits (Edit1, Edit2), a label (Label1) and a TBindScope (BindScope1) on your form (Form1).
create an event handler for both edits' OnChange event (EditChange).
select Label1, expand the drop-down of LiveBindings property, select 'New Live Binding...', select TBindExpression
edit properties of the newly created BindExpressionLabel11: assign Caption to ControlExpression, BindScope1 to SourceComponent, Edit1.Text + Edit2.Text to SourceExpression
The sample project at the (Default) location of:
C:\Users\Public\Documents\RAD Studio\9.0\Samples\Delphi\LiveBinding\Components\bindexpression\fmx\BindExpressionSampleProject.dproj
does precisely that.
You don't need to TBindScope to bind components together. Say you have edit1 and edit2 on the form. If you set edit2 BindingSource to edit1 it will be link to changes to edit1