Как сделать персистентную связь с ImageList?
От: Ignoramus  
Дата: 13.04.06 13:40
Оценка:
Я написал свой юзер-контрол, который использует ImageList, расположенный на той же форме (так же как Toolbar или ListView).

Ссылка на этот ImageList является свойством моего контрола, которое можно задавать в design-time (я создал для этого PropertyPage).

Вопрос: как сделать, чтобы это свойство стало персистентным? Т.е. чтобы загружалось и сохранялось вместе с формой?

Делаю так: храню строку с именем ImageList (например "MyImageList1") в свойстве своего контрола, а потом при загрузке перебираю все контролы на форме по имени.

Это работает в design-time но не работает в run-time, точнее, если скомпилировать ЕХЕ, то в нем иконок не видно. Видимо это потому, что в скомпилированном нативном коде нет такого понятия как "имя" контрола, все имена находятся в состоянии resolved.

Но как тогда поступить?

P.S. Пока написал ручной код инициализации вроде Set MyControl.ImageList = MyImageList1 на Form_Load, но это же неприкольно, системный тулбар и листвью как-то работают и без этого, вопрос как?
Re: Как сделать персистентную связь с ImageList?
От: Бенедикт  
Дата: 15.04.06 23:40
Оценка:
Ignoramus, TreeView хранит ссылку на ListView в виде строки — названия контрола. Тезис
I>Видимо это потому, что в скомпилированном нативном коде нет такого понятия как "имя" контрола, все имена находятся в состоянии resolved.
не верен совершенно. Проверяй код.
Re[2]: Как сделать персистентную связь с ImageList?
От: Ignoramus  
Дата: 16.04.06 11:52
Оценка:
Здравствуйте, Бенедикт, Вы писали:

Б>Ignoramus, TreeView хранит ссылку на ListView в виде строки — названия контрола.


Я тоже так решил. Собственно, я выяснил это на основании эксперимента (посмотрел содержимое frm-файла) и решил сделать точно так же. С первого раза не вышло, что ж, если Вы говорите что должно работать, проверю еще раз, о результатах сообщу .

Впрочем, сам ImageList у меня вызывает все больше претензий. Например, почему он не хранит прозрачные иконки, а только обычные битмапки, с указанием MaskColor — цветом для прозрачности? Кроме того, я постоянно сталкиваюсь с багом — при первом запуске иконки выглядят нормально, а при повторном что-то с ними случается, прозрачный цвет становится черным фоном, а сами иконки — черно-белыми. Помогает только опустошение и перезаполнение ImageList'а. Может Вы знаете в чем дело?

У системного HIMAGELIST всех этих проблем нет и в помине и если ImageList это обертка вокруг него, то это плохая обертка. Я даже склоняюсь к тому, чтобы написать свою.
Re[2]: Как сделать персистентную связь с ImageList?
От: Ignoramus  
Дата: 16.04.06 12:03
Оценка:
Кстати, если уж Вы вы этом разбираетесь, было бы интересно заодно получить ответы на следующие интересующие меня вопросы:

1) Как сохранять "вложенные" объекты-свойства в персистентном хранилище вроде FRM-файла? (Подобно тому как хранятся ListImage'и в ImageList'е — BeginProperty/EndProperty). Я себе представляю это так, что вложенный объект должен имплементировать какой-то интерфейс типа IPersist-чего-то-там. Вопрос как это реализовано в ВБ6. Обязательна ли для этого какая-то особая процедура регистрации вложенного объекта и получения им GUID'а или достаточно ссылки на объект в проекте?

2) Как сохранить в персистентном потоке тело иконки (и любые бинарные данные)? Как в этом процессе участвуют файлы FRX?
Re[3]: Как сделать персистентную связь с ImageList?
От: Бенедикт  
Дата: 17.04.06 07:03
Оценка: 2 (1)
Начну с того, что я вряд ли знаю намного больше того, что написано в официальной документации, которую можно найти здесь: Creating ActiveX Components.
Настоятельно рекумендую прочесть хотя бы единожды.


I>Я тоже так решил. Собственно, я выяснил это на основании эксперимента (посмотрел содержимое frm-файла) и решил сделать точно так же. С первого раза не вышло, что ж, если Вы говорите что должно работать, проверю еще раз, о результатах сообщу .

Хорошо.

