Работа с динамически образованными контролами на фopме в VB.

Язык Visual Basic на платформе .NET.

Модераторы: Ramzes, Sebas

Thomas
Бывалый
Бывалый
Аватара пользователя
 
Сообщения: 246
Зарегистрирован: 12.11.2005 (Сб) 0:17
Откуда: "Сказочное королевство"

Работа с динамически образованными контролами на фopме в VB.

Сообщение Thomas » 12.11.2005 (Сб) 1:05

Приветствую всех!
Занимаюсь в вечерней школе изучением VB.NET , новичок.
Ситуация:
На форме по команде из меню динамически создаются четыре десятка контролов. По заданию нужно PictureBox, но я пока использую Label.
В каждую загружается картинка. Двадцать пар. Расположение случайное.
Поверх разместил еще столько же Label в качестве "рубашки". Типа как в картах. Написал обработчик событий - клик мышкой. При помощи метода SendToBack убираю кликнутую Label на задний план. Становиться видимой картинка. Теперь кликаем по другой "рубашке" и таким же способом открываем другую картинку. Через некоторое время или после клика по третьей "рубашке" первая картинка должна закрыться, т.е. "рубашка" должна опять выйти на передний план. Метод BringToFront
Вопрос:
Как определить последовательность кликов. Первый, второй. А потом их поменять. Второй должен стать первым, а новый клик вторым. Ну и так далее.

Пробую использовать метод nrE = Me.Controls.IndexOf(sender) где nrE индекс контрола по которому кликнули и потом при помощи этого индекса закрывать контрол Me.Controls.Item(nrE).BringToFront().
Но что-то с этими индексами не так.

Работаю с VS 2003, FN1.1. Прилагаю архив с проектом. Коменты там правда на Фламандском, у меня препод Фламандец. Дико извиняюсь.

Заранее всем спасибо и наилучшие пожелания.

ЗЫ в MSDN смотрел, литературу тоже, в учебном курсе со школы изложено очень скудно. Думаю ларчик просто открывается. Ноя пока не нашел ключик. Подскажите.
Вложения
Oefening25(GeheugenSpel).zip
VB.NET учебный проект
(39.46 Кб) Скачиваний: 57

Shurrik
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 110
Зарегистрирован: 20.05.2004 (Чт) 5:35
Откуда: Керчь, Крым

Сообщение Shurrik » 12.11.2005 (Сб) 8:03

Я предложил бы использовать свойство Visible твоих элементов.
В описании переменных завел пару Label
Код: Выделить всё
    Dim lab1, lab2 As Label
    Dim nom As Boolean
    Dim kk As Integer ' количество кликов

А в обработчике событий присваивал sender
Код: Выделить всё
       sender.visible = False
        If nom Then
            If kk > 1 Then lab1.Visible = True
            lab1 = sender
        Else
            If kk > 1 Then lab2.Visible = True
            lab2 = sender
        End If : nom = Not nom : kk = kk + 1
Колесо: Хочешь жить? Умей вертеться.

Shurrik
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 110
Зарегистрирован: 20.05.2004 (Чт) 5:35
Откуда: Керчь, Крым

Сообщение Shurrik » 12.11.2005 (Сб) 8:45

Есть еще один вариант. Сохранить картинки в ImageList , где 0 - рубашка, 1-52 изображение карт. Количество Label = 52, свойство Tag= номер карты, а дальше почти тоже самое.
Код: Выделить всё
        If sender.ImageIndex = 0 Then
            If nom Then
                If kk > 1 Then lab1.ImageIndex = 0
                lab1 = sender
            Else
                If kk > 1 Then lab2.ImageIndex = 0
                lab2 = sender
            End If : nom = Not nom : kk = kk + 1
            sender.imageIndex = sender.tag
        End If

проверил, все работает.
Колесо: Хочешь жить? Умей вертеться.

Thomas
Бывалый
Бывалый
Аватара пользователя
 
