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

Delphi字符串变体比较是否用loInvariantLocale?如何改用loUserLocale?

DevExpress TcxGrid 全局修改变体比较逻辑为loUserLocale的方案

问题背景

我的问题源于DevExpress TcxGrid的默认比较逻辑(用于数据排序),其核心代码实现于cxVariants.pas:

if VarIsEmpty(V1) then
  if VarIsEmpty(V2) then
    Result := 0
  else
    Result := -1
else
  if VarIsEmpty(V2) then
    Result := 1
  else
    if V1 = V2 then
      Result := 0
    else
      if V1 < V2 then
        Result := -1
      else
        Result := 1;

测试表明V1 < V2的行为等同于CompareStr(V1, V2, loInvariantLocale),但我们期望它以CompareStr(V1, V2, loUserLocale)的方式工作。

这种差异在cxGrid排序结果中很明显:东欧语言通常要求带变音符号的字符紧跟其原字符(如aā..zž的顺序),但loInvariantLocale会将所有变音符号字符排在非变音符号字符之后(即a..z, ā..ž的顺序)。

目前我可以重写cxGridDBTableView.Controller.OnCompare()事件,但想知道是否能从全局层面影响变体比较逻辑?


解决方案

1. 修改cxVariants源码(全局生效最直接方案)

直接修改cxVariants.pas中的变体比较核心代码,替换为用户区域设置的比较逻辑:

if VarIsEmpty(V1) then
  if VarIsEmpty(V2) then
    Result := 0
  else
    Result := -1
else
  if VarIsEmpty(V2) then
    Result := 1
  else
    if VarSameValue(V1, V2) then // 用VarSameValue替代直接=,避免类型匹配问题
      Result := 0
    else
      // 替换为loUserLocale的字符串比较
      Result := CompareStr(V1, V2, loUserLocale);

修改后重新编译DevExpress的组件包,所有依赖cxVariants的模块都会自动使用用户区域排序规则。

2. Hook变体比较函数(无需修改源码)

如果不想改动DevExpress源码,可以通过Hook技术拦截VarCompareValue函数(cxVariants中负责变体比较的核心函数),替换为自定义实现:

function CustomVarCompareValue(const V1, V2: Variant): Integer;
begin
  if VarIsEmpty(V1) then
    if VarIsEmpty(V2) then
      Result := 0
    else
      Result := -1
  else
    if VarIsEmpty(V2) then
      Result := 1
    else
      if VarSameValue(V1, V2) then
        Result := 0
      else
        Result := CompareStr(V1, V2, loUserLocale);
end;

// 在程序启动时完成Hook,需确保在DevExpress组件初始化前执行
initialization
  HookFunction(@VarCompareValue, @CustomVarCompareValue);

注:Hook实现需要用到第三方Hook库或自定义Hook代码,需保证Hook时机正确。

3. 全局绑定cxGrid默认比较事件

通过扩展cxGrid类,为所有cxGridDBTableView设置默认的OnCompare事件处理逻辑:

procedure GlobalGridCompareHandler(Sender: TcxCustomGridTableView;
  ARecord1, ARecord2: TcxCustomGridRecord; const AValue1, AValue2: Variant;
  var Compare: Integer);
begin
  if VarIsEmpty(AValue1) then
    if VarIsEmpty(AValue2) then
      Compare := 0
    else
      Compare := -1
  else
    if VarIsEmpty(AValue2) then
      Compare := 1
    else
      if VarSameValue(AValue1, AValue2) then
        Compare := 0
      else
        Compare := CompareStr(AValue1, AValue2, loUserLocale);
end;

// 程序启动时绑定全局默认事件
initialization
  cxGridDBTableViewClass.OnCompare := GlobalGridCompareHandler;

这种方式无需修改源码,所有新建的cxGridDBTableView都会自动应用该比较逻辑。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 10:57:25