У меня есть вопрос, связанный с управлением объектами OLE в RichTextBox.
На данный момент я нашел много информации, но не совсем то, что мне нужно, поэтому сначала я сделаю краткое введение (я также надеюсь, что кто-то может найти это полезным).
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;
И, наконец, структура передается RichTextBox с помощью IRichEditOle.InsertObject
. IRichEditOle
- это интерфейс COM, также импортированный с помощью P / Invoke.
«Идентификатор» для объекта позволяет мне перебирать список вставленных объектов и «делать вещи». Используя IRichEditOle.GetObject
, я могу получить каждый вставленный объект и проверить поле dwUser
, чтобы убедиться, что идентификатор совпадает.
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 перестал работать). Кажется, что вставка объекта OLE каким-то образом запускает это приложение, хотя для команд оболочки Open или Edit не существует ассоциаций файлов.
Кто-нибудь знает, что может быть причиной и как ее предотвратить?
[Изменить]
У меня просто другая идея - я могу создать свой собственный элемент управления ActiveX, который позаботится об обновлении изображения. В этом случае мне нужно будет только аннулировать эту часть RichTextBox (аналогично тому, что делает автор статьи CodeProject). Но это усложнит развертывание (мне нужно открыть класс .Net для COM, а затем зарегистрировать его перед внедрением).