Сообщения: 246
Зарегистрирован: 12.11.2005 (Сб) 0:17
Откуда: "Сказочное королевство"

Сообщение Thomas » 12.11.2005 (Сб) 13:23

Shurrik
Приветствую.
Спасибо за оперативность.
Сейчас буду пробовать.

Всех благ. :)

Thomas
Бывалый
Бывалый
Аватара пользователя
 
Сообщения: 246
Зарегистрирован: 12.11.2005 (Сб) 0:17
Откуда: "Сказочное королевство"

Сообщение Thomas » 12.11.2005 (Сб) 23:16

Доброго времени суток.
Shurrik
Прописал я твой пример в обработчик собития.
Не идет. Когда по третьей Label кликаешь выдает ошибку не задана ссылка на обьект. В данный момент выполнения обьект Lab2 не имеет значения. То что он приобрел при первом клике уже утеряно, а нового нет.
Код: Выделить всё

Private Sub Geklikt(ByVal sender As System.Object, ByVal e As System.EventArgs)
        Dim nom As Boolean
        Dim lab1, lab2 As Label
        sender.visible = False
        If nom Then
            If nrClick > 1 Then lab1.Visible = True
            lab1 = sender
        Else
            If nrClick > 1 Then lab2.Visible = True
            lab2 = sender
        End If : nom = Not nom : nrClick += 1

    End Sub

Я так понимаю идея была присваивать переменным lab1 lab2 значения первой кликнутой и второй кликнутой, а потом заменять.
И потом если предусмотреть закрытие первой label после истечения некоторого времени, т о это событие должен обрабатывать timer. И как в ту субрутину передавать значение переменной, которая отражает которую label нужно закрывать.

Если не трудно, заархивируй весь проект и выложи.

Shurrik
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 110
Зарегистрирован: 20.05.2004 (Чт) 5:35
Откуда: Керчь, Крым

Сообщение Shurrik » 13.11.2005 (Вс) 7:32

Описание переменных не в обработчике событий, а там же, где и твой nrClick.
Колесо: Хочешь жить? Умей вертеться.

Thomas
Бывалый
Бывалый
Аватара пользователя
 
Сообщения: 246
Зарегистрирован: 12.11.2005 (Сб) 0:17
Откуда: "Сказочное королевство"

Сообщение Thomas » 13.11.2005 (Вс) 12:04

Shurrik
Доброго времени суток.
Только зашел, прочитал,спасибо за поправку(действительно эти две переменные нужно обьявлять глобально, а не в процедуре), исправил - проверил.
Но все равно засада осталась. :(
Первая картинка (первый клик) открывается , вторая картинка (второй клик) открывается и тут, внимание, третья картинка (третий клик) открывается и вместо первой закрывается вторая картинка, а первая остается видимой. Ну и так далее, каждый последующий клик закрывает предыдущую картинку. А первая так и остается видимой.

Сейчас через полчаса сяду конкретно разбираться. Может и ты ( ничего если на ты?) чего еще подскажешь.

Самые наилучшие пожелания. :)

Shurrik
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 110
Зарегистрирован: 20.05.2004 (Чт) 5:35
Откуда: Керчь, Крым

Сообщение Shurrik » 14.11.2005 (Пн) 6:10

Dim nom As Boolean - тоже убери из обработчика событий.
Колесо: Хочешь жить? Умей вертеться.

Thomas
Бывалый
Бывалый
Аватара пользователя
 
Сообщения: 246
Зарегистрирован: 12.11.2005 (Сб) 0:17
Откуда: "Сказочное королевство"

Сообщение Thomas » 01.12.2005 (Чт) 16:37

Приветствую всех.
Шурик привет.

Переписал я код проги по другому. Но появилась проблема.
После первого корректного удаления одинаковых картинок, начинается непонятка.
Индекс контрола который должен быть удален в дебугрежиме отображается правильно, но удаляется контрол со следующим по порядку номером. В чем причина не догоняю.
Может посмотришь, чего увидишь, подскажешь.
Заранее спасибо.
Код: Выделить всё

