【问题标题】:Night Vision Mode on WPF WindowsWPF Windows 上的夜视模式
【发布时间】:2015-12-31 21:44:49
【问题描述】:

我们制作了一个带有传统 UI(常见控件,如选项卡、按钮、标签、文本框等)的 WPF 应用程序。

我们需要添加一个“夜视”模式,让它看起来像 Stellarium 的夜视模式,这样它就可以在光线很少或根本没有光线的地方舒适地使用。

据我所知,我们只有两个选择:

  1. 一种称为“着色”的技术(我不知道如何在 WPF 中实现它)。
  2. 蛮力方式:定义控件的样式模板。如您所知,这将意味着一项巨大的工作,因为我们需要为正在使用的每个控件(边框、背景、画笔等)重新定义每个属性。

问题是:

  1. 在 WPF 中实现这一目标的最佳方法是什么?
  2. 如果我们使用 MahApps Metro Style 会更复杂吗?我知道 MahApps 可以让你自定义他们的风格,但问题是切换到不同的模板(这两个分别用于日间和夜视)。

非常感谢!

【问题讨论】:

    标签: c# wpf vision


    【解决方案1】:

    一个建议当然是MahappsThemes。 您可以通过以下方式在明暗之间切换:

    // get the theme from the current application
    var theme = ThemeManager.DetectAppStyle(Application.Current);
    
    // now set the Green accent and dark theme
    ThemeManager.ChangeAppStyle(Application.Current,
                                ThemeManager.GetAccent("Green"),
                                ThemeManager.GetAppTheme("BaseDark"));
    

    (Mahapps.Styles for ref)

    或者您可以为每个画笔使用 DynamicResource 并更改 SINGLE ResourceDictionary 持有它们以通过单击更改所有内容:)

    【讨论】:

    • 谢谢!我会试试看。
    • 另一个基于您的回答的问题:如果我们打算包含一个滑块控件以便能够设置夜视颜色的“亮度”怎么办?使用 MahApps Metro 风格选项会很容易做到吗?
    • 不,不会的。如果您只是想“调暗”显示屏,例如“屏幕过滤器”在我们手机上的工作方式,您可以在“HitTestVisibility”设置为“假”和Background黑色的所有内容上创建一个“画布”,并控制它的Opacity :)
    • 我会的。再次感谢:)
    【解决方案2】:

    或者,考虑仅使用 DWM 放大功能使所有窗口进入“夜间模式”。

    示例:

    查看Magnification API 示例,修改如下:

    /*************************************************************************************************
    *
    * File: FullscreenMagnifierSample.cpp
    *
    * Description: Implements simple UI to control fullscreen magnification, using the 
    * Magnification API.
    *
    *  Copyright (C) Microsoft Corporation.  All rights reserved.
    * 
    * This source code is intended only as a supplement to Microsoft
    * Development Tools and/or on-line documentation.  See these other
    * materials for detailed information regarding Microsoft code samples.
    * 
    * THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
    * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
    * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
    * PARTICULAR PURPOSE.
    * 
    *************************************************************************************************/
    
    #include "windows.h"
    #include "resource.h"
    #include "strsafe.h"
    #include "magnification.h"
    
    // Global variables and strings.
    const LPWSTR g_pszAppTitle = L"Night Colors";
    
    // Initialize color effect matrices to apply grayscale or restore the colors on the desktop.
    MAGCOLOREFFECT g_MagEffectIdentity = { 
        1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
        0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
        0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
        0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
        0.0f, 0.0f, 0.0f, 0.0f, 1.0f };
    MAGCOLOREFFECT g_MagEffectWashout = {
        0.5f, 0.0f, 0.0f, 0.0f, 0.0f,
        0.0f, 0.3f, 0.0f, 0.0f, 0.0f,
        0.0f, 0.0f, 0.3f, 0.0f, 0.0f,
        0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
        0.0f, 0.0f, 0.0f, 0.0f, 1.0f };
    
    MAGCOLOREFFECT g_MagEffectWashout2 = {
        0.35f, 0.0f, 0.0f, 0.0f, 0.0f,
        0.0f, 0.2f, 0.0f, 0.0f, 0.0f,
        0.0f, 0.0f, 0.2f, 0.0f, 0.0f,
        0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
        0.0f, 0.0f, 0.0f, 0.0f, 1.0f };
    
    MAGCOLOREFFECT g_MagEffectWashout3 = {
        0.25f, 0.0f, 0.0f, 0.0f, 0.0f,
        0.0f, 0.13f, 0.0f, 0.0f, 0.0f,
        0.0f, 0.0f, 0.13f, 0.0f, 0.0f,
        0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
        0.0f, 0.0f, 0.0f, 0.0f, 1.0f };
    
    /*MAGCOLOREFFECT g_MagEffectGrayscale = {0.3f,  0.3f,  0.3f,  0.0f,  0.0f,
                                           0.6f,  0.6f,  0.6f,  0.0f,  0.0f,
                                           0.1f,  0.1f,  0.1f,  0.0f,  0.0f,
                                           0.0f,  0.0f,  0.0f,  1.0f,  0.0f,
                                           0.0f,  0.0f,  0.0f,  0.0f,  1.0f};*/
    
    MAGCOLOREFFECT g_MagEffectGrayscaleInverted = { 
        -0.3f, 0.0f, 0.0f, 0.0f, 0.0f,
        -0.6f, 0.0f, 0.0f, 0.0f, 0.0f,
        -0.1f, 0.0f, 0.0f, 0.0f, 0.0f,
        0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
        1.0f, 0.0f, 0.0f, 0.0f, 1.0f };
    
    MAGCOLOREFFECT g_MagEffectInverted = { 
        -1.0f,  0.0f,  0.0f, 0.0f, 0.0f,
         0.0f, -1.0f,  0.0f, 0.0f, 0.0f,
         0.0f,  0.0f, -1.0f, 0.0f, 0.0f,
         0.0f,  0.0f,  0.0f, 1.0f, 0.0f,
         1.0f,  1.0f,  1.0f, 0.0f, 1.0f 
    };
    
    MAGCOLOREFFECT g_MagEffectGrayscale = {0.3f,  0.0f,  0.0f,  0.0f,  0.0f,
                                           0.6f,  0.0f,  0.0f,  0.0f,  0.0f,
                                           0.1f,  0.0f,  0.0f,  0.0f,  0.0f,
                                           0.0f,  0.0f,  0.0f,  1.0f,  0.0f,
                                           0.0f,  0.0f,  0.0f,  0.0f,  1.0f};
    
    // Forward declarations.
    INT_PTR CALLBACK SampleDialogProc(_In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam);
    void InitDlg(_In_ HWND hwndDlg);
    void HandleCommand(_In_ HWND hwndDlg, _In_ WORD wCtrlId);
    
    //
    // FUNCTION: WinMain()
    //
    // PURPOSE: Entry point for the application.
    //
    int APIENTRY WinMain(_In_ HINSTANCE hInstance,
                         _In_opt_ HINSTANCE /*hPrevInstance*/,
                         _In_ LPSTR     /*lpCmdLine*/,
                         _In_ int       /*nCmdShow*/)
    {
        // Initialize the magnification functionality for this process.
        if (MagInitialize())
        {
            // Present a dialog box to allow the user to control fullscreen magnification.
            DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG_FULLSCREENMAGNIFICATIONCONTROL), NULL, SampleDialogProc);
    
            // Any current magnification and color effects will be turned off as a result of calling MagUninitialize().
            MagUninitialize();
        }
        else
        {
            MessageBox(NULL, L"Failed to initialize magnification.", g_pszAppTitle, MB_OK);
        }
    
        return 0;
    }
    
    //
    // FUNCTION: SampleDialogProc()
    //
    // PURPOSE: Dialog proc for the UI used for controlling fullscreen magnification.
    //
    INT_PTR CALLBACK SampleDialogProc(
      _In_  HWND   hwndDlg,
      _In_  UINT   uMsg,
      _In_  WPARAM wParam,
      _In_  LPARAM /*lParam*/
    )
    {
        INT_PTR ipRet = 0;
    
        switch (uMsg)
        {
        case WM_INITDIALOG:
    
            InitDlg(hwndDlg);
    
            ipRet = 0;
    
            break;
    
        case WM_COMMAND:
    
            if(HIWORD(wParam) == BN_CLICKED)
            {
                WORD wCtrlId = LOWORD(wParam);
    
                HandleCommand(hwndDlg, wCtrlId);
    
                ipRet = 1;
            }
    
            break;
    
        case WM_CLOSE:
    
            EndDialog(hwndDlg, 0);
    
            break;
        }
    
        return ipRet;
    }
    
    //
    // FUNCTION: InitDlg()
    //
    // PURPOSE: Initialize the sample dialog box's position and controls.
    //
    void InitDlg(_In_ HWND hwndDlg)
    {
        // Position the dialog box in the center of the primary monitor.
        RECT rcDlg;
        GetWindowRect(hwndDlg, &rcDlg);
    
        int xDlg = (GetSystemMetrics(SM_CXSCREEN) - (rcDlg.right - rcDlg.left)) / 2;
        int yDlg = (GetSystemMetrics(SM_CYSCREEN) - (rcDlg.bottom - rcDlg.top)) / 2;
    
        SetWindowPos(hwndDlg, NULL, xDlg, yDlg, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
    
        // Set the default color and invalidate the status
        DWORD defaultColor = IDC_CHECK_SETRED_INVERTED;
        HWND hwndControl = GetDlgItem(hwndDlg, defaultColor);
        SendMessage(hwndControl, BM_SETCHECK, BST_CHECKED, 0);
        HandleCommand(hwndDlg, defaultColor);
        SetFocus(hwndControl);
    }        
    
    //
    // FUNCTION: HandleCommand()
    //
    // PURPOSE: Take action in response to user action at the dialog box's controls.
    //
    void HandleCommand(_In_ HWND hwndDlg, _In_ WORD wCtrlId)
    {
        switch (wCtrlId)
        {
            case IDC_CHECK_SETRED:
                {
                    if (IsDlgButtonChecked(hwndDlg, IDC_CHECK_SETRED))
                    {
                        MagSetFullscreenColorEffect(&g_MagEffectGrayscale);
                    }
                }
                break;
            case IDC_CHECK_SETRED_INVERTED:
                {
                    if (IsDlgButtonChecked(hwndDlg, IDC_CHECK_SETRED_INVERTED))
                    {
                        MagSetFullscreenColorEffect(&g_MagEffectGrayscaleInverted);
                    }
                }
                break;
            case IDC_CHECK_SET_INVERTED:
                {
                    if (IsDlgButtonChecked(hwndDlg, IDC_CHECK_SET_INVERTED))
                    {
                        MagSetFullscreenColorEffect(&g_MagEffectInverted);
                    }
                }
                break;
            case IDC_CHECK_SET_WASHOUT:
            {
                if (IsDlgButtonChecked(hwndDlg, IDC_CHECK_SET_WASHOUT))
                {
                    MagSetFullscreenColorEffect(&g_MagEffectWashout);
                }
            }
            break;
            case IDC_CHECK_SET_WASHOUT2:
            {
                if (IsDlgButtonChecked(hwndDlg, IDC_CHECK_SET_WASHOUT2))
                {
                    MagSetFullscreenColorEffect(&g_MagEffectWashout2);
                }
            }
            break;
            case IDC_CHECK_SET_WASHOUT3:
            {
                if (IsDlgButtonChecked(hwndDlg, IDC_CHECK_SET_WASHOUT3))
                {
                    MagSetFullscreenColorEffect(&g_MagEffectWashout3);
                }
            }
            break;
        }
    }
    

    特定窗口

    您也可以通过compiling a HLSL shader 逐个窗口执行此操作,并将其设置为对窗口的效果。如果您需要vary the intensity,此技术也适用。

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
    
            this.img.Source = new BitmapImage(new Uri("https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png"));
            this.Effect = new ColorComplementEffect();
        }
    }
    
    public class ColorComplementEffect : ShaderEffect
    {
        public ColorComplementEffect()
        {
            PixelShader = _shader;
            UpdateShaderValue(InputProperty);
        }
    
        public Brush Input
        {
            get { return (Brush)GetValue(InputProperty); }
            set { SetValue(InputProperty, value); }
        }
    
        public static readonly DependencyProperty InputProperty = ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(ColorComplementEffect), 0);
        private static PixelShader _shader = new PixelShader() { UriSource = new Uri(@"cc.ps") };
    }
    

    着色器:

    sampler2D implicitInput : register(s0);
    
    float4 main(float2 uv : TEXCOORD) : COLOR
    {
        float4 color = tex2D(implicitInput, uv);
    
        float4 complement;
        complement.r = (color.a - color.r) * 0.2;
        complement.g = (color.a - color.g) * 0.2;
        complement.b = (color.a - color.b) * 0.2;
        complement.a = color.a;
    
        return complement;
    }
    

    【讨论】:

    • 有趣!感谢您的回复。但应用程序的目标用户只是希望应用程序窗口本身具有该模式,而不是将整个东西设置为夜视模式。
    • @Daniel,更新为包含仅影响单个窗口的示例。
    • 嘿,非常感谢!我试试看。
    猜你喜欢
    • 2013-12-30
    • 2017-09-06
    • 1970-01-01
    • 1970-01-01
    • 2022-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-03
    相关资源
    最近更新 更多