Контрол "Смайлы"
Хочу сделать выбор смайлов также, как в Qip Infium 9020:
[ATTACH]3332[/ATTACH]
Т.е. Нажимаешь на рисунок с рожицей и вылазить мини-окошечко, в котором все смайлы.
Может кто-то видел подобный контрол, или как его вырвать из QIP.
Если самому писать, то как именно. Я делал свои контролы раньше, но на основе существующих. Есть идеи?
Да там и не элемент управления, а простой диалог без рамки. Уж где-где, а здесь у вас полный простор для творчества...
Да там и не элемент управления, а простой диалог без рамки. Уж где-где, а здесь у вас полный простор для творчества...
Ну это ясно, а как лучше сделать, как бы сделали профессионалы? а то я сделаю как нибудь не очень....не лучшим способом
Тупейшее решение - кнопки с картинками на сетке.
Я не просил тупейшее решение! Я просил профессиональное)) Так я точно не сделаю. Экономить место формы надо. Я вот думаю сделать контрол....Но запарюсь наверно. В качестве контрола хочу сделать то самое мини окошечко....
Но пока ищу лучшие решения....
А про диалог.... и как этот диалог сделать? Я понимаю диалог OpenFile, SaveFile, Print, FolderBrowser.... А это как? странно... пошел искать наилучшее для себя решение
Самое профессиональное решение зачастую самое простое (привет Раскину).
Никто никогда не использует выпадающее комбо со смайлами - до мышки тяжело тянуться от клавиатуры, особенно когда набираешь текст. Потому можно повесить их выбор на хоткей: нажали на хоткей (Alt + S, например) и рядом с кареткой появилось окошко (без бордера) с такой сеткой, стрелочками выбрали смайлик - он появился в текущей позиции.
Никто никогда не использует выпадающее комбо со смайлами - до мышки тяжело тянуться от клавиатуры, особенно когда набираешь текст. Потому можно повесить их выбор на хоткей: нажали на хоткей (Alt + S, например) и рядом с кареткой появилось окошко (без бордера) с такой сеткой, стрелочками выбрали смайлик - он появился в текущей позиции.
Да не это главное - это то дело вкусов, хотя ты прав. Меня интерсует что это за окно, которое появляется, это просто форма без рамки или что-то иное?
Да, форма без рамки.
Создал свойство 'Items', это массив Bitmap, т.е. самих смайлов непосредерственно.
Работает примерно так: при происхождении OnPaint я перебираю все bitmap из массива и рисую через System.Drawing сам. Ну для ускорения всего дела я сохраняю это нарисованное в отдельный bitmap, чтобы в следующий раз на OnPaint не перебирать массив вновь, а просто 'срисовать' с уже готового bitmap.
Так же я отслеживаю движение мыши и по ее координатам определяю, есть ли в этом месте смайл, если есть - рисую вокруг него рамку в наш временный bitmap, якобы его выделили. После этого, как написано в MSDN, вызываю Invalidate, чтобы вызвать перерисовку OnPaint, чтобы отобразить результаты выделения смайла. И все работает. Но возникает мерцание, хотя код хорошо отлажен и для укорения используется временный bitmap, в котором рисуется весь элемент, а только потом переносится на реальный элемент. Все вообще выглядит красиво.
недолго отслеживая, я заметил, что мерцание возникает из-за того, что после вызова Invalidate прорисовка происходит 3(ТРИ!!!) раза, хотя должна произойти только один раз.
Из за этого у меня 3 раза копируется изображение из временного bitmap в реальный элемент. Invalidate стоит в MouseMove, поэтому вызывается часто. А учитывая, что 1 MouseMove вызывает за каждый раз 3 перерисовки, то это и вызывает мерцание.
Помогите избавится от этого глюка. Есть ли аналог Invalidate, который вызовет перерисовку только один раз?
P.S. Если нужен код или сам контрол, то скажите, я выложу, просто я сейчас с телефона...
Invalidate() перерисовывает окно или его часть лишь раз. Более того, вызовы Invalidate() для совпадающих областей не накапливаются (то есть, если вызвать Invalidate() два раза подряд, отрисовка должна произойти лишь единожды), так что с тремя разами проблемы, наверное, где-то у вас...
Все можно сделать гораздо проще. Создаем UserControl, на него укладываем FlowLayoutPanel и создаем на ней кнопки под каждый смайлик. На какой кликнул пользователь - тот смайл он и выбрал.
И ничего не нужно перерисовывать.
Таким контролом тоже можно делиться, уверяю :rolleyes:
1. Решить проблему того, что смайлы могут не уместиться и нужно показать vscroll, чтобы рисовались именно те смайлы. Перемотка как бы...
2. А вот это жесть... Сейчас у меня отображается в bitmap *.bmp файлы. А что если я хочу анимацию gif? Одна проблема-Рисовать кадры друг за другом, а другая- как рисовать? Как получить кадры, из которых состоит gif?
Есть идеи?:)
а с 3 перерисовками... Я понял где глюк еще утром. Действительно, Invalidate выполняется один раз, даже если его поставить в цикле...
У меня в MouseMove стоит проверка: 'если выделен элемент, т.е мышь находится над смайлом, то рисуем ему рамку. Но у меня нет проверки, что эта рамка была нарисована до этого. Позже сяду за компьютер- попытаюсь исправить, надеюсь это уберет мерцание при движении мыши. Помогите с проблемами выше, особенно со 2 про gif
Прилаживаю проект, код и сам контрол
[ATTACH]3338[/ATTACH]
Контрол пока нельзя присоединить к проекту, видимо потому что нет конструктора... но из под отладчика работает... измените btnAdd на свои смайлы bmp и нажмите эту кнопку...
Public Class SmilesControl
Dim SmilesBitmaps() As Bitmap
Dim CountHorizontal, CountVertical As Integer
Dim SpaceHor As Integer = 5
Dim SpaceVert As Integer = 5
Dim SelIndex As Integer = -1
Dim NewSelIndex As Integer
Dim PaddSpaces As Integer = 5
Dim smilespos As New List(Of Integer)
Dim StatImage As New Bitmap(Me.Width, Me.Height)
Dim SelImage As Bitmap
Dim SelGraph As Graphics
Dim MyGraph As Graphics = Graphics.FromImage(StatImage)
Dim StatClear = True
Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
MyBase.OnPaint(e)
If SmilesBitmaps Is Nothing OrElse SmilesBitmaps.Count = 0 Then Exit Sub
If SelIndex = -1 Then
If StatClear Then Redraw()
e.Graphics.DrawImage(StatImage, 0, 0)
Else
e.Graphics.DrawImage(SelImage, 0, 0)
End If
End Sub
Public Sub Redraw()
Dim CurX, CurY, CurHorizontal, CurVertical, MaxHeight As Integer
CurX = PaddSpaces
CurY = PaddSpaces
StatImage = New Bitmap(Me.Width, Me.Height)
MyGraph = Graphics.FromImage(StatImage)
smilespos.Clear()
If SmilesBitmaps Is Nothing OrElse SmilesBitmaps.Count = 0 Then Exit Sub
For Each mybit As Bitmap In SmilesBitmaps
If mybit.Width + CurX > Me.Width - VSBar.Width Then
CurX = PaddSpaces
CurY += MaxHeight + SpaceVert
CurHorizontal = 0
CurVertical += 1
End If
If mybit.Height > MaxHeight Then MaxHeight = mybit.Height
MyGraph.DrawImage(mybit, CurX, CurY)
smilespos.Add(CurX)
smilespos.Add(CurY)
CurX += mybit.Width + SpaceHor
CurHorizontal += 1
Next
StatClear = False
CurX = 0
CurY = 0
CurHorizontal = 0
CurVertical = 0
MaxHeight = 0
End Sub
<Category("Главное"), Description("Определяет и возвращает массив Bitmap, которые отображаются на элементе управления"), Browsable(True)> _
Public Property Items() As Bitmap()
Get
Return SmilesBitmaps
End Get
Set(ByVal value() As Bitmap)
If value.Count = 0 Then Exit Property Else RemoveHandler MyBase.MouseMove, AddressOf SmilesControl_MouseMove
SmilesBitmaps = value
AddHandler MyBase.MouseMove, AddressOf SmilesControl_MouseMove
Invalidate()
End Set
End Property
<Category("Главное"), Description("Определяет и возвращает количество Bitmap по горизонтали"), Browsable(True)> _
Public Property CountHor() As Integer
Get
Return CountHorizontal
End Get
Set(ByVal value As Integer)
CountHorizontal = value
End Set
End Property
<Category("Главное"), Description("Определяет и возвращает количество Bitmap по вертикали"), Browsable(True)> _
Public Property CountVert() As Integer
Get
Return CountVertical
End Get
Set(ByVal value As Integer)
CountVertical = value
End Set
End Property
<Category("Главное"), Description("Определяет и возвращает количество пикселей между двумя соседними Bitmap по горизонтали"), Browsable(True)> _
Public Property SpaceHorizontal() As Integer
Get
Return SpaceHor
End Get
Set(ByVal value As Integer)
SpaceHor = value
End Set
End Property
<Category("Главное"), Description("Определяет и возвращает количество пикселей между двумя соседними Bitmap по вертикали"), Browsable(True)> _
Public Property SpaceVertical() As Integer
Get
Return SpaceVert
End Get
Set(ByVal value As Integer)
SpaceVert = value
End Set
End Property
<Category("Главное"), Description("Определяет и возвращает количество пикселей отступа внутри объекта"), Browsable(True)> _
Public Property PaddingSpaces() As Integer
Get
Return PaddSpaces
End Get
Set(ByVal value As Integer)
PaddSpaces = value
End Set
End Property
Private Function GetIndexByCoords(ByVal X, ByVal Y) As Integer
For i As Integer = 0 To smilespos.Count - 1 Step 2
If (smilespos(i) <= X AndAlso smilespos(i + 1) <= Y) AndAlso (SmilesBitmaps(i / 2).Width + smilespos(i) >= X AndAlso SmilesBitmaps(i / 2).Height + smilespos(i + 1) >= Y) Then
Return i / 2
End If
Next
Return -1
End Function
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn1.Click
SmilesBitmaps = New Bitmap() {New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\2.bmp")}
Me.Invalidate()
End Sub
Private Sub btn2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn2.Click
Redraw()
Invalidate()
End Sub
Private Sub SmilesControl_MouseMove(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseMove
NewSelIndex = GetIndexByCoords(e.X, e.Y)
If NewSelIndex = -1 Then SelIndex = -1
If NewSelIndex <> SelIndex AndAlso NewSelIndex <> -1 Then
SelIndex = NewSelIndex
DrawBorder()
End If
End Sub
Private Sub DrawBorder()
SelImage = StatImage.Clone()
SelGraph = Graphics.FromImage(SelImage)
SelGraph.DrawRectangle(Pens.Black, smilespos(SelIndex * 2) - 1, smilespos(SelIndex * 2 + 1) - 1, SmilesBitmaps(SelIndex).Width + 2, SmilesBitmaps(SelIndex).Height + 2)
Invalidate()
End Sub
End Class
Я хотел еще сократить количество перерисовок так: Рисую первый раз все смайлы и запоминаю в булевскую переменную, что нарисовал смайлы, в следующий раз уже не перерисовывать( мол пусть останется таже картинка), но при происхождении OnPaint контрол уже чист, т.е. рисунка моего старого нет, получается вновь рисовать, а это плохо, это мерцание. Что делать? Как сделать так, чтобы при происхождении OnPaint старый рисунок был не стерт, чтобы мне вновь не рисовать тоже самое.
Public Class SmilesControl
Dim SmilesBitmaps() As Bitmap
Dim CountHorizontal, CountVertical As Integer
Dim SpaceHor As Integer = 5
Dim SpaceVert As Integer = 5
Dim SelIndex As Integer = -1
Dim NewSelIndex As Integer
Dim PaddSpaces As Integer = 5
Dim smilespos As New List(Of Integer)
Dim StatImage As New Bitmap(Me.Width, Me.Height)
Dim SelImage As Bitmap
Dim SelDrawed As Boolean
Dim SelGraph As Graphics
Dim MyGraph As Graphics = Graphics.FromImage(StatImage)
Dim StatClear = True
Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
If SmilesBitmaps Is Nothing OrElse SmilesBitmaps.Count = 0 Then Exit Sub
If SelIndex = -1 Then
If StatClear Then Redraw()
If SelDrawed Then
MyBase.OnPaint(e)
e.Graphics.DrawImage(StatImage, 0, 0)
SelDrawed = False
End If
Else
If Not SelDrawed Then
MyBase.OnPaint(e)
e.Graphics.DrawImage(SelImage, 0, 0)
SelDrawed = True
End If
End If
End Sub
Public Sub Redraw()
Dim CurX, CurY, CurHorizontal, CurVertical, MaxHeight As Integer
CurX = PaddSpaces
CurY = PaddSpaces
StatImage = New Bitmap(Me.Width, Me.Height)
MyGraph = Graphics.FromImage(StatImage)
smilespos.Clear()
If SmilesBitmaps Is Nothing OrElse SmilesBitmaps.Count = 0 Then Exit Sub
For Each mybit As Bitmap In SmilesBitmaps
If mybit.Width + CurX > Me.Width - VSBar.Width Then
CurX = PaddSpaces
CurY += MaxHeight + SpaceVert
CurHorizontal = 0
CurVertical += 1
End If
If mybit.Height > MaxHeight Then MaxHeight = mybit.Height
MyGraph.DrawImage(mybit, CurX, CurY)
smilespos.Add(CurX)
smilespos.Add(CurY)
CurX += mybit.Width + SpaceHor
CurHorizontal += 1
Next
StatClear = False
CurX = 0
CurY = 0
CurHorizontal = 0
CurVertical = 0
MaxHeight = 0
End Sub
<Category("Главное"), Description("Определяет и возвращает массив Bitmap, которые отображаются на элементе управления"), Browsable(True)> _
Public Property Items() As Bitmap()
Get
Return SmilesBitmaps
End Get
Set(ByVal value() As Bitmap)
If value.Count = 0 Then Exit Property Else RemoveHandler MyBase.MouseMove, AddressOf SmilesControl_MouseMove
SmilesBitmaps = value
AddHandler MyBase.MouseMove, AddressOf SmilesControl_MouseMove
Invalidate()
End Set
End Property
<Category("Главное"), Description("Определяет и возвращает количество Bitmap по горизонтали"), Browsable(True)> _
Public Property CountHor() As Integer
Get
Return CountHorizontal
End Get
Set(ByVal value As Integer)
CountHorizontal = value
End Set
End Property
<Category("Главное"), Description("Определяет и возвращает количество Bitmap по вертикали"), Browsable(True)> _
Public Property CountVert() As Integer
Get
Return CountVertical
End Get
Set(ByVal value As Integer)
CountVertical = value
End Set
End Property
<Category("Главное"), Description("Определяет и возвращает количество пикселей между двумя соседними Bitmap по горизонтали"), Browsable(True)> _
Public Property SpaceHorizontal() As Integer
Get
Return SpaceHor
End Get
Set(ByVal value As Integer)
SpaceHor = value
End Set
End Property
<Category("Главное"), Description("Определяет и возвращает количество пикселей между двумя соседними Bitmap по вертикали"), Browsable(True)> _
Public Property SpaceVertical() As Integer
Get
Return SpaceVert
End Get
Set(ByVal value As Integer)
SpaceVert = value
End Set
End Property
<Category("Главное"), Description("Определяет и возвращает количество пикселей отступа внутри объекта"), Browsable(True)> _
Public Property PaddingSpaces() As Integer
Get
Return PaddSpaces
End Get
Set(ByVal value As Integer)
PaddSpaces = value
End Set
End Property
Private Function GetIndexByCoords(ByVal X, ByVal Y) As Integer
For i As Integer = 0 To smilespos.Count - 1 Step 2
If (smilespos(i) <= X AndAlso smilespos(i + 1) <= Y) AndAlso (SmilesBitmaps(i / 2).Width + smilespos(i) >= X AndAlso SmilesBitmaps(i / 2).Height + smilespos(i + 1) >= Y) Then
Return i / 2
End If
Next
Return -1
End Function
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn1.Click
SmilesBitmaps = New Bitmap() {New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\1.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\2.bmp"), New Bitmap("C:\2.bmp")}
End Sub
Private Sub btn2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn2.Click
Redraw()
Invalidate()
End Sub
Private Sub SmilesControl_MouseMove(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseMove
NewSelIndex = GetIndexByCoords(e.X, e.Y)
If NewSelIndex = -1 Then SelIndex = -1
If NewSelIndex <> SelIndex AndAlso NewSelIndex <> -1 Then
SelIndex = NewSelIndex
DrawBorder()
End If
End Sub
Private Sub DrawBorder()
SelImage = StatImage.Clone()
SelGraph = Graphics.FromImage(SelImage)
SelGraph.DrawRectangle(Pens.Black, smilespos(SelIndex * 2) - 1, smilespos(SelIndex * 2 + 1) - 1, SmilesBitmaps(SelIndex).Width + 2, SmilesBitmaps(SelIndex).Height + 2)
Invalidate()
End Sub
End Class
Так вот, я пытаюсь перехватить нажатия клавишь(KeyDown) и перехватываю с кнопок и самого контрола, и если это налево или направо, то я пытаюсь переключить смайлы соответственно - следующий или предыдущий. но на практике переключается фокус между кнопками, как от этого избавится? помогите.
Inherits Button
Public Sub New()
SetStyle(ControlStyles.Selectable, False)
End Sub
End Class
думаю тут все ясно))) чтбы кнопки не имели фокуса...
в конструкторе вызываю Focus
Кстати, спасибо, что сказали про SetStyle... поскольку почитал мсдн и меня привлекло следуующее:
Потом попытаюсь разобраться с этим, чтобы не мерцало. Если работали с тим, поделитесь опытом, исходниками?
SetStyle(ControlStyles.DoubleBuffer, True)
Вроде не мерцает. Как еще можно сделать по-другому?
Процедура:
Select Case e.KeyCode
Case Keys.Right
If SelectedIndex < StsSince(StBlock + 1) Then SelIndex += 1 Else SelIndex = StBlock
Case Keys.Left
If SelIndex > StsSince(StBlock) Then SelIndex -= 1 Else SelIndex = StsSince(StBlock + 1) - 1
Case Keys.Enter
ChoosingElement()
End Select
DrawBorder()
End Sub
Почему так работает, а если поменешь на вот это:
В конструкторе:
то процедура даже не начинает выполняться. С фокусом в обоих случаях одинаково - он есть. Почему так? KeyDown и KeyUp аргументами не отличаются вроде..... че за?))помогите
Хочу сделать выбор смайлов также, как в Qip Infium 9020:
[ATTACH]3332[/ATTACH]
Т.е. Нажимаешь на рисунок с рожицей и вылазить мини-окошечко, в котором все смайлы.
Может кто-то видел подобный контрол, или как его вырвать из QIP.
Если самому писать, то как именно. Я делал свои контролы раньше, но на основе существующих. Есть идеи?
создай второе окно помести туда WebBrowser и загрузи в него страничку со смайликами....
При выборе смайлика меняй текст (в панели статуса с помощью JavaScript) = пути к картинке а WebBrowser-e отслеживай это сообытие и вставляй этот смайлик в твой WebBrowser в котором идет редактирование сообщения
Ну да ладно... Более менее разобрался с созданием контролов...
В конструкторе:
то процедура даже не начинает выполняться. С фокусом в обоих случаях одинаково - он есть. Почему так? KeyDown и KeyUp аргументами не отличаются вроде..... че за?))помогите
как всегда никто не помог, поэтому самому пришлось искать решение. Поискал в MSDN "Control.KeyDown" и в итоге нашел следующее:
и в Control.IsInputKey - метод:
В итоге в голове родился такой РАБОЧИЙ код:
Select Case keyData
Case Keys.Left, Keys.Right, Keys.Up, Keys.Down
Return True
Case Else
Return MyBase.IsInputKey(keyData)
End Select
End Function