叠加图标被拉伸
Overlay icons are painted stretched
我正在将 C++Builder 2009 项目移植到 C++Builder 11。
出于某种奇怪的原因,覆盖图标在继承自 TTreeView
的自定义对象中被拉伸绘制。
当使用 C++Builder 2009 构建时,它显然可以正常工作。
我没有在里面做任何定制绘画。
当我创建一个新项目时,在设计时添加一个 TTreeView 和 TImageList,不要更改任何默认设置,只需添加两个图标和两个项目,按照下面的图像和代码,一切似乎都工作正常:
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
ImageList1->Overlay(0, 0) ;
TreeView1->Items->Item[1]->OverlayIndex = 0 ;
}
当我创建自己的 TTreeView
后代并执行相同操作时,覆盖图标被拉长(在右侧):
//---------------------------------------------------------------------------
class MyTreeView : public TTreeView
{
public :
MyTreeView(TPanel *TreeViewLocation)
: TTreeView(TreeViewLocation)
{
Parent = TreeViewLocation ;
Align = alClient ;
}
virtual __fastcall ~MyTreeView() {}
};
//---------------------------------------------------------------------------
MyTreeView *TreeView2 ;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
ImageList1->Overlay(0, 0) ;
TreeView1->Items->Item[1]->OverlayIndex = 0 ;
TreeView2 = new MyTreeView(Panel1) ;
TreeView2->Items->Add(NULL, L"Item1")->ImageIndex = 1 ;
TreeView2->Items->Add(NULL, L"Item2")->ImageIndex = 1 ;
TreeView2->Images = ImageList1 ;
TreeView2->Items->Item[1]->OverlayIndex = 0 ;
}
//---------------------------------------------------------------------------
在移植的项目中使用相同的图标和ImageList:
就好像 TTreeView
要求 TImageList
以错误的 Canvas 尺寸绘制覆盖图标?
仅供参考,这是使用 C++Builder 2009 构建的完全相同代码的结果:
编辑
剧情变厚了
我刚刚在 OS 的范围内(在 VM VirtualBox 中)测试了这个,我没有在旧的 OS(XP、Vista、W7、W8 甚至 W10)上看到问题(较旧的不是最新版本))。
但是,我确实在我的 W10 开发系统(最新)上看到了它
我还在 VirtualBox 的 W11 上测试了它,问题也存在。所以这不仅仅是我的系统,它与最新的 Windows 更新有关。
超级讨厌..
您在这里看到的问题并不完全是因为 TTreeView
后代具有拉伸的覆盖图标。事实上,拉伸的覆盖图标来自任何 TTreeview
(库存或继承),其中 Images
属性 被分配 在组件流进程之外 。您的 designer-placed 树视图没有显示问题,因为它的 Images
属性 是在从 DFM(组件流处理)读取时设置的。如果您在对象检查器中为 TreeView1
取消链接 Images
属性 并在代码中分配它,那么该树视图将显示完全相同的问题。
好的,那么现在我们重新定义了问题,到底是怎么回事?
在 TCustomTreeView.SetImages
中,要做的第一件事是调用 SetDPIScaling(True)
,如果在运行时在具有 window 句柄的树视图上完成(并且如果 Per Monitor V2 处于活动状态)发送 CCM_DPISCALE
到树视图(MS says 此 在 Tree-View 控件 中启用自动高每英寸点数 (dpi) 缩放。这样做的净效果(在 100% 缩放显示中)是覆盖图标缩放到它们似乎预期的大小的两倍,否则应该是。
看起来这可能是解决 RSP-24440 in RAD Studio 10.4, and looks like it is also reported (or very nearly the same issue is reported) in RSP-36397 的工作的意外副作用。
我们可以解决这个问题吗?是的!在分配给任何树视图的 Images
属性 之前,您必须设置图像列表的受保护 Scaled
属性(此 Scaled
属性 似乎保留 False
在 TImageList
在所有正常情况下),然后这将导致树视图跟随对 SetDPIScaling(True)
的调用和对 SetDPIScaling(False)
的另一个调用,然后问题就没有了发生。
啊,但是Scaled
是受保护的属性,那我们怎么设置呢?
在 Delphi 中,您可以使用 起重机 class,像这样:
type
TImageListHoist = class(TImageList);
...
var
TreeView2: MyTreeView;
procedure TForm1.FormCreate(Sender: TObject);
var
Node: TTreeNode;
begin
// There can be 4 overlay indices, numbered 0-3
ImageList1.Overlay(1, 0); // image index 1 is to be overlay index 0
TImageListHoist(ImageList1).Scaled := True; // set Scaled to True using the hoist class
TreeView1.Images := ImageList1; // connect the image list
Node := TreeView1.Items.Item[0];
Node.OverlayIndex := 0; // which overlay mask from the image list to use
Node := TreeView1.Items.Item[1];
Node.OverlayIndex := 0;
TreeView2 := MyTreeView.CreateHere(Panel1);
TreeView2.Width := 180;
TreeView2.Images := ImageList1; // connect the image list
Node := TreeView2.Items.Add(nil, 'TreeView2 Item1');
Node.ImageIndex := 0;
Node.SelectedIndex := 0;
Node.OverlayIndex := 0;
Node := TreeView2.Items.Add(nil, 'TreeView2 Item2');
Node.ImageIndex := 0;
Node.SelectedIndex := 0;
Node.OverlayIndex := 0;
end;
在 C++ 中,您需要创建一个浅层后代 class,它只为您访问受保护的成员并进行适当的转换:
MyTreeView *TreeView2;
class MyImageList: public TImageList
{
public:
void SetScaledTrue() { Scaled = true; }
};
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
TTreeNode *Node;
// There can be 4 overlay indices, numbered 0-3
ImageList1->Overlay(1, 0); // image index 1 is to be overlay index 0
((MyImageList *)ImageList1)->SetScaledTrue(); // use the temp class to set Scaled
TreeView1->Images = ImageList1; // connect the image list
Node = TreeView1->Items->Item[0];
Node->OverlayIndex = 0; // which overlay mask from the image list to use
Node = TreeView1->Items->Item[1];
Node->OverlayIndex = 0;
TreeView2 = new MyTreeView(Panel1);
TreeView2->Width = 180;
TreeView2->Images = ImageList1; // connect the image list
Node = TreeView2->Items->Add(NULL, L"TreeView2 Item1");
Node->ImageIndex = 0;
Node->SelectedIndex = 0;
Node->OverlayIndex = 0;
Node = TreeView2->Items->Add(NULL, L"TreeView2 Item2");
Node->ImageIndex = 0;
Node->SelectedIndex = 0;
Node->OverlayIndex = 0;
}
我正在将 C++Builder 2009 项目移植到 C++Builder 11。
出于某种奇怪的原因,覆盖图标在继承自 TTreeView
的自定义对象中被拉伸绘制。
当使用 C++Builder 2009 构建时,它显然可以正常工作。
我没有在里面做任何定制绘画。
当我创建一个新项目时,在设计时添加一个 TTreeView 和 TImageList,不要更改任何默认设置,只需添加两个图标和两个项目,按照下面的图像和代码,一切似乎都工作正常:
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
ImageList1->Overlay(0, 0) ;
TreeView1->Items->Item[1]->OverlayIndex = 0 ;
}
当我创建自己的 TTreeView
后代并执行相同操作时,覆盖图标被拉长(在右侧):
//---------------------------------------------------------------------------
class MyTreeView : public TTreeView
{
public :
MyTreeView(TPanel *TreeViewLocation)
: TTreeView(TreeViewLocation)
{
Parent = TreeViewLocation ;
Align = alClient ;
}
virtual __fastcall ~MyTreeView() {}
};
//---------------------------------------------------------------------------
MyTreeView *TreeView2 ;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
ImageList1->Overlay(0, 0) ;
TreeView1->Items->Item[1]->OverlayIndex = 0 ;
TreeView2 = new MyTreeView(Panel1) ;
TreeView2->Items->Add(NULL, L"Item1")->ImageIndex = 1 ;
TreeView2->Items->Add(NULL, L"Item2")->ImageIndex = 1 ;
TreeView2->Images = ImageList1 ;
TreeView2->Items->Item[1]->OverlayIndex = 0 ;
}
//---------------------------------------------------------------------------
在移植的项目中使用相同的图标和ImageList:
就好像 TTreeView
要求 TImageList
以错误的 Canvas 尺寸绘制覆盖图标?
仅供参考,这是使用 C++Builder 2009 构建的完全相同代码的结果:
编辑
剧情变厚了
我刚刚在 OS 的范围内(在 VM VirtualBox 中)测试了这个,我没有在旧的 OS(XP、Vista、W7、W8 甚至 W10)上看到问题(较旧的不是最新版本))。 但是,我确实在我的 W10 开发系统(最新)上看到了它 我还在 VirtualBox 的 W11 上测试了它,问题也存在。所以这不仅仅是我的系统,它与最新的 Windows 更新有关。 超级讨厌..
您在这里看到的问题并不完全是因为 TTreeView
后代具有拉伸的覆盖图标。事实上,拉伸的覆盖图标来自任何 TTreeview
(库存或继承),其中 Images
属性 被分配 在组件流进程之外 。您的 designer-placed 树视图没有显示问题,因为它的 Images
属性 是在从 DFM(组件流处理)读取时设置的。如果您在对象检查器中为 TreeView1
取消链接 Images
属性 并在代码中分配它,那么该树视图将显示完全相同的问题。
好的,那么现在我们重新定义了问题,到底是怎么回事?
在 TCustomTreeView.SetImages
中,要做的第一件事是调用 SetDPIScaling(True)
,如果在运行时在具有 window 句柄的树视图上完成(并且如果 Per Monitor V2 处于活动状态)发送 CCM_DPISCALE
到树视图(MS says 此 在 Tree-View 控件 中启用自动高每英寸点数 (dpi) 缩放。这样做的净效果(在 100% 缩放显示中)是覆盖图标缩放到它们似乎预期的大小的两倍,否则应该是。
看起来这可能是解决 RSP-24440 in RAD Studio 10.4, and looks like it is also reported (or very nearly the same issue is reported) in RSP-36397 的工作的意外副作用。
我们可以解决这个问题吗?是的!在分配给任何树视图的 Images
属性 之前,您必须设置图像列表的受保护 Scaled
属性(此 Scaled
属性 似乎保留 False
在 TImageList
在所有正常情况下),然后这将导致树视图跟随对 SetDPIScaling(True)
的调用和对 SetDPIScaling(False)
的另一个调用,然后问题就没有了发生。
啊,但是Scaled
是受保护的属性,那我们怎么设置呢?
在 Delphi 中,您可以使用 起重机 class,像这样:
type
TImageListHoist = class(TImageList);
...
var
TreeView2: MyTreeView;
procedure TForm1.FormCreate(Sender: TObject);
var
Node: TTreeNode;
begin
// There can be 4 overlay indices, numbered 0-3
ImageList1.Overlay(1, 0); // image index 1 is to be overlay index 0
TImageListHoist(ImageList1).Scaled := True; // set Scaled to True using the hoist class
TreeView1.Images := ImageList1; // connect the image list
Node := TreeView1.Items.Item[0];
Node.OverlayIndex := 0; // which overlay mask from the image list to use
Node := TreeView1.Items.Item[1];
Node.OverlayIndex := 0;
TreeView2 := MyTreeView.CreateHere(Panel1);
TreeView2.Width := 180;
TreeView2.Images := ImageList1; // connect the image list
Node := TreeView2.Items.Add(nil, 'TreeView2 Item1');
Node.ImageIndex := 0;
Node.SelectedIndex := 0;
Node.OverlayIndex := 0;
Node := TreeView2.Items.Add(nil, 'TreeView2 Item2');
Node.ImageIndex := 0;
Node.SelectedIndex := 0;
Node.OverlayIndex := 0;
end;
在 C++ 中,您需要创建一个浅层后代 class,它只为您访问受保护的成员并进行适当的转换:
MyTreeView *TreeView2;
class MyImageList: public TImageList
{
public:
void SetScaledTrue() { Scaled = true; }
};
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
TTreeNode *Node;
// There can be 4 overlay indices, numbered 0-3
ImageList1->Overlay(1, 0); // image index 1 is to be overlay index 0
((MyImageList *)ImageList1)->SetScaledTrue(); // use the temp class to set Scaled
TreeView1->Images = ImageList1; // connect the image list
Node = TreeView1->Items->Item[0];
Node->OverlayIndex = 0; // which overlay mask from the image list to use
Node = TreeView1->Items->Item[1];
Node->OverlayIndex = 0;
TreeView2 = new MyTreeView(Panel1);
TreeView2->Width = 180;
TreeView2->Images = ImageList1; // connect the image list
Node = TreeView2->Items->Add(NULL, L"TreeView2 Item1");
Node->ImageIndex = 0;
Node->SelectedIndex = 0;
Node->OverlayIndex = 0;
Node = TreeView2->Items->Add(NULL, L"TreeView2 Item2");
Node->ImageIndex = 0;
Node->SelectedIndex = 0;
Node->OverlayIndex = 0;
}