Public Class frmGeheugenSpel
    Inherits System.Windows.Forms.Form

#Region " Windows Form Designer generated code "

   
    Private Tindex(40) As Int16
    Dim x, y As Int16
    Dim k1 As Int16 = -1
    Dim k2 As Int16 = -1

    Private Sub mnuStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuStart.Click
        '
        ' //invoelen geheugen tabel met 20 paar indexen voor ico's
        ' // создаю в памяти масив для записи 20-ти пар индексов рисунков
        Dim index As Int16
        For i As Int16 = 1 To 20 ' //запись первого индекса из пары
            index = CInt(Int((40 * Rnd()) + 1))
            If Tindex(index) = 0 Then ' //если ячейка пуста то пишем туда значение
                Tindex(index) = i
            Else
                i -= 1  ' //если ячека уже занята, то генерируем по новой и снова пытаемся записать.
            End If
        Next
        For j As Int16 = 1 To 20 ' //запись второго индекса из пары
            index = CInt(Int((40 * Rnd()) + 1))
            If Tindex(index) = 0 Then
                Tindex(index) = j
            Else
                j -= 1
            End If
        Next
        '
        ' // tovoegen 40 dinamisch controls
        ' // динамически создаю 40 picturebox
        Dim tel As Int16 = 1
        Dim count As Int16 = 1
        'creeren voorgrond labels
        Dim pic As PictureBox
        For r As Int16 = 1 To 5
            For k As Int16 = 1 To 8
                pic = New PictureBox
                pic.Name = "pic" & tel
                pic.BorderStyle = BorderStyle.Fixed3D
                pic.Width = 20
                pic.Height = 20
                pic.Location = New Point(k * 30, r * 30)
                pic.Tag = Tindex(tel)   ' //привязываю свойство контрола tag к индексу картинки для него
                pic.Image = Image.FromFile("..\SUN.ICO") ' //для начала показываю "рубашку"
                AddHandler pic.Click, AddressOf Geklikt
                Me.Controls.Add(pic)
                tel += 1
            Next
        Next

    End Sub

    Private Sub Geklikt(ByVal sender As System.Object, ByVal e As System.EventArgs)
        ' //при старте проги переменные X и Y (им присваивается значение tag  контролов для сравнения) равны нулю
        ' //и значения переменных к1 и к2 (обозначающих второй и первый клик) указаны как -1
        ' //это сделано потому как индексы динамически созданных контролов начинаются с нуля
        ' //а эти индексы нам нужны для манипуляций с контролами
        sender.Image = Image.FromFile("..\MISC" & sender.tag & ".ICO") ' //показываю истинную картинку
        x = sender.tag ' //присваиваю значение (временное) для последюущего сравнения
        k2 = Me.Controls.IndexOf(sender) ' //начинаю и продолжаю в процессе работы со второго клика
        ' //при старте считаем что "первый" клик уже был
        Application.DoEvents()  ' //использую замедление для того чтобы можно было видеть картинку (первую) до закрытия
        System.Threading.Thread.Sleep(1000)  ' //или удаления пары картинок
        If x = y Then  ' //проверяем идентичность первой и второй картинки сравнивая tag  каждой из них
            ' //при первом клике(старт программы) х сравнивается с нулем
            sender.dispose()
            Me.Controls.Item(k1).Dispose() 'проверить не корректно удаляет вторую и далее пары.к1 соответствует, но удаляет следующий контрол
            'Me.Controls.Item(k2).Dispose()
            x = 0 : y = 0 ' //после удаления идентичных картинок обнуляем параметры сравнения
            k1 = -1 : k2 = -1 ' //а так же "обнуляем" индексы певого кликнутого и второго кликнутого контрола
        Else  ' //картинки не одинаковы идем дальше
            If k1 >= 0 Then  ' //это условие нужно только при старте, потому как нельзя закрыть первый же контрол не открыв второго
                CType(Me.Controls.Item(k1), PictureBox).Image = Image.FromFile("..\SUN.ICO")
            End If
            y = x ' //переписываем значения для контрола который будет считаться первым кликнутым
            k1 = k2
        End If

    End Sub

    Private Sub mnuEinde_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuEinde.Click
        Me.Close()
    End Sub
