Как создать метель на рабочем столе Windows? - PullRequest
11 голосов
/ 11 января 2011

Помимо практического использования, как (если это вообще возможно) вы могли бы создать эффект «снега» на настольном компьютере под управлением Windows?Желательно только с чистым C / C ++ и WinAPI.

Требования к снегу:

  • Появляется поверх всего остального показанного (Примечание: окна всегда наверху могут появлятьсявершина снега все еще, это нормально. Я понимаю, что для любого приложения не может быть никакого флага "абсолютно сверху")
  • Снежинки маленькие, возможно простые точки или скопления из нескольких белых пикселей;
  • Не мешает работе с компьютером (щелчок по снежинке отправляет щелчок в основное окно);
  • Прекрасно играет с пользователями, перетаскивающими окна;
  • Поддержка нескольких мониторов.

Бонусные баллы за любую из следующих функций:

  • Снег накапливается в нижней части окна или на панели задач (если он находится внизу экрана);
  • Снег накапливается и на окнах верхнего уровня.Или, возможно, некоторые снег накапливается, некоторые продолжают падать, накапливаясь в каждом окне с заголовком;
  • Снег, накопленный на окнах, "стряхивается" при перетаскивании окон;
  • Снег, накопленный на панели задач, знает о расширенной кнопке «Пуск» в Vista / 7.
  • Снежинки имеют тени / очертания, поэтому они видны на белом фоне;
  • Снежинки имеют сложную снежно-подобную форму.похожие фигуры (они все еще должны быть крошечными).
  • Нажатие на снежинку действительно посылает щелчок в основное окно, но снежинка испаряется с небольшой крутой анимацией;

БольшинствоЭти эффекты достаточно просты, за исключением той части, где снег проскальзывает и хорошо играет при перетаскивании окон.В ранние годы я делал реализацию, которая рисует на HDC, который вы получаете из GetDesktopWindow(), который проходил по кликам, но имел проблемы с перетаскиванием окон пользователями (визуализированные на них снежинки «перетаскивались»).

В решении могут использоваться функции Vista / 7 Aero, но, конечно, предпочтительнее универсальное решение.Есть идеи?

1 Ответ

6 голосов
/ 11 января 2011

Ради краткости и простоты этот ответ был сокращен до ограниченного набора требований.Это просто, чтобы расширить это и сделать его более надежным.

Этот ответ использует WPF в Windows XP.Он должен работать на двух мониторах и на других системах Windows.

Он начинается с простого окна:

<Window x:Class="TestDump.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" WindowStartupLocation="Manual" Loaded="Window_Loaded"
WindowStyle="None" AllowsTransparency="True" Background="Transparent"
>
    <Grid x:Name="FieldOfSnow"/>
</Window>

К этому окну мы добавим снежинки, определенныеследующим образом:

<UserControl x:Class="TestDump.SnowFlake"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="5" Width="5">
    <Border Background="White" CornerRadius="2" BorderThickness="1" BorderBrush="LightGray"/>
</UserControl>

У снежинок есть код по умолчанию UserControl без изменений.

Наконец, WindowBehind

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Runtime.InteropServices;
using System.Windows.Interop;

namespace TestDump
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        private TimeSpan _lastRender;

        public Window1()
        {
            InitializeComponent();
            _lastRender = TimeSpan.FromTicks(0);
            CompositionTarget.Rendering += SnowflakeTick;
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            this.Topmost = true;
            this.Top = 0;
            this.Left = 0;

            this.Width = System.Windows.SystemParameters.PrimaryScreenWidth;
            this.Height = System.Windows.SystemParameters.PrimaryScreenHeight;

            if (System.Windows.Forms.SystemInformation.MonitorCount == 2)
            {
                System.Drawing.Rectangle SecondScreenArea = System.Windows.Forms.Screen.AllScreens[1].Bounds;

                this.Width += SecondScreenArea.Width;
                this.Height = this.Height > SecondScreenArea.Height ? this.Height : SecondScreenArea.Height;
            }
        }

        public const int WS_EX_TRANSPARENT = 0x00000020;
        public const int GWL_EXSTYLE = (-20);

        [DllImport("user32.dll")]
        public static extern int GetWindowLong(IntPtr hwnd, int index);

        [DllImport("user32.dll")]
        public static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);

        protected override void OnSourceInitialized(EventArgs e)
        {
            base.OnSourceInitialized(e);

            // Get this window's handle
            IntPtr hwnd = new WindowInteropHelper(this).Handle;

            // Change the extended window style to include WS_EX_TRANSPARENT
            int extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
            SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_TRANSPARENT);
        }

        List<TranslateTransform> Flakes = new List<TranslateTransform>();
        Random rand = new Random();

        private void SnowflakeTick(object sender, EventArgs e)
        {
            RenderingEventArgs renderingArgs = (RenderingEventArgs)e;
            TimeSpan dTime = (renderingArgs.RenderingTime - _lastRender);
            double deltaTime = dTime.TotalMilliseconds;
            _lastRender = renderingArgs.RenderingTime;

            if ( _lastRender.Milliseconds < deltaTime)
            {
                TranslateTransform SnowPos = new TranslateTransform(this.Width * rand.Next(1000) / 1000.0 - this.Width/2, -this.Height/2);

                SnowFlake sf = new SnowFlake();
                sf.RenderTransform = SnowPos;

                // The flakes are centered when added, so all positions must be translated with this in mind.
                FieldOfSnow.Children.Add(sf);
                Flakes.Add(SnowPos);
            }

            foreach (TranslateTransform Flake in Flakes)
            {
                double ScreenHeight = this.Height / 2 - 2;

                if (Flake.Y < ScreenHeight)
                {
                    Flake.Y += deltaTime / 50;
                }
            }
        }
    }
}

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

Я не очень много тестировал, но он работает на моей системе, и снег уходиткогда закончите, внизу экрана.

Я использовал этот справочник по поведению сквозного клика.

Более преданный парень, чем я должен быть в состоянии приспособиться this , плюс некоторое обнаружение краев для задачи заставить снег сидеть где-то в другом месте.

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

Веселись!

...