Свои контролы в C# (работа с ними из дизайнера)
От: AndrewZomb  
Дата: 14.01.08 10:52
Оценка:
Недавно начал работать с C# и
накопилось вот несколько вопросов, может поможет кто:
1) Свойства своих классов в дизайнере
Есть мой контрол (MyControl) — лейбл имеющий рамку и текст которые умеют моргать и менять цвета, наследованный от UserControl, который использует мои классы не наследованные ни от чего (MyText, MyBorder) — они собственно и умеют моргать и менять цвета
Так вот — как заставить дизайнер отображать и изменять свойства MyText и MyBorder, чтобы они выбирались в ToolBox'е как выпадающий список свойств, а не определяя свойства этих классов как свойства MyControl?

2) Групповое переименование контролов в дизайнере
Есть тот же MyControl. На форме их может быть очень много (20-60 штук), разделенных на группы по именам (т.е. MyControl_A1..n, MyControl_B1..n, MyControl_C1..n) и они кидаются на форму вручную (в основном Ctrl+C, Ctrl+V)
Как сделать так, чтобы можно было переименовать группу контролов (MyControl1..n) -> (MyControl_A1..n), или, если можно написать, чтобы при копировании контрола копировались не только свойства но и имя его, тока индекс новый?

3) Индексация контролов
Как получить доступ к одному контролу из группы (могут быть в разных контейнерах типа панель) по индексу?
Придумал только вариант выделения индекса из имени контрола:

  Control[] cc;
  for (int i = 1; i < n+1; i++)
  {
      cc = this.Controls.Find("MyControl" + i.ToString(), true);
      if (cc.Length == 0) continue;
      
      MyControls.MyControl objControl;
      objControl = (MyControls.MyControl)cc[0];
  }

может вы знаете лучший вариант?

Заранее спасибо всем отвечающим.

15.01.08 10:51: Перенесено модератором из '.NET' — Хитрик Денис
Re: Свои контролы в C# (работа с ними из дизайнера)
От: Pavel_Agurov Россия  
Дата: 14.01.08 11:25
Оценка:
AZ>1) Свойства своих классов в дизайнере
AZ>Есть мой контрол (MyControl) — лейбл имеющий рамку и текст которые умеют моргать и менять цвета, наследованный от UserControl, который использует мои классы не наследованные ни от чего (MyText, MyBorder) — они собственно и умеют моргать и менять цвета
AZ> Так вот — как заставить дизайнер отображать и изменять свойства MyText и MyBorder, чтобы они выбирались в ToolBox'е как выпадающий список свойств, а не определяя свойства этих классов как свойства MyControl?

Не понятно что куда должно выпадать. Можно например прицепить к Mytext и MyBorder атрибут ExpandableObjectConverter.


[TypeConverter(typeof(ExpandableObjectConverter))]
public class MyBorder
{
...
}


Тогда свойство MyBorder будет распахиваться в дизайнере как например Font.

AZ>2) Групповое переименование контролов в дизайнере

AZ>Есть тот же MyControl. На форме их может быть очень много (20-60 штук), разделенных на группы по именам (т.е. MyControl_A1..n, MyControl_B1..n, MyControl_C1..n) и они кидаются на форму вручную (в основном Ctrl+C, Ctrl+V)
AZ>Как сделать так, чтобы можно было переименовать группу контролов (MyControl1..n) -> (MyControl_A1..n), или, если можно написать, чтобы при копировании контрола копировались не только свойства но и имя его, тока индекс новый?

Посмотрите метод InitializeNewComponent класса ComponentDesigner. Еще есть класс DesignerSurface и у него ResolveName.
Хотя тут надо определиться с вопросом "а зачем это надо" и тогда искать решения.

AZ>3) Индексация контролов

AZ>Как получить доступ к одному контролу из группы (могут быть в разных контейнерах типа панель) по индексу?
AZ>Придумал только вариант выделения индекса из имени контрола:

Возможно подойдет IExtenderProvider.
Re[2]: Свои контролы в C# (работа с ними из дизайнера)
От: AndrewZomb  
Дата: 14.01.08 13:33
Оценка:
Здравствуйте, Pavel_Agurov, Вы писали:

P_A>Не понятно что куда должно выпадать. Можно например прицепить к Mytext и MyBorder атрибут ExpandableObjectConverter.


[TypeConverter(typeof(ExpandableObjectConverter))]