End Class


ЗЫ если нужно выложить архив со всем, то выложу

Shurrik
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 110
Зарегистрирован: 20.05.2004 (Чт) 5:35
Откуда: Керчь, Крым

Сообщение Shurrik » 02.12.2005 (Пт) 4:20

Ты делаешь ту же ошибку, что и я когда-то. При создании массива контролов нельзя удалять контрол из середины массива, это нарушает индексы контролов. Поэтому не удаляй их, а делай Visible=False. Или не цепляйся за индекс, а используй имя или Tag.
Колесо: Хочешь жить? Умей вертеться.

Thomas
Бывалый
Бывалый
Аватара пользователя
 
Сообщения: 246
Зарегистрирован: 12.11.2005 (Сб) 0:17
Откуда: "Сказочное королевство"

Сообщение Thomas » 03.12.2005 (Сб) 0:39

Доброго времени суток.
Спасибо за подсказку про изменение индексов контролов при удалении.
Но у меня встречный вопрос, пишешь - используй имя или Tag, а как?
Что есть синтаксис в данном случае?
Про видимость - невидимость, это интересно. Должно работать.
Но в задании стоит - если обьекты совпадают, то удалять оба.
Значит нужно искать как корректно удалить контрол.

Еще раз спасибо и удачи во всем.

Shurrik
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 110
Зарегистрирован: 20.05.2004 (Чт) 5:35
Откуда: Керчь, Крым

Сообщение Shurrik » 03.12.2005 (Сб) 11:13

Твоя программа чревата неожиданностями. Например, что будет, когда пользователь кликнет mnuStart до конца игры? Старые контролы еще не убраны, а новые будут создаваться и мешаться со старыми, создавая путаницу. Поэтому я и советую создать контролы при старте, а в процессе игры делать их невидимыми.
Но если тебе приспичило именно удалять их, то надо было воспользоваться моим предыдущим советом, где я предлагал работать по ссылкам на контролы.
Здесь важны два момента: первое – это номер картинки (используем Tag) и состояние картинки в данный момент (используем Enable, когда видна «рубашка» - состояние True и по картинке можно кликать, а когда видна «картинка» - состояние False и картинка на клик не реагирует)
Описательная часть:
Код: Выделить всё
    Dim Tindex(40) As Int16
    Dim nom As Boolean        '  для переключения с p1 на р2 
    Dim kk As Integer         '  подсчет кликов
    Dim p1, p2 As PictureBox  ' ссылки на открытые картинки

Твоя функция для клика:
Код: Выделить всё
    Private Sub Geklikt(ByVal sender As System.Object, ByVal e As System.EventArgs)
  ' проверка, доступна ли картинка (хотя можно и не проверять)
        If sender.enabled Then
' здесь можешь вставить свою паузу
            If nom Then
                If kk > 1 Then
                    p1.Enabled = True ' делаем доступной снова
                    p1.Image = Image.FromFile("..\SUN.ICO") ' рисуем рубашку
                End If : p1 = sender
            Else
                If kk > 1 Then
                    p2.Enabled = True ' делаем доступной снова
                    p2.Image = Image.FromFile("..\SUN.ICO") ' рисуем рубашку
                End If : p2 = sender
            End If : nom = Not nom : kk = kk + 1
            sender.Enabled = False ' запрещаем доступ
            sender.Image = Image.FromFile("..\MISC" & sender.tag & ".ICO") ' рисуем картинку
            If kk > 1 Then
                ' проверяем совпадение
                If p1.Tag = p2.Tag Then
        ' здесь можешь вставить анимацию исчезновения картинок
                    p1.Dispose() : p2.Dispose() : kk = 0 ' удаляем и клики в 0
                End If
            End If
        End If
    End Sub