I>Впрочем, сам ImageList у меня вызывает все больше претензий. Например, почему он не хранит прозрачные иконки, а только обычные битмапки, с указанием MaskColor — цветом для прозрачности? Кроме того, я постоянно сталкиваюсь с багом — при первом запуске иконки выглядят нормально, а при повторном что-то с ними случается, прозрачный цвет становится черным фоном, а сами иконки — черно-белыми. Помогает только опустошение и перезаполнение ImageList'а. Может Вы знаете в чем дело?


Нет, не сталкивался. Быть может, IPictureDisp/StdPicture не может разобраться с форматом иконки? Не знаю, короче. Я всегда в одном ImageList-е использую битмапы строго одинакового формата. Кроме того, иконки можно создать из битмапа через ListImage.ExtractIcon.


I>У системного HIMAGELIST всех этих проблем нет и в помине и если ImageList это обертка вокруг него, то это плохая обертка. Я даже склоняюсь к тому, чтобы написать свою.


На vbAccelerator.com такая обёртка уже есть. На практике не применял, рекомендации, кроме как попробовать, дать не могу. Вообще на данном сайте, наверное, самый полный и профессионально реализованный набор UserControl-ов, который я видел.


I>1) Как сохранять "вложенные" объекты-свойства в персистентном хранилище вроде FRM-файла? (Подобно тому как хранятся ListImage'и в ImageList'е — BeginProperty/EndProperty). Я себе представляю это так, что вложенный объект должен имплементировать какой-то интерфейс типа IPersist-чего-то-там. Вопрос как это реализовано в ВБ6. Обязательна ли для этого какая-то особая процедура регистрации вложенного объекта и получения им GUID'а или достаточно ссылки на объект в проекте?


а) Хо. А MS против "вложенных" объектов-свойств. См. Don't Expose Constituent Controls as Properties в Exposing Properties of Constituent Controls. Аргументация, надо сказать, вполне убедительная. По-видимому, компромисное решение состоит в том, чтобы завернуть все такие объекты и их подчинённые объекты в свои классы, сигнализирующие о внешнем изменении свойств UserControl-у. Простой пример такого решения — класс StdFont.
б) Никаких особых усилий предпринимать не надо, кроме реализации UserControl_WriteProperties и UserControl_ReadProperties. PropBag.WriteProperty в UserControl_WriteProperties сам анализирует тип варианта-аргумента. Не думаю, что WriteProperty интересны References проекта — всё (CLSID, поддержка интерфейсов) достаётся из экземпляра. Потенциально серых пятна видится два: 1) использование объекта из внешней библиотеки, помимо описанных в документации (реализация IPersistStorage или IPersist-чего-то-там); 2) необходимость и эффективность предоставления значений по умолчанию для объектных типов.

I>2) Как сохранить в персистентном потоке тело иконки (и любые бинарные данные)? Как в этом процессе участвуют файлы FRX?

См. Saving the Properties of Your Control, в частности, Saving and Retrieving Binary Data. Вкратце: как байтовый массив постоянной или переменной длины.
Re[4]: Как сделать персистентную связь с ImageList?
От: Ignoramus  
Дата: 18.04.06 12:22
Оценка:
Здравствуйте, Бенедикт, Вы писали:

Б>Начну с того, что я вряд ли знаю намного больше того, что написано в официальной документации, которую можно найти здесь: Creating ActiveX Components.

Б>Настоятельно рекумендую прочесть хотя бы единожды.
Спасибо, уже читал не один раз. Прикольно написано кстати — как для чайников В других разделах МСДНа такого доходчивого стиля изложения не найдешь


I>>Я тоже так решил. Собственно, я выяснил это на основании эксперимента (посмотрел содержимое frm-файла) и решил сделать точно так же. С первого раза не вышло, что ж, если Вы говорите что должно работать, проверю еще раз, о результатах сообщу .

Б>Хорошо.

Уже заработало, само, я так и не понял в чем была проблема. Плохо, вдруг опять вылезет

Впрочем, все равно интересно насчет "тезиса". В каком это виде бейсиковские имена переменных существуют в рантайме? Где об этом можно почитать?

I>>Впрочем, сам ImageList у меня вызывает все больше претензий. Например, почему он не хранит прозрачные иконки, а только обычные битмапки, с указанием MaskColor — цветом для прозрачности? Кроме того, я постоянно сталкиваюсь с багом — при первом запуске иконки выглядят нормально, а при повторном что-то с ними случается, прозрачный цвет становится черным фоном, а сами иконки — черно-белыми. Помогает только опустошение и перезаполнение ImageList'а. Может Вы знаете в чем дело?


