Dropshadow в стиле Windows 7 в бескрайнем виде - PullRequest
13 голосов
/ 09 января 2012

Короткая версия:

Цель: Глубокая, темная тень для Windows 7 в WinForm без полей в C #


Известные существующие решения 1: Простые тени в стиле XP с использованием CreateParams.

Проблема: Слишком слабый, слишком легкий, слишком уродливый.


Известные существующие решения 2: Заменить GDI формы на растровое изображение.

Проблема: Потерять возможность использовать элементы управления, работающие только в качестве заставки.


Цель этого поста: Найти медианное решение этой проблемы или все вместе лучшее.

. , .

Длинная версия:

(Редактировать: я имею в виду падающую тень, идущую вдоль границы любой формы окна, если это не было ясно.) Я понимаю, что есть способ сделать тени в стиле XP в C #, используя:

C # Code 1 - Простые тени в стиле XP (проблема: светить, слабеть, безобразно)

// Define the CS_DROPSHADOW constant
private const int CS_DROPSHADOW = 0x00020000;

// Override the CreateParams property
protected override CreateParams CreateParams
{
    get
    {
        CreateParams cp = base.CreateParams;
        cp.ClassStyle |= CS_DROPSHADOW;
        return cp;
    }
}

Однако я пытаюсь выяснить, как заставить их выглядеть, как в Windows 7 (более глубокие и большие тени), и не могу найти лучший способ сделать это.

У меня есть метод, созданный сейчас, который позволит мне переопределить весь GDI формы и выглядеть как заставка (кредит не мой):

C # Код 2: Заменить GDI формы на Bitmap (Проблема: невозможно использовать элементы управления формы, сложно поддерживать графический интерфейс)

    public void SetBitmap(Bitmap bitmap, byte opacity)
    {
        if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
            throw new ApplicationException("The bitmap must be 32ppp with alpha-channel.");

        // 1. Create a compatible DC with screen;
        // 2. Select the bitmap with 32bpp with alpha-channel in the compatible DC;
        // 3. Call the UpdateLayeredWindow.

        IntPtr screenDc = Win32.GetDC(IntPtr.Zero);
        IntPtr memDc = Win32.CreateCompatibleDC(screenDc);
        IntPtr hBitmap = IntPtr.Zero;
        IntPtr oldBitmap = IntPtr.Zero;

        try
        {
            hBitmap = bitmap.GetHbitmap(Color.FromArgb(0));  // grab a GDI handle from this GDI+ bitmap
            oldBitmap = Win32.SelectObject(memDc, hBitmap);

            Win32.Size size = new Win32.Size(bitmap.Width, bitmap.Height);
            Win32.Point pointSource = new Win32.Point(0, 0);
            Win32.Point topPos = new Win32.Point(Left, Top);
            Win32.BLENDFUNCTION blend = new Win32.BLENDFUNCTION();
            blend.BlendOp = Win32.AC_SRC_OVER;
            blend.BlendFlags = 0;
            blend.SourceConstantAlpha = opacity;
            blend.AlphaFormat = Win32.AC_SRC_ALPHA;

            Win32.UpdateLayeredWindow(this.Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, 0, ref blend, Win32.ULW_ALPHA);
        }
        finally
        {
            Win32.ReleaseDC(IntPtr.Zero, screenDc);
            if (hBitmap != IntPtr.Zero)
            {
                Win32.SelectObject(memDc, oldBitmap);
                Win32.DeleteObject(hBitmap);
            }
            Win32.DeleteDC(memDc);
        }
    }


    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams cp = base.CreateParams;
            cp.ExStyle |= 0x00080000; // This form has to have the WS_EX_LAYERED extended style
            return cp;
        }
    }

Тем не менее, это дает мне полный 32-битный фон (так как мне требуется добавить тени), но я теряю возможность создавать видимые элементы формы.

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

Ответы [ 2 ]

8 голосов
/ 10 января 2012

Хорошо, после примерно 4 часов мозгового штурма и кодирования я наконец-то разработал решение.В основном я сделал 2 формы.