Насчет тасования картинок есть способ попроще:
Код: Выделить всё
        ' присваиваем массиву значения индексов от 1 до 20
        For i As Int16 = 0 To 39 : Tindex(i) = (i Mod 20) + 1 : Next
        ' берем каждый поочереди и меняем со случайным другим
        For i As Int16 = 0 To 39
            Dim j = (40 * Rnd()) Mod 40
            If i <> j Then
                Dim a As Int16 = Tindex(i)
                Tindex(i) = Tindex(j) : Tindex(j) = a
            End If
        Next

Да совсем забыл про сбой генератора случайных чисел, а то у тебя начальный расклад будет всегда повторяться при старте программы.
Код: Выделить всё
   Dim j% = Rnd(Now.Millisecond)
Колесо: Хочешь жить? Умей вертеться.

Thomas
Бывалый
Бывалый
Аватара пользователя
 
Сообщения: 246
Зарегистрирован: 12.11.2005 (Сб) 0:17
Откуда: "Сказочное королевство"

Сообщение Thomas » 06.12.2005 (Вт) 14:19

Приветствую.
Вот добрался до компа и пишу.
Спасибо за советы. В принципе я про это уже думал и кое что воплотил в жизнь. В меню добавил несколько пунктов. После старта пункт старт недоступен. В пункте "по новой" вызываю метод очистки формы от созданных элементов управления. Но при удалении удаляется только половина pictureboxов. В чем причина пока не нашел.
Привожу фрагмент кода:
Код: Выделить всё

'// по идее эта процедура должна удалить все созданные контролы типа пикчербох на форме
    Private Sub Opruimen()
        RemoveHandler pic.Click, AddressOf Geklikt
        Dim el As Control
        For Each el In Me.Controls
            If TypeOf el Is PictureBox Then
                Me.Controls.Remove(el)
                el.Dispose()
            End If
        Next
       
    End Sub

Прцедура вызывается из события клик по пункту меню "по-новой"

Кстати пробовал по другому:
Код: Выделить всё
Private Sub Opruimen()
        RemoveHandler pic.Click, AddressOf Geklikt
        For i As Int16 = 1 To 40
            Me.Controls.Remove(Me.Controls.Item(i))
        Next
    End Sub

Здесь оно начинает ругаться когда индекс доходит до 21. Оно в данном случае почему то считает что существует только 20. Хотя если определять индекс элемента управления при помощи выражения
k2 = Me.Controls.IndexOf(sender), то определяется корректно. У последнего правого нижнего picturebox индекс 39.
Что здесь Не Правильно???
По идее в первом случае оно должно перебрать все контролы на форме и если контрол это picturebox, то удалить. Во втором случае просто тупо перебрать по индексам все контролы и тоже удалить.
Можно зайти с другого конца и попробовать удалять контролы по имени. Но тут у меня засада с синтаксисом. Как правильно написать выражение, чтобы оно не ругалось.(VS имеется в виду)
Ведь когда пишешь код контролов еще нет, они создадутся при старте - динамически. И только тогда получат имена. А тут до старта нужно написать код для переборки заднных имен и их удаления. Ну предположим я уже знаю какими будут имена. Типа: pic1, pic2, pic3, ... , pic40. Перебирать можно с for , next. Только вот вот как синтаксически правильно написать это. Пробую пока не получается. все время ругается.

Может кто знает?

Да, для исключения повторного нажатия по уже открытой картинке (спасибо Шурику за подсказку) добавил свойство Enable для активного контрола. Сразу после определения индекса контрола, его и отключаем.
Код: Выделить всё
Private Sub Geklikt(ByVal sender As System.Object, ByVal e As System.EventArgs)
        Timer1.Start()
        sender.Image = Image.FromFile("..\MISC" & sender.tag & ".ICO")
        x = sender.tag
        k2 = Me.Controls.IndexOf(sender)
        Me.Controls.Item(k2).Enabled = False '/и далее по тексту после проверки идентичности картинок включаем его опять.