Б>Нет, не сталкивался. Быть может, IPictureDisp/StdPicture не может разобраться с форматом иконки? Не знаю, короче. Я всегда в одном ImageList-е использую битмапы строго одинакового формата.


Какого именно формата?

Б>Кроме того, иконки можно создать из битмапа через ListImage.ExtractIcon.


Да, знаю, так и делаю.

I>>У системного HIMAGELIST всех этих проблем нет и в помине и если ImageList это обертка вокруг него, то это плохая обертка. Я даже склоняюсь к тому, чтобы написать свою.


Б>На vbAccelerator.com такая обёртка уже есть. На практике не применял, рекомендации, кроме как попробовать, дать не могу. Вообще на данном сайте, наверное, самый полный и профессионально реализованный набор UserControl-ов, который я видел.


Акселератором часто пользуюсь как примером, реализации ихние не рискую брать, имхо на профессиональные они не тянут. К тому же не в курсе как у них с лицензиями.

I>>1) Как сохранять "вложенные" объекты-свойства в персистентном хранилище вроде FRM-файла? (Подобно тому как хранятся ListImage'и в ImageList'е — BeginProperty/EndProperty). Я себе представляю это так, что вложенный объект должен имплементировать какой-то интерфейс типа IPersist-чего-то-там. Вопрос как это реализовано в ВБ6. Обязательна ли для этого какая-то особая процедура регистрации вложенного объекта и получения им GUID'а или достаточно ссылки на объект в проекте?


Б>а) Хо. А MS против "вложенных" объектов-свойств. См. Don't Expose Constituent Controls as Properties в Exposing Properties of Constituent Controls. Аргументация, надо сказать, вполне убедительная. По-видимому, компромисное решение состоит в том, чтобы завернуть все такие объекты и их подчинённые объекты в свои классы, сигнализирующие о внешнем изменении свойств UserControl-у. Простой пример такого решения — класс StdFont.


Я вообще-то не это имел в виду. Я не собираюсь выставлять constituent controls в виде свойств. Просто внутри моего контрола (тулбар) есть кнопки — это не контролы, просто вложенные объекты (cls), они клиенту класса не доступны, ни как проперти, никак вообще, используются только внутренне, в контроле.

И вот, я хочу сохранять свойства кнопок в PropertyBag не в одной куче с свойствами самого тулбара и кнопками, а в отдельных, вложенных маленьких сумочках. Так, кстати, работает стандартный контрол Toolbar. Метод WriteProperties получает на вход некий объект, который надо сохранить (строка, число, массив байт и т.п.). Если бы этот метод писал я, я бы сделал чтобы объект (кнопка) сам знал как себя сохранить в сумке, путем реализации некоего интерфейса, например, IPersist-чего-то-там. Но я начинаю думать, что так сделать нельзя (не выходя за пределы VB6 и не создавая дочерних контролов в контроле).


Б>б) Никаких особых усилий предпринимать не надо, кроме реализации UserControl_WriteProperties и UserControl_ReadProperties. PropBag.WriteProperty в UserControl_WriteProperties сам анализирует тип варианта-аргумента. Не думаю, что WriteProperty интересны References проекта — всё (CLSID, поддержка интерфейсов) достаётся из экземпляра.

Вот интересно какие типы еще распознаются. Существует ли какой-то интерфейс чтобы создать свой собственный сохраняемый тип?

Б>Потенциально серых пятна видится два: 1) использование объекта из внешней библиотеки, помимо описанных в документации (реализация IPersistStorage или IPersist-чего-то-там); 2) необходимость и эффективность предоставления значений по умолчанию для объектных типов.


I>>2) Как сохранить в персистентном потоке тело иконки (и любые бинарные данные)? Как в этом процессе участвуют файлы FRX?

Б>См. Saving the Properties of Your Control, в частности, Saving and Retrieving Binary Data. Вкратце: как байтовый массив постоянной или переменной длины.
Re[5]: Как сделать персистентную связь с ImageList?
От: Бенедикт  
Дата: 19.04.06 13:12
Оценка: 2 (1)
Здравствуйте, Ignoramus, Вы писали:

I>Впрочем, все равно интересно насчет "тезиса". В каком это виде бейсиковские имена переменных существуют в рантайме? Где об этом можно почитать?


