Подтвердить что ты не робот

Прозрачное управление Windows Forms

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

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

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

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

4b9b3361

Ответ 1

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

Да, вы, конечно, можете, но это требует немного усилий. Я бы рекомендовал следующий подход. Создайте форму верхнего уровня, которая не имеет границ или области заголовка, а затем дайте убедиться, что она не рисует фон в области клиентов, устанавливая 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 и продолжаете.

Ответ 2

Это действительно крутая идея - я, вероятно, буду ее использовать, поэтому спасибо. Во всяком случае, мое решение действительно просто... открыть новую 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();
}

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

Здесь моя очень-то-в-20-минутная демонстрация

Ответ 3

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

Ответ 4

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

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

Сбросьте свою собственную базу. Класс класса говорит о классе MyLightboxAwareForm, который будет прослушивать LightboxEvent из LightboxManager. Затем все ваши формы наследуются от MyLightboxAwareForm.

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

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

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

Ответ 5

Другое решение, которое не связано с использованием новой формы:

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

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

Скажем, у меня есть 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.

Ответ 6

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