从 RichTextBox 中删除(或更新)图像
我有一个与管理 RichTextBox 中的 OLE 对象相关的问题。
到目前为止我发现了很多信息,但并不完全是我需要的,所以我将首先做一个快速介绍(我也希望有人会觉得这有帮助)。
1. 到目前为止我所知道的
首先,我使用 OLE 将图像(或任何 ActiveX)插入到 RichTextBox 中。 这应该是执行此操作的“正确方法”,因为不涉及剪贴板,并且您可以插入所需的任何 ActiveX 控件。 CodeProject 上有一篇文章 (MyExtRichTextBox) 解释了如何执行此操作 (具有完整的源代码),但简而言之:
使用 P/Invoke, OleCreateFromFile
函数从 ole32.dll 导入,用于从图像文件创建 OLE 对象。
int hresult = OleCreateFromFile(...);
函数返回一个 IOleObject
实例,然后必须通过 REOBJECT
结构引用该实例:
REOBJECT reoObject = new REOBJECT();
reoObject.cp = 0; // charated index for insertion
reoObject.clsid = guid; // iOleObject class guid
reoObject.poleobj = Marshal.GetIUnknownForObject(pOleObject); // actual object
// etc. (set other fields
// Then we set the flags. We can, for example, make the image resizable
// by adding a flag. I found this question to be asked frequently
// (how to enable or disable image drag handles).
reoObject.dwFlags = (uint)
(REOOBJECTFLAGS.REO_BELOWBASELINE | REOOBJECTFLAGS.REO_RESIZABLE);
// and I use the `dwUser` property to set the object's unique id
// (it's a 32-bit word, and it will be sufficient to identify it)
reoObject.dwUser = id;
最后使用 IRichEditOle.InsertObject
。 IRichEditOle
是一个 COM 接口,也使用 P/Invoke 导入。
对象的“id”使我能够迭代插入的对象列表,并“做一些事情”。 使用 IRichEditOle.GetObject 可以获取每个插入的对象并检查 dwUser 字段以查看 id 是否匹配。
2. 问题
现在问题来了:
a) 第一个问题是更新插入的图像。 我希望能够按需“刷新”某些图像(或更改它们)。 我现在的做法是这样的:
if (reoObject.dwUser == id)
{
// get the char index for the "old" image
oldImageIndex = reoObject.cp;
// insert the new image (I added this overload for testing,
// it does the thing described above)
InsertImageFromFile(oldImageIndex, id, filename);
// and now I select the old image (which has now moved by one "character"
// position to the right), and delete it by setting the selection to ""
_richEdit.SelectionStart = oldImageIndex + 1;
_richEdit.SelectionLength = 1;
_richEdit.SelectedText = "";
}
由于我是从 Gui 线程更新的,所以我相信我不应该担心用户在此方法期间更改选择,因为 OLE 插入会阻塞线程,并且应用程序是在 STA 中运行。
但我不知何故觉得可能有更好/更安全的方法来做到这一点? 这个方法看起来我应该用 [DirtyHack]
属性来标记它。
b) 另一个问题是,在插入时 (IRichEditOle.InsertObject
),我收到未处理的异常(Paint Shop Pro 已停止工作)。 尽管不存在打开或编辑 shell 命令的文件关联,但插入 OLE 对象似乎会以某种方式启动此应用程序。
有谁知道可能导致这种情况的原因以及如何预防?
[编辑]
我刚刚有了一个不同的想法 - 我可以创建自定义 ActiveX 控件来负责更新图像。 在这种情况下,我只需要使 RichTextBox 的该部分无效(类似于 CodeProject 文章的作者所做的)。 但这会使部署变得更加复杂(我需要向 COM 公开一个 .Net 类,然后在嵌入之前注册它)。
I have a question related to managing OLE objects in a RichTextBox.
What I've found so far is lots of info, but not exactly what I need, so I will make a quick introduction first (I also hope someone might find this helpful).
1. What I know so far
First of all, I am using OLE to insert images (or any ActiveX) into the RichTextBox. This is supposed to be "the right way" to do it, since there is no clipboard involved, and you can insert any ActiveX control you want. There is an article on CodeProject (MyExtRichTextBox) which explains how to do it (with complete source code), but to make it short:
Using P/Invoke, the OleCreateFromFile
function is imported from ole32.dll to create an OLE object from the image file.
int hresult = OleCreateFromFile(...);
Function returns an IOleObject
instance, which then has to be referenced by a REOBJECT
struct:
REOBJECT reoObject = new REOBJECT();
reoObject.cp = 0; // charated index for insertion
reoObject.clsid = guid; // iOleObject class guid
reoObject.poleobj = Marshal.GetIUnknownForObject(pOleObject); // actual object
// etc. (set other fields
// Then we set the flags. We can, for example, make the image resizable
// by adding a flag. I found this question to be asked frequently
// (how to enable or disable image drag handles).
reoObject.dwFlags = (uint)
(REOOBJECTFLAGS.REO_BELOWBASELINE | REOOBJECTFLAGS.REO_RESIZABLE);
// and I use the `dwUser` property to set the object's unique id
// (it's a 32-bit word, and it will be sufficient to identify it)
reoObject.dwUser = id;
And finally the structure is passed to RichTextBox using IRichEditOle.InsertObject
. IRichEditOle
is a COM interface, imported using P/Invoke also.
The "id" for the object enables me to iterate through the list of inserted objects, and "do stuff". Using IRichEditOle.GetObject
I can get each inserted object and check the dwUser
field to see if the id matched.
2. Problems
Now come the questions:
a) First problem is updating an inserted image. I want to be able to "refresh" certain images on demand (or change them). The way I am doing it right now is something like this:
if (reoObject.dwUser == id)
{
// get the char index for the "old" image
oldImageIndex = reoObject.cp;
// insert the new image (I added this overload for testing,
// it does the thing described above)
InsertImageFromFile(oldImageIndex, id, filename);
// and now I select the old image (which has now moved by one "character"
// position to the right), and delete it by setting the selection to ""
_richEdit.SelectionStart = oldImageIndex + 1;
_richEdit.SelectionLength = 1;
_richEdit.SelectedText = "";
}
Since I am updating from the Gui thread, I believe I shouldn't be worried about user changing the selection during this method, because OLE insertion blocks the thread, and the app is running in STA.
But I somehow feel that there might be a better/safer way to do it? This method looks like I should mark it with a [DirtyHack]
attribute.
b) The other problem is that, in the moment of insertion (IRichEditOle.InsertObject
), I get an unhandled exception (Paint Shop Pro has stopped working). It seems that inserting the OLE object somehow starts this app, although no file associations exist for Open or Edit shell commands.
Does anybody know what might be causing this and how to prevent it?
[Edit]
I just got a different idea - I could create my custom ActiveX control which would take care of updating the image. In that case I would only need to invalidate that part of the RichTextBox (similar to what the author of the CodeProject article does). But this would make deploying a bit more complicated (I need to expose a .Net class to COM, and then register it before embedding).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我对 .NET/OLE/ActiveX 等不太了解,但是在编写 GUI 时,您应该避免从线程修改/刷新窗口。
我在 C++ 方面也有过类似的经历。 您应该使用计时器之类的东西,而不是使用线程来修改窗口。
I don't know so much about .NET/OLE/ActiveX etc, but when programming GUIs, you should avoid to modify/refresh windows from threads.
I had similar experiences with C++. Rather than using a thread to modify your window, you should use something like a timer.