P_A>Тогда свойство MyBorder будет распахиваться в дизайнере как например Font.

так и нужно, чтобы свойства распахивались как например Font
А можно подробнее про [TypeConverter(typeof(ExpandableObjectConverter))] — я поставил этот атрибут перед классом MyBorder но желаемого не получил — свойство Border стало просто доступно в Properties window, но не распахивается.. Может надо еще что-то писать в коде?
А свойство Border я определяю в MyControl как

        public MyBorder Border
        {
            get { return _border; }
            set { _border = value; this.Invalidate(); }
        }

Перед этим никакого атрибута ставить не надо? (атрибуты для меня пока неизведанная до конца штука)

P_A>Посмотрите метод InitializeNewComponent класса ComponentDesigner. Еще есть класс DesignerSurface и у него ResolveName.

P_A>Хотя тут надо определиться с вопросом "а зачем это надо" и тогда искать решения.
Не совсем понятно как использовать эти классы — наследовать свой MyBorder от него(них) или писать как в примере MSDN свой класс-дизайнер наследованный от них? Тогда встает вопрос как его использовать.. Если можно напишите подробнее..

спасибо за подсказки, хоть понятно в какую сторону копать. )
Re[3]: Свои контролы в C# (работа с ними из дизайнера)
От: Pavel_Agurov Россия  
Дата: 14.01.08 18:35
Оценка:
AZ>А можно подробнее про [TypeConverter(typeof(ExpandableObjectConverter))] — я поставил этот атрибут перед классом MyBorder но желаемого не получил — свойство Border стало просто доступно в Properties window, но не распахивается.. AZ>А свойство Border я определяю в MyControl как

AZ>
AZ>        public MyBorder Border
AZ>        {
AZ>            get { return _border; }
AZ>            set { _border = value; this.Invalidate(); }
AZ>        }
AZ>


надо чтобы MyBorder имело public свойства (не поля, а именно свойства).

P_A>>Посмотрите метод InitializeNewComponent класса ComponentDesigner. Еще есть класс DesignerSurface и у него ResolveName.

P_A>>Хотя тут надо определиться с вопросом "а зачем это надо" и тогда искать решения.
AZ> Не совсем понятно как использовать эти классы — наследовать свой MyBorder от него(них) или писать как в примере MSDN свой класс-дизайнер наследованный от них? Тогда встает вопрос как его использовать.. Если можно напишите подробнее..

вообще это не дает ответа на вопрос "зачем это надо" и что вы хотите сделать. Зачем наследовать MyBorder от чего либо, если вы хотите просто задавать имена своим компонентам? Или я что-то не понимаю. Опишите четко задачу.
Re[4]: Свои контролы в C# (работа с ними из дизайнера)
От: AndrewZomb  
Дата: 15.01.08 07:40
Оценка:
Здравствуйте, Pavel_Agurov, Вы писали:

AZ>>[TypeConverter(typeof(ExpandableObjectConverter))] — я поставил этот атрибут перед классом MyBorder но желаемого не получил — свойство Border стало просто доступно в Properties window, но не распахивается..

..
P_A>надо чтобы MyBorder имело public свойства (не поля, а именно свойства).
Спасибо большое, заработало! Хотя и непонятно почему вчера не работало — у MyBorder были public свойства.. Я просто еще добавил..

P_A>вообще это не дает ответа на вопрос "зачем это надо" и что вы хотите сделать. Зачем наследовать MyBorder от чего либо, если вы хотите просто задавать имена своим компонентам? Или я что-то не понимаю. Опишите четко задачу.


Хоть этот вопрос и не настолько актуален уже, но все же интересно, можно ли так сделать..
Значит имеем контрол MyControl. Другой человек будет делать формы с этим контролом. Форм будет 20-30 штук и на каждой будет 20-60 этих контролов.
С этими формами работает один и тот же код, которому без разницы что за форма и сколько их, потому что он работает с именами контролов, выделяя из них группу и индекс.
Т.о. на каждой форме надо дать контролам имена по группам и индексам: MyControl_A1..n, MyControl_B1..m, MyControl_C1..k, где n,m,k — количество контролов в разной группе. Разным группам контролов присваиваются разные свойства (их довольно много).
Задача состоит в том, чтобы облегчить жизнь человеку, который будет делать эти формы. Т.е. сделать так, чтобы можно было кинуть на форму один контрол, задать ему нужные свойства и имя, и скопировать\вставить его столько раз сколько их таких требуется, причем в имени должна меняться только последняя цифра. Или сделать еще как-нибудь, но чтобы избавится от необходимости каждый раз давать имя контролу.

