Я написал свой юзер-контрол, который использует ImageList, расположенный на той же форме (так же как Toolbar или ListView).
Ссылка на этот ImageList является свойством моего контрола, которое можно задавать в design-time (я создал для этого PropertyPage).
Вопрос: как сделать, чтобы это свойство стало персистентным? Т.е. чтобы загружалось и сохранялось вместе с формой?
Делаю так: храню строку с именем ImageList (например "MyImageList1") в свойстве своего контрола, а потом при загрузке перебираю все контролы на форме по имени.
Это работает в design-time но не работает в run-time, точнее, если скомпилировать ЕХЕ, то в нем иконок не видно. Видимо это потому, что в скомпилированном нативном коде нет такого понятия как "имя" контрола, все имена находятся в состоянии resolved.
Но как тогда поступить?
P.S. Пока написал ручной код инициализации вроде Set MyControl.ImageList = MyImageList1 на Form_Load, но это же неприкольно, системный тулбар и листвью как-то работают и без этого, вопрос как?
Ignoramus, TreeView хранит ссылку на ListView в виде строки — названия контрола. Тезис I>Видимо это потому, что в скомпилированном нативном коде нет такого понятия как "имя" контрола, все имена находятся в состоянии resolved.
не верен совершенно. Проверяй код.
Re[2]: Как сделать персистентную связь с ImageList?
Здравствуйте, Бенедикт, Вы писали:
Б>Ignoramus, TreeView хранит ссылку на ListView в виде строки — названия контрола.
Я тоже так решил. Собственно, я выяснил это на основании эксперимента (посмотрел содержимое frm-файла) и решил сделать точно так же. С первого раза не вышло, что ж, если Вы говорите что должно работать, проверю еще раз, о результатах сообщу .
Впрочем, сам ImageList у меня вызывает все больше претензий. Например, почему он не хранит прозрачные иконки, а только обычные битмапки, с указанием MaskColor — цветом для прозрачности? Кроме того, я постоянно сталкиваюсь с багом — при первом запуске иконки выглядят нормально, а при повторном что-то с ними случается, прозрачный цвет становится черным фоном, а сами иконки — черно-белыми. Помогает только опустошение и перезаполнение ImageList'а. Может Вы знаете в чем дело?
У системного HIMAGELIST всех этих проблем нет и в помине и если ImageList это обертка вокруг него, то это плохая обертка. Я даже склоняюсь к тому, чтобы написать свою.
Re[2]: Как сделать персистентную связь с ImageList?
Кстати, если уж Вы вы этом разбираетесь, было бы интересно заодно получить ответы на следующие интересующие меня вопросы:
1) Как сохранять "вложенные" объекты-свойства в персистентном хранилище вроде FRM-файла? (Подобно тому как хранятся ListImage'и в ImageList'е — BeginProperty/EndProperty). Я себе представляю это так, что вложенный объект должен имплементировать какой-то интерфейс типа IPersist-чего-то-там. Вопрос как это реализовано в ВБ6. Обязательна ли для этого какая-то особая процедура регистрации вложенного объекта и получения им GUID'а или достаточно ссылки на объект в проекте?
2) Как сохранить в персистентном потоке тело иконки (и любые бинарные данные)? Как в этом процессе участвуют файлы FRX?
Re[3]: Как сделать персистентную связь с ImageList?
Начну с того, что я вряд ли знаю намного больше того, что написано в официальной документации, которую можно найти здесь: 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?
Здравствуйте, Бенедикт, Вы писали:
Б>Начну с того, что я вряд ли знаю намного больше того, что написано в официальной документации, которую можно найти здесь: 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?
Здравствуйте, 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?
Здравствуйте, Бенедикт, Вы писали:
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?
Здравствуйте, 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_ImageListNameDim iml As ImageList
Set iml = UserControl.ParentControls(0)(m_ImageListName) '1Set iml = UserControl.ParentControls(0).Controls(m_ImageListName) '2
'илиDim frm As Form
Set frm = UserControl.ParentControls(0)
Set iml = frm(m_ImageListName) '1aSet iml = frm.Controls(m_ImageListName) '2a
Но это не всё...
I>Может быть я неясно выражаюсь вследствие того, что имею неправильное представление о предмете . У каждого контрола есть идентификатор (свойство (Name) в Properties). Этот идентификатор используется в коде программы как имя переменной. По аналогии с другими переменными (и другими языками программирования), имя переменной является информацией для программиста в исходном коде, а в скомпилированном коде имя исчезает, превращаясь в адрес в памяти. Соответственно, если я пытаюсь найти на форме ImageList по его имени (MyImageList) в скомпилированном контроле, то, как мне кажется, это не должно получиться, т.к. исходного кода уже как бы нет Укажите мне на ошибку в моих рассуждениях. Может быть контролы — это какие-то особенные переменные, которые сохраняют свои имена в рантайме, в виде свойства?
...потому что есть способ, явно раскрывающий сущность контролов:
Т.е. контрол — это Property Get объекта-хозяина, которое возвращает ссылку на экземпляр контрола (или его Extender-а для не-intinsic-ов? Не соображу.) Это не переменная, а свойство. VB-шные объекты наследуют IDispatch, и обращение по имени свойства/метода приводит к вызову IDispatch::Invoke() — за что MS упорно и боролся, создавая Automation. В общем, надо взять книжку типа "Inside COM" Роджерсона (можно найти в Сети) и почитать про диспетчеризацию и дуальные интерфейсы.