.NET-графика Ghosting

Я делаю образец графического интерфейса для нового приложения, над которым мы работаем. Язык уже определен за меня, но мне разрешено использовать любые сторонние библиотеки DLL или надстройки или все, что мне нужно, чтобы графический интерфейс работал как можно более плавно.

Они хотят, чтобы это было очень похоже на mac / ubuntu / vista / Windows 7, поэтому я придумал несколько очень интересных элементов управления и симпатичных функций графического интерфейса. Одна из них - это кнопки увеличения / уменьшения в верхней части экрана, которые увеличиваются в размере при наведении на них указателя мыши (он использует формулу расстояния для расчета размера, на который необходимо увеличить). Когда вы убираете мышь с элементов управления, они снова сжимаются. Эффект выглядит очень профессионально и броско, за исключением того, что появляется эффект двоения, когда кнопка сжимается вниз (и кнопки справа от нее, поскольку они фиксируются на бедре).

Вот как выглядят кнопки в дизайнере:

alt text

Вот несколько фрагментов кода, которые я использую для этого:

всплывает дочерние кнопки внизу при наведении курсора на родительский

Private Sub buttonPop(ByVal sender As Object, ByVal e As System.EventArgs)
    For Each control As System.Windows.Forms.Control In Me.Controls
        If control.GetType.ToString = "Glass.GlassButton" AndAlso control.Location.Y > sender.Location.Y AndAlso control.Location.X >= sender.Location.X AndAlso control.Width < sender.Width AndAlso control.Location.X + control.Width < sender.Location.X + sender.Width Then
            control.Visible = True
        End If
    Next
End Sub

вернуться к нормальному размеру больших кнопок после того, как мышь покинет

Private Sub shrinkpop(ByVal sender As Object, ByVal e As System.EventArgs)
    Dim oldSize As Size = sender.Size
    sender.Size = New Size(60, 60)
    For Each control As System.Windows.Forms.Control In Me.Controls
        If control.GetType.ToString = "Glass.GlassButton" AndAlso control.Location.X > sender.Location.X AndAlso (Not control.Location.X = control.Location.X + (sender.size.width - oldSize.Width)) Then

            control.Location = New Point(control.Location.X + (sender.size.width - oldSize.Width), control.Location.Y)

        End If
        If control.GetType.ToString = "Glass.GlassButton" AndAlso control.Location.Y > sender.Location.Y AndAlso control.Location.X = sender.Location.X AndAlso control.Width < sender.Width Then
            control.Location = New Point(control.Location.X, control.Location.Y + (sender.size.height - oldSize.Height))
            If Windows.Forms.Control.MousePosition.X < control.Location.X Or Windows.Forms.Control.MousePosition.X > control.Location.X + control.Width Then
                control.Visible = False
            End If
        End If
    Next
End Sub

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

    Private Sub buttonMouseMovement(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)
    Dim oldSize As Size = sender.Size
    Dim middle As Point = New Point(30, 30)
    Dim adder As Double = Math.Pow(Math.Pow(middle.X - e.X, 2) + Math.Pow(middle.Y - e.Y, 2), 0.5)
    Dim total As Double = Math.Pow(1800, 0.5)

    adder = (1 - (adder / total)) * 20

    If Not (sender.size.width = 60 + adder And sender.size.height = 60 + adder) Then
        sender.Size = New Size(60 + adder, 60 + adder)
    End If
    For Each control As System.Windows.Forms.Control In Me.Controls
        If control.GetType.ToString = "Glass.GlassButton" AndAlso control.Location.X > sender.Location.X AndAlso (Not control.Location.X = control.Location.X + (sender.size.width - oldSize.Width)) Then
            control.Location = New Point(control.Location.X + (sender.size.width - oldSize.Width), control.Location.Y)
        End If
        If control.GetType.ToString = "Glass.GlassButton" AndAlso control.Location.Y > sender.Location.Y AndAlso control.Location.X >= sender.Location.X AndAlso control.Width < sender.Width AndAlso control.Location.X + control.Width < sender.Location.X + sender.Width AndAlso (Not control.Location.Y = control.Location.Y + (sender.size.height - oldSize.Height)) Then
            control.Location = New Point(control.Location.X, control.Location.Y + (sender.size.height - oldSize.Height))
        End If
    Next
End Sub

увеличивать размер меньших командных кнопок