В общем вот так ) надеюсь, не сильно напрягаю своим непрофессионализмом..
Re[5]: Свои контролы в C# (работа с ними из дизайнера)
От: Ziaw Россия  
Дата: 15.01.08 08:49
Оценка:
Здравствуйте, AndrewZomb, Вы писали:

AZ>Хоть этот вопрос и не настолько актуален уже, но все же интересно, можно ли так сделать..

AZ>Значит имеем контрол MyControl. Другой человек будет делать формы с этим контролом. Форм будет 20-30 штук и на каждой будет 20-60 этих контролов.
AZ>С этими формами работает один и тот же код, которому без разницы что за форма и сколько их, потому что он работает с именами контролов, выделяя из них группу и индекс.
AZ> Т.о. на каждой форме надо дать контролам имена по группам и индексам: MyControl_A1..n, MyControl_B1..m, MyControl_C1..k, где n,m,k — количество контролов в разной группе. Разным группам контролов присваиваются разные свойства (их довольно много).
AZ>Задача состоит в том, чтобы облегчить жизнь человеку, который будет делать эти формы. Т.е. сделать так, чтобы можно было кинуть на форму один контрол, задать ему нужные свойства и имя, и скопировать\вставить его столько раз сколько их таких требуется, причем в имени должна меняться только последняя цифра. Или сделать еще как-нибудь, но чтобы избавится от необходимости каждый раз давать имя контролу.

зачем хранить данные в имени переменной?
для привязки любых данных к контролу есть замечательное свойство Tag

  myControl.Tag = new Tuple<char, int, char>('B', 1, 'm');
... << RSDN@Home 1.2.0 alpha rev. 786>>
Re[6]: Свои контролы в C# (работа с ними из дизайнера)
От: Аноним  
Дата: 15.01.08 09:56
Оценка:
Здравствуйте, Ziaw, Вы писали:

Z>зачем хранить данные в имени переменной?

Z>для привязки любых данных к контролу есть замечательное свойство Tag

Z>
Z>  myControl.Tag = new Tuple<char, int, char>('B', 1, 'm');
Z>


Да я знаю что в Tag можно поместить любой объект, но тогда как человек, делающий формы будет заполнять эти данные? А как обратиться к нужному контролу зная его данные? Если только перебирать все и сравнивать..

В общем свои тут тоже не все просто..
Re[5]: Свои контролы в C# (работа с ними из дизайнера)
От: Pavel_Agurov Россия  
Дата: 16.01.08 20:21
Оценка:
AZ>Хоть этот вопрос и не настолько актуален уже, но все же интересно, можно ли так сделать..
AZ>Значит имеем контрол MyControl. Другой человек будет делать формы с этим контролом. Форм будет 20-30 штук и на каждой будет 20-60 этих контролов.
AZ>С этими формами работает один и тот же код, которому без разницы что за форма и сколько их, потому что он работает с именами контролов, выделяя из них группу и индекс.
AZ> Т.о. на каждой форме надо дать контролам имена по группам и индексам: MyControl_A1..n, MyControl_B1..m, MyControl_C1..k, где n,m,k — количество контролов в разной группе. Разным группам контролов присваиваются разные свойства (их довольно много).
AZ>Задача состоит в том, чтобы облегчить жизнь человеку, который будет делать эти формы. Т.е. сделать так, чтобы можно было кинуть на форму один контрол, задать ему нужные свойства и имя, и скопировать\вставить его столько раз сколько их таких требуется, причем в имени должна меняться только последняя цифра. Или сделать еще как-нибудь, но чтобы избавится от необходимости каждый раз давать имя контролу.

Вообще-то все равно не понятно что такое группа. Если группа — это компоненты, объедененные по имени (с разными номерами), то как собственно тогда задавать это имя, если не задав имя не понятно в какой он группе?
Предложу решение, которое можно расширить по необходимости. Недостаток имени в том что его нельзя задать всем контролам одновременно, но можно добавить свойство всем компонентам и задавать его, как это делает например tooltip.