Если проблему с удалением контролов решить, то можно выложить окончательный вариант с подробными коментами кода. Может кому еще пригодиться.

Всем самые наилучшие пожелания.

Shurrik
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 110
Зарегистрирован: 20.05.2004 (Чт) 5:35
Откуда: Керчь, Крым

Сообщение Shurrik » 07.12.2005 (Ср) 4:15

Thomas, ты так и не понимаешь, что происходит с индексами при удалении. Например у тебя есть три индекса 0,1,2 и ты удаляешь любой из трех. Остались 0 и 1, т.е. индексы у оставшихся изменились. Поэтому, чтобы удалить контрола с индексами от 0 до N, то делаем цикл For i=0 To N Step -1 и удаляем контрол Control(i). Но можно и все время удалять, т.е. For i=0 To N и удаляем контрол Control(0).
Колесо: Хочешь жить? Умей вертеться.

Thomas
Бывалый
Бывалый
Аватара пользователя
 
Сообщения: 246
Зарегистрирован: 12.11.2005 (Сб) 0:17
Откуда: "Сказочное королевство"

Сообщение Thomas » 07.12.2005 (Ср) 21:00

Привет.
Я так понимаю, мы с тобой вдвоем ведем беседу.
Что происходит с контролами при удалении я дотумкал.
Правильно ты заметил при удалении одного контрола у остальных индексы меняются. Если сделать люс , как сделал я, от 0 до 40, то естественно ничего путного не получается. Так как удали люс первый контрол с индексом ноль, то второй контрол с индексом один тут же получает индекс ноль. А люс идет дальше и удаляет контрол с индесом один. Ну и так далее. Поэтому я сделал обратный люс с 40 до 1 с шагом -1. В этом случае, я думаю (???), удаление происходит корректно.
Код: Выделить всё

Private Sub Opruimen()
        RemoveHandler pic.Click, AddressOf Geklikt
        For i As Int16 = 40 To 1 Step -1
            Me.Controls.Item(i).Enabled = True
            Me.Controls.Remove(Me.Controls.Item(i))
        Next
    End Sub

Конструкция for next работает. Но почему, блин, не работала конструкция не связанная с индексами контролов, а лишь с их типом???
Код: Выделить всё

Private Sub Opruimen()
        RemoveHandler pic.Click, AddressOf Geklikt
        Dim el As Control
        For Each el In Me.Controls
            If TypeOf el Is PictureBox Then
                Me.Controls.Remove(el)
                el.Dispose()
             End If
        Next
    End Sub
в чем причина здесь?

Теперь опять баг. Контролы удаляются. Это хорошо. Но если хочеться стартануть по новой играть. Не а. Ни ни. Прога зависает на прочь.
По идее, старые picturebox удалили, форма свободна, ничего не мешает создавать снова и размещать picturebox на форме. Но не работает.
Шурик, в чем тут проблема???

Всего наилучшего.

Thomas
Бывалый
Бывалый
Аватара пользователя
 
Сообщения: 246
Зарегистрирован: 12.11.2005 (Сб) 0:17
Откуда: "Сказочное королевство"

Сообщение Thomas » 07.12.2005 (Ср) 21:32

Shurrik
Баг нашел сам. Контролы то я удалял, а про таблицу с индексами забыл. Если ее не обнулить, то прога виснет. Значиться после удаления контролов обнуляем таблицу с индексами картинок и можно начинать по-новой.
Еще раз спасибо за помощь и наставления.

ЗЫ На днях перепишу менюшки и остальное на русский и выложу окончательный вариант с моими коментариями. Может кому еще из новичков пригодиться.

Удачи всем.


Вернуться в Visual Basic .NET

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 98

    TopList