Почему речь пошла об именах переменных? Говорили же об элементах управления. До элементов управления на форме из методов UserControl-а можно добраться и в design-, и в run-time через UserControl.ParentControls, либо через .Controls последнего в цепочке Parent-ов UserControl.Extender-а. См. также справку по .ContainedControls, .Controls.Add и Q190670.

I>Какого именно формата?

BMP одинакового размера, глубины цвета (24 бита), с одинаковым цветом маски.

I>Акселератором часто пользуюсь как примером, реализации ихние не рискую брать, имхо на профессиональные они не тянут. К тому же не в курсе как у них с лицензиями.


Куда ж без напильника?

I>Я вообще-то не это имел в виду. Я не собираюсь выставлять constituent controls в виде свойств. Просто внутри моего контрола (тулбар) есть кнопки — это не контролы, просто вложенные объекты (cls), они клиенту класса не доступны, ни как проперти, никак вообще, используются только внутренне, в контроле.


I>И вот, я хочу сохранять свойства кнопок в PropertyBag не в одной куче с свойствами самого тулбара и кнопками, а в отдельных, вложенных маленьких сумочках.

Теперь дошло.

I>Так, кстати, работает стандартный контрол Toolbar. Метод WriteProperties получает на вход некий объект, который надо сохранить (строка, число, массив байт и т.п.). Если бы этот метод писал я, я бы сделал чтобы объект (кнопка) сам знал как себя сохранить в сумке, путем реализации некоего интерфейса, например, IPersist-чего-то-там. Но я начинаю думать, что так сделать нельзя (не выходя за пределы VB6 и не создавая дочерних контролов в контроле).


Дочерние контролы в контроле с InvisibleAtRuntime=True — это "почти то". Учитывая, что интерфейс их и их коллекции можно описать через абстрактные PublicNotCreatable классы, который торчат наружу и которые контролы Implements. Но такие контролы должны быть Public, чтобы ReadProperty смог найти CLSID. Т.е. нормального сокрытия (чтобы класс был PublicNotCreatable или Private, и Persistable одновременно) как-то не получается.
Что радует, данный способ можно рассматривать как средство наглядного документирования структуры данных Шутка.

У обычных классов с Instancing=3 (SingleUse) или бОльшим можно задать Persistable=1 (Persistable). Но их PropertyBag сохраняется в двоичном виде (вроде бы).

I>Вот интересно какие типы еще распознаются. Существует ли какой-то интерфейс чтобы создать свой собственный сохраняемый тип?

Распознаются Std- (-Picture, -Font, -DataFormat) и OLE_-типы, встроенные VB-шные контролы, типы из common controls , и т. д., короче, видимо, всё, что реализует IPersistStorage, IPersistStreamInit или IPersistStream.

По показаниям OLE/COM Object Viewer персистентные MultiUse VB-классы отличаются следующим: реализацией _DPersistableClass вместо _DClass, наличием реализации IPersist, или IPersistPropertyBag, IPersistStream, IPersistStreamInit.

Шансы так себе, но можно попытаться попробовать: 1) создать .tlb, содержащий эти интерфейсы; 2) подключить её в проект (ручками в .vbp) — пример OLEGUIDS.TLB с vbAccelerator показывает, что такое возможно; 3) реализовать интерфейсы (или часть) через Implements... И это всё ради красивого вида в .frm? Интересно узнать, чем это закончится
Re[6]: Как сделать персистентную связь с ImageList?
От: Ignoramus  
Дата: 25.04.06 08:44
Оценка:
Здравствуйте, Бенедикт, Вы писали:

I>>Впрочем, все равно интересно насчет "тезиса". В каком это виде бейсиковские имена переменных существуют в рантайме? Где об этом можно почитать?


Б>Почему речь пошла об именах переменных? Говорили же об элементах управления. До элементов управления на форме из методов UserControl-а можно добраться и в design-, и в run-time через UserControl.ParentControls, либо через .Controls последнего в цепочке Parent-ов UserControl.Extender-а. См. также справку по .ContainedControls, .Controls.Add и Q190670.


По индексу можно, а по имени — вопрос.

Может быть я неясно выражаюсь вследствие того, что имею неправильное представление о предмете . У каждого контрола есть идентификатор (свойство (Name) в Properties). Этот идентификатор используется в коде программы как имя переменной. По аналогии с другими переменными (и другими языками программирования), имя переменной является информацией для программиста в исходном коде, а в скомпилированном коде имя исчезает, превращаясь в адрес в памяти. Соответственно, если я пытаюсь найти на форме ImageList по его имени (MyImageList) в скомпилированном контроле, то, как мне кажется, это не должно получиться, т.к. исходного кода уже как бы нет Укажите мне на ошибку в моих рассуждениях. Может быть контролы — это какие-то особенные переменные, которые сохраняют свои имена в рантайме, в виде свойства?

