【问题标题】:C# WinForms control in .Net COM Server won't redraw.Net COM Server 中的 C# WinForms 控件不会重绘
【发布时间】:2010-12-28 16:21:22
【问题描述】:

我有一个 COM 服务器应用程序,它使用一些 WinForms 控件,坦率地说,当它们的属性(文本、背景色等)发生更改时,这些控件似乎不会重绘。

我尝试过调用txtControlName.Invalidate() 和.Update(),但似乎都没有任何影响。

我们坚持 .Net 2.0 并提供视觉响应和有趣的 UI 是一项业务需求,虽然我意识到我可能可以使用 WinAPI SendMessage() 强制重绘,但我宁愿让 .Net 处理所有这些东西 - - 已有足够多的 hack,我们不想再添加了。

最后,我想指出 .Net COM 服务器托管在非托管应用程序中。

谢谢!

汤姆

附录:

此时实际更新代码如下:

public void UpdateSt(int? seq, string text) {

    Control.CheckForIllegalCrossThreadCalls = true;

    if (this.lblText.InvokeRequired) {

        this.lblText.Invoke(new MethodInvoker(() => {

            UpdateSt(seq, text);

        }));

    } else {

        if (text != String.Empty) {

            lblText.Text = text;
            //WinAPI.InvalidateRect(lblText, true);
            lblText.Refresh();
            //WinAPI.SendMessage(lblText.Handle, (uint)WinAPI.WM.SETTEXT, 0, new StringBuilder(text));
            DebugTrace("lblText says '" + lblText.Text + "', is supposed to say '" + text + "'.");
        }

        if (imgSeq.HasValue) {

            // not implemented yet

        }

    }

}

附录#2:

Spy++ 报告来自 .Net 的 Text setter 的 WM_SETTEXT 调用失败,就像我自己的 WM_SETTEXT 调用一样。

附录 #3:已解决

事实证明,问题在于消息泵损坏和一些弊大于利的 P/Invoke 调用的组合。由于库是通过 COM 启动的,因此没有 .Net 消息泵,并且在新线程中添加 Application.Run() 允许一切以应有的方式响应。确保所有基于表单的交互都从具有该调用的线程开始似乎是一个好主意。

【问题讨论】:

    标签: c# .net winforms user-interface com


    【解决方案1】:

    一般诊断是消息泵有问题。您并没有抱怨控件根本不会自行绘制,因此这似乎不太可能完全破坏。如果这是一个偶然的绘画问题,那么诊断就是你遇到了线程问题。换句话说,您正在从错误的线程更新控件属性或调用 Invalidate/Update。

    Windows 窗体对此具有内置诊断功能,在附加调试器时处于活动状态。确保您没有将 Control.CheckForIllegalCrossThreadCalls 设置为 false。

    下一个要看的地方是消息泵本身。当您使用 Show() 方法而不是 ShowDialog() 显示表单时,您的非托管消息泵将发送消息。这本身就有一些不良的副作用,键盘加速器将不再起作用,标签也不起作用。使用 ShowDialog() 检查问题是否消失。


    您的评论提供了另一个提示,说明问题可能是什么。如果当您知道您正在从另一个线程调用时从 InvokeRequired 得到 False 并且您没有看到任何可见的更新迹象,那么您使用了错误的 Form 对象引用。可能是您使用 new 运算符创建的。确保您使用现有的,Application.OpenForms[] 可以为您提供参考,如果您无法获得一个。

    【讨论】:

    • 奇怪的是,Control.InvokeRequired 始终返回 false。我已经开始手动调用 Invoke() 命令来更新控件,但仍然没有发生任何事情。知道了这一点,排除任何明显的线程问题是否安全?
    • 要验证这一点,您可以使用 IWin32WindowHandle 检索要更新的项目的 HWND,并将其与使用 Spy++ 找到的进行比较。
    • @nobugz -- 有趣的是,我的印象是(并且过去这样做没有问题)通过更改实例化的表单/控件来更改控件属性。如果不是这样,那么我的想法可能会暂时被打破。老实说,我还没有确认我是从另一个线程调用的,几乎整个插件都是从事件处理程序运行的,而且它们过去一直处于单独的线程中。
    • @nobugz 您还建议消息泵可能已损坏。我不确定如何验证这一点,因为我认为它通常是在框架内部处理的。如何检查?
    • 不太确定,我更喜欢“错误的表单引用”解释。然而,它通常是在您的非托管代码中进行调度的消息泵。 ShowDialog() 角度是如何产生的?
    猜你喜欢
    • 1970-01-01
    • 2012-01-14
    • 1970-01-01
    • 2018-01-10
    • 2014-10-14
    • 2015-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多