Посему далаем так. Создаем компонент, реализующий IExtenderProvider, т.е. он будет добавлять ВСЕМ компонентам свойство GroupIndex:


    [ProvideProperty("GroupIndex", typeof(Control))]
    [DesignerAttribute(typeof(GroupIndexerDesigner))]
    public class GroupIndexer : Component, IExtenderProvider
    {
        private Hashtable hashIndexs;
        private IList<Component> components;

        public GroupIndexer()
        {
            hashIndexs = new Hashtable();
            components = new List<Component>();
        }

        ~GroupIndexer()
        {
            Dispose(false);
        }

        public bool CanExtend(object extendee)
        {
            if (extendee is Control && !(extendee is GroupIndexer) && !(extendee is Form))
            {
                return true;
            }
            return false;
        }

        public int GetGroupIndex(Control parent)
        {
            if (hashIndexs.Contains(parent))
            {
                return (int)hashIndexs[parent];
            }
            else
            {
                return 0;
            }
        }

        public void SetGroupIndex(Control parent, int value)
        {
            if (value == 0)
            {
                hashIndexs.Remove(parent);
                components.Remove(parent);
            }
            else
            {
                if (hashIndexs.Contains(parent))
                {
                    hashIndexs[parent] = value;
                }
                else
                {
                    hashIndexs.Add(parent, value);
                    components.Add(parent);
                }
            }
        }

        // Освобождение ресурсов
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                hashIndexs.Clear();
                hashIndexs = null;
                components.Clear();
                components = null;
            }
            base.Dispose(disposing);
        }

        public IList<Component> Components
        {
            get
            {
                return components;
            }
        }
    }


Если индекс не 0, то компонент считается входящим в группу. Если 0 — удаляем из групп.

Дизайнер компонента очень простой. Он только добавляет меню:


    public class GroupIndexerDesigner : ComponentDesigner
    {
        private DesignerActionListCollection actionLists;

        public override DesignerActionListCollection ActionLists
        {
            get
            {
                // Если еще не создавали actionList
                if (actionLists == null)
                {
                    // Создаем ActionList
                    actionLists = new DesignerActionListCollection();
                    // Добавляем тег
                    actionLists.Add(new TooltipsActionList(this.Component));
                }

                return actionLists;
            }
        }

    }


Меню описывается так:


    public class GroupIndexActionList : DesignerActionList
    {
        GroupIndexer component;
        DesignerActionUIService designerActionUIService;

        public GroupIndexActionList(IComponent component)
            : base(component)
        {
            // Сохраняем ссылку на редактируемый компонент
            this.component = component as GroupIndexer;
            // Сохраняем ссылку на ActionList сервис
            designerActionUIService = GetService(typeof(DesignerActionUIService)) as DesignerActionUIService;
        }

        public void ApplyName()
        {
            IList<Component> list = component.Components;
            for (int i = 0; i < list.Count; i++)
            {
                list[i].Site.Name = "group_"+i.ToString();
            }
        }

        public override DesignerActionItemCollection GetSortedActionItems()
        {
            DesignerActionItemCollection items = new DesignerActionItemCollection();
            items.Add(new DesignerActionMethodItem(this, "ApplyName", "ApplyName"));
            return items;
        }

    }


Собвстенно вся работа производится в методе ApplyName, который пока просто переименовывает все компоненты, у которых индекс не 0. А по идее должен раскидывать имена согласно группам. Ну это уже дело техники, а мне лень...

Процесс выглядит так: накидываем компонентов, добавляем на форму GroupIndexer, выделяем первую группу и массово задаем индекс 1, затем вторую и т.д. Потом тыкаем в GroupIndexer и вызываем меню ApplyName. Оно все переименовывает махом.

Сюда надо будет добавить логики проверки если вдруг имена пересекуться и т.д. Но это уже на любителя.
Re[6]: Свои контролы в C# (работа с ними из дизайнера)
От: Pavel_Agurov Россия  
Дата: 17.01.08 06:08
Оценка:
P_A> [ProvideProperty("GroupIndex", typeof(Control))]
P_A> [DesignerAttribute(typeof(GroupIndexerDesigner))]
P_A> public class GroupIndexer : Component, IExtenderProvider
P_A> {
P_A> private Hashtable hashIndexs;
P_A> private IList<Component> components;

P_A> public IList<Component> Components

P_A> {
P_A> get
P_A> {
P_A> return components;
P_A> }
P_A> }
P_A> }

Ну и собственно это свойство можно переделать чтобы был доступ к компонентам группы по индексу и номеру группы.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.