Б>Шансы так себе, но можно попытаться попробовать: 1) создать .tlb, содержащий эти интерфейсы; 2) подключить её в проект (ручками в .vbp) — пример OLEGUIDS.TLB с vbAccelerator показывает, что такое возможно; 3) реализовать интерфейсы (или часть) через Implements... И это всё ради красивого вида в .frm? Интересно узнать, чем это закончится


Спасибо за интересную информацию и ссылки, узнал кое-что новое для себя . Закончилось тем, естественно, что я решил, что овчинка выделки не стоит и оставил как есть.

А насчет ImageList вообще решил, что мне удобнее отказаться от списков, а грузить картинки непосредственно и ресурсов по индексу (LoadResPicture). Так у меня есть доступ к родному формату иконки и снимается проблема персистентности и ссылок на контролы. Единственный недостаток — нужно делать make чтобы увидеть результат.
Re[7]: Как сделать персистентную связь с ImageList?
От: Бенедикт  
Дата: 27.04.06 07:41
Оценка:
Здравствуйте, Ignoramus, Вы писали:

I>>>Впрочем, все равно интересно насчет "тезиса". В каком это виде бейсиковские имена переменных существуют в рантайме? Где об этом можно почитать?


Б>>Почему речь пошла об именах переменных? Говорили же об элементах управления. До элементов управления на форме из методов UserControl-а можно добраться и в design-, и в run-time через UserControl.ParentControls, либо через .Controls последнего в цепочке Parent-ов UserControl.Extender-а. См. также справку по .ContainedControls, .Controls.Add и Q190670.


I>По индексу можно, а по имени — вопрос.


Да, у коллекций ParentControls и ContainedControls ключи только типа Long, но это не останавливает. Главное — добраться до формы. Проще всего, наверное, это сделать через ParentControls, в ней первым элементом идёт как раз она. Допустим, из модуля UserControl-а хотим добраться по имени до ImageList-а, лежащего на той же форме, что экземпляр UserControl-а. Тогда следующие обращения эквивалентны:
 Private m_ImageListName as String
 '... как-то получили имя ImageList-а и положили в m_ImageListName
 Dim iml As ImageList
 Set iml = UserControl.ParentControls(0)(m_ImageListName)                    '1
 Set iml = UserControl.ParentControls(0).Controls(m_ImageListName)           '2
 'или
 Dim frm As Form
 Set frm = UserControl.ParentControls(0)
 Set iml = frm(m_ImageListName)                                              '1a
 Set iml = frm.Controls(m_ImageListName)                                     '2a

Но это не всё...

I>Может быть я неясно выражаюсь вследствие того, что имею неправильное представление о предмете . У каждого контрола есть идентификатор (свойство (Name) в Properties). Этот идентификатор используется в коде программы как имя переменной. По аналогии с другими переменными (и другими языками программирования), имя переменной является информацией для программиста в исходном коде, а в скомпилированном коде имя исчезает, превращаясь в адрес в памяти. Соответственно, если я пытаюсь найти на форме ImageList по его имени (MyImageList) в скомпилированном контроле, то, как мне кажется, это не должно получиться, т.к. исходного кода уже как бы нет Укажите мне на ошибку в моих рассуждениях. Может быть контролы — это какие-то особенные переменные, которые сохраняют свои имена в рантайме, в виде свойства?


...потому что есть способ, явно раскрывающий сущность контролов:
 Set iml = CallByName(UserControl.ParentControls(0), m_ImageListName, VbGet) '3
 Set iml = CallByName(frm, m_ImageListName, VbGet)                           '3a

Т.е. контрол — это Property Get объекта-хозяина, которое возвращает ссылку на экземпляр контрола (или его Extender-а для не-intinsic-ов? Не соображу.) Это не переменная, а свойство. VB-шные объекты наследуют IDispatch, и обращение по имени свойства/метода приводит к вызову IDispatch::Invoke() — за что MS упорно и боролся, создавая Automation. В общем, надо взять книжку типа "Inside COM" Роджерсона (можно найти в Сети) и почитать про диспетчеризацию и дуальные интерфейсы.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.