Форма № 1 : создайте тени, изменив и объединив 8 изображений (4 угловых градиента + 4 линейных градиента для каждого направления) и установите их в качестве фона, используя второй код, который я разместил выше( C # Code 2: Заменить GDI формы на Bitmap ).Код в значительной степени объясняет это.

public partial class Dropshadow : Form
{

    public Dropshadow(Form parentForm)
    {
        /*This bit of code makes the form click-through. 
          So you can click forms that are below it in z-space */
        int wl = GetWindowLong(this.Handle, -20);
        wl = wl | 0x80000 | 0x20;
        SetWindowLong(this.Handle, -20, wl);

        InitializeComponent();

        //Makes the start location the same as parent.
        this.StartPosition = parentForm.StartPosition;

        parentForm.Activated += ParentForm_Activated; //Fires on parent activation to do a this.BringToFront() 
        this.Deactivate += This_Deactivated; //Toggles a boolean that ensures that ParentForm_Activated does fire when clicking through (this)
        parentForm.Closed += ParentForm_Closed; //Closes this when parent closes
        parentForm.Move += ParentForm_Move; //Follows movement of parent form

        //Draws border with standard bitmap modifications and merging
        /* Omitted function to avoid extra confusion */
        Bitmap getShadow = DrawBlurBorder(parentForm.ClientSize.Width, parentForm.ClientSize.Height);
        /* **This code was featured in the original post:** */
        SetBitmap(getShadow, 255); //Sets background as 32-bit image with full alpha.

        this.Location = Offset; //Set within DrawBlurBorder creates an offset 

    }
    private void ParentForm_Activated(object o, EventArgs e)
    {
        /* Sets this form on top when parent form is activated.*/
        if (isBringingToFront)
        { 
            /*Hopefully prevents recusion*/
            isBringingToFront = false;
            return;
        }

        this.BringToFront();


        /* Some special tweaks omitted to avoid confusion */
    }
    private void This_Deactivated(object o, EventArgs e)
    {
        /* Prevents recusion. */
        isBringingToFront = true;
    }
    /* Closes this when parent form closes. */
    private void ParentForm_Closed(object o, EventArgs e)
    {
        this.Close();
    }
    /* Adjust position when parent moves. */
    private void ParentForm_Move(object o, EventArgs e)
    {
        if(o is Form)
            this.Location = new Point((o as Form).Location.X + Offset.X, (o as Form).Location.Y + Offset.Y);
    }
 }

Форма # 2 : Это просто запускает форму drophadow при запуске, и я также создал несколько интерфейсов, чтобы позволить дальнейшую интеграцию и гибкость, которые я пропустил, чтобы избежатьдополнительная путаницаВ основном методы, гарантирующие, что форма Dropshadow не убирает щелчки мыши из активной формы и не заставляет пользователя дважды нажимать кнопку, если форма Dropshadow была сверху.

4 голосов
/ 28 декабря 2013

Спасибо, Корилулу.

Рабочий класс здесь .

var f = new Dropshadow(this)
{
    BorderRadius = 40,
    ShadowColor = Color.Blue
};

f.RefreshShadow();

DEMO

DrawShadow создает тень, похожую на растровое изображение, но еще не идеально. Этот класс не совершенен, но он работает.

Кстати, я не знаю, как скрыть теневую форму на панели задач. Установите ShowInTaskBar = false, если форма исчезнет.

EDIT

Я переписываю класс, теперь он выглядит как настоящий DropShadow.

Источник здесь .

Одна вещь, которую вы должны знать, - этот класс не учитывает border-radius (принимает форму css).

Основное свойство

  • ShadowColor
  • ShadowV
  • ShadowH
  • ShadowSpread
  • ShadowBlur

Свойство совпадает с css box-shadow, см. здесь

Эти владения

  • ShadowSpread
  • ShadowBlur
  • ShadowColor

требуется ручной вызов RefreshShadow().

Перейти на демонстрационный проект

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