Прозрачный элемент управления .NET Windows Forms

Я хочу смоделировать технику пользовательского интерфейса в стиле лайтбокса Web 2.0 в приложении Windows Forms. То есть, чтобы привлечь внимание к некоторому элементу управления переднего плана путем «затемнения» всего остального содержимого в клиентской области окна.

Очевидное решение - создать элемент управления, который представляет собой просто частично прозрачный прямоугольник, который можно прикрепить к клиентской области окна и перенести в начало Z-порядка. Он должен действовать как грязная стеклянная боль, сквозь которую все еще можно видеть другие элементы управления (и, следовательно, продолжать рисовать). Это возможно?

Я хорошо поохотился и попробовал несколько приемов сам, но пока безуспешно. Если это невозможно, что можно сделать по-другому?

См .: http://www.useit.com/alertbox/application-design.html (в разделе "Лайтбоксы", чтобы проиллюстрировать, что я имею в виду, снимок экрана приведен).


person Fake Jim    schedule 16.09.2008    source источник


Ответы (6)


Можете ли вы сделать это в .NET / C #?

Да, конечно, можете, но это требует немного усилий. Я бы порекомендовал следующий подход. Создайте форму верхнего уровня, у которой нет границы или области заголовка, а затем убедитесь, что она не рисует фон клиентской области, установив для TransparencyKey и BackColor одно и то же значение. Итак, теперь у вас есть окно, которое ничего не рисует ...

public class DarkenArea : Form
{
    public DarkenArea()
    {
        FormBorderStyle = FormBorderStyle.None;
        SizeGripStyle = SizeGripStyle.Hide;
        StartPosition = FormStartPosition.Manual;
        MaximizeBox = false;
        MinimizeBox = false;
        ShowInTaskbar = false;
        BackColor = Color.Magenta;
        TransparencyKey = Color.Magenta;
        Opacity = 0.5f;
    }
}

Создайте и разместите это окно DarkenArea над клиентской областью вашей формы. Затем вам нужно иметь возможность отображать окно без того, чтобы оно занимало фокус, и поэтому вам нужно будет вызвать платформу следующим образом, чтобы показать, не становясь активным ...

public void ShowWithoutActivate()
{
    // Show the window without activating it (i.e. do not take focus)
    PlatformInvoke.ShowWindow(this.Handle, (short)SW_SHOWNOACTIVATE);
}

Вам нужно сделать так, чтобы он действительно что-то рисовал, но исключил рисование в той области элемента управления, которую вы хотите оставить выделенной. Так что переопределите обработчик OnPaint и нарисуйте черным / синим цветом или чем угодно, но исключая область, которую вы хотите оставить яркой ...

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
    // Do your painting here be exclude the area you want to be brighter
}

Наконец, вам нужно переопределить WndProc, чтобы предотвратить взаимодействие мыши с окном, если пользователь пытается что-то сумасшедшее, например, щелкнуть затемненную область. Что-то вроде этого...

protected override void WndProc(ref Message m)
{
    if (m.Msg == (int)WM_NCHITTEST)
        m.Result = (IntPtr)HTTRANSPARENT;
    else
        base.WndProc(ref m);
}

Этого должно быть достаточно, чтобы получить желаемый эффект. Когда вы будете готовы отменить эффект, вы избавляетесь от экземпляра DarkenArea и продолжаете.

person Phil Wright    schedule 16.09.2008

Это действительно классная идея - я, наверное, ей воспользуюсь, так что спасибо. В любом случае, мое решение действительно простое ... откройте новую 50% непрозрачную форму поверх текущей, а затем нарисуйте фоновое изображение для этой формы с прямоугольником, соответствующим границам элемента управления, который вы хотите выделить, заполненным цветом ключ прозрачности.

В моем приблизительном примере я назвал эту форму LBform, и суть ее в следующем:

public Rectangle ControlBounds { get; set; }
private void LBform_Load(object sender, EventArgs e)
{
    Bitmap background = new Bitmap(this.Width, this.Height);
    Graphics g = Graphics.FromImage(background);
    g.FillRectangle(Brushes.Fuchsia, this.ControlBounds);

    g.Flush();

    this.BackgroundImage = background;
    this.Invalidate();
}

Color.Fuchia - это TransparencyKey для этой полупрозрачной формы, поэтому вы сможете видеть сквозь нарисованный прямоугольник и взаимодействовать с чем-либо в пределах его границ основной формы.

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

private void button1_Click(object sender, EventArgs e)
{
    // setup user control:
    UserControl1 uc1 = new UserControl1();
    uc1.Left = (this.Width - uc1.Width) / 2;
    uc1.Top = (this.Height - uc1.Height) / 2;
    this.Controls.Add(uc1);
    uc1.BringToFront();

    // load the lightbox form:
    LBform lbform = new LBform();
    lbform.SetBounds(this.Left + 8, this.Top + 30, this.ClientRectangle.Width, this.ClientRectangle.Height);
    lbform.ControlBounds = uc1.Bounds;

    lbform.Owner = this;
    lbform.Show();
}