Private Sub SmallButtonMouseMovement(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)
    Dim oldSize As Size = sender.Size
    Dim middle As Point = New Point(22.5, 22.5)
    Dim adder As Double = Math.Pow(Math.Pow(middle.X - e.X, 2) + Math.Pow(middle.Y - e.Y, 2), 0.5)
    Dim total As Double = Math.Pow(1012.5, 0.5)

    adder = (1 - (adder / total)) * 15

    If Not (sender.size.Width = 45 + adder And sender.size.height = 45 + adder) Then
        sender.Size = New Size(45 + adder, 45 + adder)
    End If
    For Each control As System.Windows.Forms.Control In Me.Controls
        If control.GetType.ToString = "Glass.GlassButton" AndAlso control.Location.Y > sender.Location.Y AndAlso control.Location.X = sender.location.X AndAlso (Not control.Location.Y = control.Location.Y + (sender.size.height - oldSize.Height)) Then
            control.Location = New Point(control.Location.X, control.Location.Y + (sender.size.height - oldSize.Height))
        End If
    Next
End Sub

уменьшение возвращает командные кнопки в правильное положение и скрывает их, если необходимо

    Private Sub SmallShrinkPop(ByVal sender As Object, ByVal e As System.EventArgs)
    Dim oldsize As Size = sender.Size
    If Not (sender.size.width = 45 AndAlso sender.size.height = 45) Then
        sender.size = New Size(45, 45)
    End If

    Dim ChildCounter As Integer = 0

    For Each control As System.Windows.Forms.Control In Me.Controls
        If control.GetType.ToString = "Glass.GlassButton" AndAlso control.Location.X = sender.location.X AndAlso control.Width = sender.width AndAlso control.Location.Y > sender.location.y Then
            ChildCounter += 1
            control.Location = New Point(control.Location.X, control.Location.Y + (sender.size.height - oldsize.Height))
            If Windows.Forms.Control.MousePosition.X < control.Location.X Or Windows.Forms.Control.MousePosition.X > control.Location.X + control.Width Then
                sender.visible = False
                control.Visible = False
            End If
        End If
    Next
    If (ChildCounter = 0 AndAlso Windows.Forms.Control.MousePosition.Y > sender.Location.Y + sender.Height) Or (Windows.Forms.Control.MousePosition.X < sender.Location.X Or Windows.Forms.Control.MousePosition.X > sender.Location.X + sender.Width) Then
        sender.visible = False
        For Each control As System.Windows.Forms.Control In Me.Controls
            If control.GetType.ToString = "Glass.GlassButton" AndAlso control.Location.X = sender.location.x AndAlso control.Width = sender.width Then
                control.Visible = False
            End If
        Next
    End If
End Sub

Что я знаю:

  1. Если бы в форме не было фонового изображения, у меня не было бы проблемы с ореолом.
  2. Если бы это была обычная кнопка, которую я рисую, у меня, вероятно, не было бы проблемы с ореолом.

Что я сделал и как пытался это исправить:

  1. Обеспечение включения двойной буферизации формы (было)
  2. Двойная буферизация вручную с использованием класса bufferedGraphics (не помогло или ухудшило ситуацию)
  3. Убедите дизайнеров, что для этого не нужны фоновые изображения или красивые стеклянные кнопки (нет)
  4. Запустите Invalidate () на прямоугольнике, содержащем форму (не помогло)
  5. Запустите Refresh () в форме (исправлено двоение, но теперь весь экран мигает при перезагрузке изображения)
  6. Сижу в углу кабинки и тихонько плачьте про себя (помогло стрессу, но также не устранило проблему)

Я ищу ответы на следующие вопросы:

  1. Кто-нибудь знает, как избавиться от описываемого мною ореола? Стоит ли реже фокусироваться на изменении размера? я должен сосредоточиться на буферизации фонового изображения?
  2. Есть ли здесь другие технологии, которые мне следует использовать? Существуют ли элементы управления ActiveX, которые были бы лучше, чем унаследованные пользователем .NET? Можно ли сделать так, чтобы пользовательский элемент управления DirectX использовал графическую карту для рисования?
  3. Есть что-то еще, о чем я здесь не думаю?

~~~~~~~~~~~~~~~~~~~~~~~~ Обновление 1: 17.11.2009 9:21 ~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~

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


