You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调整Windows屏幕缩放后TTreeView复选框选中状态丢失求助

Delphi 11/12中TTreeView缩放后复选框状态丢失的解决方案

以下是针对Windows屏幕缩放调整后TTreeView复选框状态丢失问题的几种可行解决方案:


方案1:监听DPI变化消息,手动保存并恢复状态

Windows在屏幕缩放比例变更时会发送WM_DPICHANGED消息,我们可以通过捕获该消息,在控件重绘前后分别保存和恢复所有节点的复选状态。

实现代码示例:

unit MainForm;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, System.Generics.Collections;

type
  TForm1 = class(TForm)
    TreeView1: TTreeView;
  private
    FNodeCheckedStates: TDictionary<TTreeNode, Boolean>;
    procedure WMDpiChanged(var Message: TMessage); message WM_DPICHANGED;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

constructor TForm1.Create(AOwner: TComponent);
begin
  inherited;
  FNodeCheckedStates := TDictionary<TTreeNode, Boolean>.Create;
end;

destructor TForm1.Destroy;
begin
  FNodeCheckedStates.Free;
  inherited;
end;

procedure TForm1.WMDpiChanged(var Message: TMessage);
var
  Node: TTreeNode;
begin
  // 1. 保存当前所有节点的复选状态
  FNodeCheckedStates.Clear;
  Node := TreeView1.Items.GetFirstNode;
  while Node <> nil do
  begin
    FNodeCheckedStates.Add(Node, Node.Checked);
    Node := Node.GetNext;
  end;

  // 2. 执行系统默认的DPI缩放处理逻辑
  inherited;

  // 3. 恢复节点的复选状态
  Node := TreeView1.Items.GetFirstNode;
  while Node <> nil do
  begin
    if FNodeCheckedStates.ContainsKey(Node) then
      Node.Checked := FNodeCheckedStates[Node];
    Node := Node.GetNext;
  end;
end;

end.

方案2:自定义TTreeView子类,重载DPI相关处理方法

通过继承原生TTreeView,重载CreateWnd和WM_DPICHANGED方法,确保控件句柄重建或DPI变化时,状态能被正确保存和恢复。

实现代码示例:

unit CustomTreeView;

interface

uses
  Winapi.Windows, Winapi.Messages, System.Classes, Vcl.ComCtrls, System.Generics.Collections;

type
  TCustomTreeView = class(TTreeView)
  private
    FCheckedStates: TDictionary<TTreeNode, Boolean>;
    procedure SaveCheckedStates;
    procedure RestoreCheckedStates;
    procedure WMDpiChanged(var Message: TMessage); message WM_DPICHANGED;
  protected
    procedure CreateWnd; override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;

implementation

constructor TCustomTreeView.Create(AOwner: TComponent);
begin
  inherited;
  FCheckedStates := TDictionary<TTreeNode, Boolean>.Create;
end;

destructor TCustomTreeView.Destroy;
begin
  FCheckedStates.Free;
  inherited;
end;

procedure TCustomTreeView.SaveCheckedStates;
var
  Node: TTreeNode;
begin
  FCheckedStates.Clear;
  Node := Items.GetFirstNode;
  while Node <> nil do
  begin
    FCheckedStates.Add(Node, Node.Checked);
    Node := Node.GetNext;
  end;
end;

procedure TCustomTreeView.RestoreCheckedStates;
var
  Node: TTreeNode;
begin
  Node := Items.GetFirstNode;
  while Node <> nil do
  begin
    if FCheckedStates.ContainsKey(Node) then
      Node.Checked := FCheckedStates[Node];
    Node := Node.GetNext;
  end;
end;

procedure TCustomTreeView.WMDpiChanged(var Message: TMessage);
begin
  SaveCheckedStates;
  inherited;
  RestoreCheckedStates;
end;

procedure TCustomTreeView.CreateWnd;
begin
  SaveCheckedStates;
  inherited;
  RestoreCheckedStates;
end;

end.

使用时,将窗体中的原生TTreeView替换为这个自定义控件即可。


方案3:安装官方补丁

该问题属于Delphi 11/12的已知VCL控件DPI适配缺陷,Embarcadero可能已发布相关修复补丁。可以通过RAD Studio的补丁管理工具,检查并安装对应版本的最新累积补丁,直接解决底层问题。


注意事项

  • 如果TreeView节点是动态加载的,保存状态时需确保遍历所有已加载的节点;
  • 若节点存在动态删除的场景,需同步更新保存状态的字典,避免无效节点引用;
  • 自定义控件时,确保继承自正确的VCL基类,避免引发兼容性问题。

内容的提问来源于stack exchange,提问作者Stepel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 02:05:12