.NET Windows Forms Прозрачное управление - PullRequest
15 голосов
/ 16 сентября 2008

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

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

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

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

Ответы [ 6 ]

14 голосов
/ 16 сентября 2008

Можете ли вы сделать это в .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 и продолжаете.

8 голосов
/ 04 октября 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();
}

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

Вот моя демонстрация "Сделай это за 20 минут"

1 голос
/ 22 апреля 2011

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

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

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

Допустим, у меня есть 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.

1 голос
/ 16 сентября 2008

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

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

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

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

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

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

1 голос
/ 16 сентября 2008

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

0 голосов
/ 16 сентября 2008

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...