Это действительно базовые вещи, которые вы можете делать по-своему, если хотите, но это просто добавление пользовательского элемента управления, затем установка формы лайтбокса поверх основной формы и установка свойства bounds для отображения полной прозрачности в нужном месте. Такие вещи, как перетаскивание и закрытие формы лайтбокса и UserControl, не обрабатываются в моем быстром примере. Да, и не забудьте избавиться от экземпляра Graphics - я тоже не учел (уже поздно, я очень устал).

Вот моя очень полезная демонстрация за 20 минут

person ZeroBugBounce    schedule 04.10.2008
comment
В итоге я получил аналогичное решение, за исключением того, что моя форма LightBox (полупрозрачная, которая отображается модально над основной формой) передается UserControl в своем конструкторе, который затем показывает себя модально - так что вы получаете стек из двух модальных окна. Это соответствовало моим требованиям. - person Fake Jim; 06.10.2008

Большой затемняющий прямоугольник - это хорошо.

Другой способ сделать это:

Roll your own base Form class говорит, что MyLightboxAwareForm класс будет слушать LightboxEvent от LightboxManager. Затем все ваши формы унаследованы от MyLightboxAwareForm.

При вызове метода Show на любом Lightbox, LightboxManager транслирует событие LightboxShown на все экземпляры MyLightboxAwareForm и заставляет их затемняться.

Это имеет то преимущество, что обычные функции форм Win32 будут продолжать работать, например, мигание формы на панели задач при нажатии на один из ее модальных диалоговых окон или управление событиями наведения указателя мыши / mousedown будет по-прежнему работать нормально и т. Д.

И если вам нужен стиль затемнения прямоугольника, вы можете просто поместить логику в MyLightboxAwareForm

person chakrit    schedule 16.09.2008

Сами формы обладают свойством Opacity, которое было бы идеальным, но я не думаю, что большинство отдельных элементов управления им обладают. Вам придется нарисовать его владельцем.

person Joel Coehoorn    schedule 16.09.2008

Другое решение, не использующее новую форму:

  • сделайте картинку вашего контейнера (форма / панель / что угодно),
  • изменить его непрозрачность,
  • отобразить его на фоне новой панели.
  • Заполните контейнер этой панелью.

А теперь код ...

Скажем, у меня есть UserControl под названием Frame, к которому я хочу применить свой эффект лайтбокса:

public partial class Frame : UserControl
{
    private Panel shadow = new Panel();
    private static float LIGHTBOX_OPACITY = 0.3f;

    public Frame()
    {
        InitializeComponent(); 
        shadow.Dock = DockStyle.Fill;
    }

    public void ShowLightbox()
    {
        Bitmap bmp = new Bitmap(this.Width, this.Height);
        this.pnlContainer.DrawToBitmap(bmp, new Rectangle(0, 0, this.Width, this.Height));
        shadow.BackgroundImage = SetImgOpacity(bmp, LIGHTBOX_OPACITY );
        this.Controls.Add(shadow);
        shadow.BringToFront();
    }

    // http://www.geekpedia.com/code110_Set-Image-Opacity-Using-Csharp.html
    private Image SetImgOpacity(Image imgPic, float imgOpac)
    {
        Bitmap bmpPic = new Bitmap(imgPic.Width, imgPic.Height);
        Graphics gfxPic = Graphics.FromImage(bmpPic);
        ColorMatrix cmxPic = new ColorMatrix();
        cmxPic.Matrix33 = imgOpac;
        ImageAttributes iaPic = new ImageAttributes();
        iaPic.SetColorMatrix(cmxPic, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
        gfxPic.DrawImage(imgPic, new Rectangle(0, 0, bmpPic.Width, bmpPic.Height), 0, 0, imgPic.Width, imgPic.Height, GraphicsUnit.Pixel, iaPic);
        gfxPic.Dispose();
        return bmpPic;
    }
}

Преимущества использования этой техники:

  • Вам не придется обрабатывать все события мыши
  • Вам не придется управлять несколькими формами, чтобы взаимодействовать с элементами лайтбокса.
  • Нет переопределения WndProc
  • Вы будете круты, потому что вы единственный, кто не использует формы для достижения этого эффекта.

Недостатки в основном заключаются в том, что этот метод намного медленнее, потому что вам нужно обработать все изображение, чтобы исправить каждую точку пикселя, используя ColorMatrix.

person koni    schedule 22.04.2011

Каждая форма имеет свойство «Непрозрачность». Установите его на 50% (или 0,5 из кода), чтобы он был полупрозрачным. Удалите границы и покажите его развернутым перед формой, на которой вы хотите сфокусироваться. Вы можете изменить BackColor формы или даже установить фоновое изображение для различных эффектов.

person Miroslav Zadravec    schedule 16.09.2008