person Jrud    schedule 04.11.2009    source источник
comment
Вау, я думаю, вы помогаете нам всем этим щедрым кодом и описанием!   -  person John K    schedule 05.11.2009
comment
О том, есть ли другие технологии, которые я должен использовать здесь ?: WPF - это современный уровень создания пользовательских интерфейсов, подобных mac / ubuntu / vista / Windows 7, с помощью VB.NET. Похоже, вы все еще используете Winforms ...   -  person Heinzi    schedule 05.11.2009
comment
Да, я считаю, что WPF был передан нашей управленческой команде, но они отказались от этого, потому что это было слишком ново. Так что да, я застрял в .NET 2005 (они сказали, что мы могли бы перейти на 2008, если бы у меня была веская причина)   -  person Jrud    schedule 05.11.2009
comment
Слишком ново ?? WPF может быть относительно новым, но он идеально подходит для создаваемого вами приложения. Также эта технология будет поддерживаться и улучшаться в будущем. Прошу пересмотреть ...   -  person Meta-Knight    schedule 05.11.2009
comment
Я, конечно, постараюсь заставить их пересмотреть свое решение, но я сомневаюсь, что они будут слишком новы, как спор с кирпичной стеной.   -  person Jrud    schedule 05.11.2009
comment
В качестве случайного элемента вместо: control.GetType.ToString = Glass.GlassButton используйте элемент управления TypeOf Is Glass.GlassButton   -  person Stu    schedule 05.11.2009
comment
Хороший улов, Стю, надо исправить кодовые запахи.   -  person Jrud    schedule 05.11.2009
comment
у вас есть доступ к Spy ++ или подобному инструменту? Если да, можете ли вы проверить, отправляется ли сообщение WM_ERASEBKGND в родительскую форму, когда кнопки удаляются до их меньшего размера?   -  person meklarian    schedule 19.11.2009


Ответы (2)


Есть возможное быстрое решение вашей проблемы. Вставьте этот код в свою форму:

  public partial class Form1 : Form {
    public Form1() {
      InitializeComponent();
      this.IsMdiContainer = true; 
      foreach (Control ctl in this.Controls) {
        if (ctl is MdiClient) {
          ctl.BackgroundImage = Properties.Resources.SampleImage;
          break;
        }
      }
    }
    protected override CreateParams CreateParams {
      get {
        CreateParams cp = base.CreateParams;
        cp.ExStyle |= 0x02000000;  // Turn on WS_EX_COMPOSITED
        return cp;
      }
    }
  }

Флаг стиля работает на XP SP1 и выше. Он выполняет двойную буферизацию всей формы, а не только каждого отдельного элемента управления, и должен устранить эффект двоения, который вы видите.

person Hans Passant    schedule 17.11.2009
comment
Я почти уверен, что это решит проблему с ореолом, однако теперь я не могу видеть фоновые изображения за пределами элемента управления клиентом MDI. Любые идеи? - person Jrud; 18.11.2009
comment
Я не знаю, что это значит. Установка свойства BackgroundImage в MdiClient работает нормально. - person Hans Passant; 18.11.2009
comment
хм ... на моем компьютере он не работает нормально. серый фон отображается вместо BackgroundImage MDIClient. - person Jrud; 19.11.2009
comment
Я обновил пример кода, чтобы установить фоновое изображение окна клиента MDI. Это отлично работает, когда я пробую. Измените этот код, чтобы он воспроизводил вашу проблему. - person Hans Passant; 19.11.2009
comment
Мой управленческий персонал решил, что за кнопками должна быть черная панель и что они должны быть разнесены и не перемещать друг друга, так что это действительно избавляет меня от всей проблемы. Я считаю, что ваш ответ был наиболее правильным и изобретательным решением вопроса, поэтому вы получаете баллы. Основная причина, по которой я считаю, что у меня серый фон, заключается в том, что в нашем приложении на самом деле есть 4 клиентские области MDI, что на самом деле представляет собой еще больший беспорядок кода, чем я хотел бы подробно описать в этой теме. Спасибо за всю твою помощь! - person Jrud; 20.11.2009

Я не вижу ничего плохого в коде. Вот что вы можете попробовать:

Нарисуйте фоновое изображение на картинке размером с форму, а не на саму форму. Вы можете оставить кнопки перед ним, и он должен выглядеть так же. Затем вы можете управлять перерисовкой фонового изображения либо с помощью графического объекта в событии рисования, либо с помощью постоянного изображения с графическим блоком. Чтобы предотвратить мерцание, вы можете перерисовывать только ту часть изображения, которая находится в пределах границ кнопок (или измененной кнопки и тех, что справа от нее).

Это не идеально, но это может быть полезным решением.

person xpda    schedule 05.11.2009
comment
На самом деле это может быть жизнеспособным решением проблемы. Однако вы не можете сказать это по картинке, но серый фон здесь на самом деле является контейнером MDI, который будет содержать окна, запускаемые этими кнопками. Контейнер MDI также имеет события рисования и тому подобное, и я уже пробовал аннулировать определенные его разделы, перерисовывать части и тому подобное (и было непростой задачей выяснить, как изменить свойства в MDIClient, поскольку это не так. t фактический элемент управления формой) Так что в некотором смысле я как бы пробовал то, что вы говорите, насколько мог. Спасибо за идею! - person Jrud; 05.11.2009