前言: 上一个版本的 Winform 需要改成 WPF 来做界面, 第一次接触 WPF, 在转换过程中遇到的需求就是一个背景透明模糊, 一个是类似于 加载中...... 这样的等待窗口, 等后台执行完毕后再关掉. 在 Winform 中是通过一个类指定等待窗口的 parent 为调用者, 并指定 topmost 为最顶层来实现. 在 WPF 中这个方法不太灵光, 通过这几天的摸索, 找到一个 WPF 下的 UI 利器 --HandyControl(https://github.com/HandyOrg/HandyControl) 感谢作者分享. 通过它来实现一些界面的效果, 它里面带的有个顶部弹出对话框的功能 (带遮罩), 但这个不支持后台关闭 (作者说是可以调用带回调的模式, 但没有找到, 也没有明确说明是哪个). 所以我就单独从里面把这个功能提取出来, 实现了弹出提示框, 后台可以关闭的模式.
先看一下 HandyControl 提供的 Demo 中的这种对话框.
由于我需要的是弹出后, 后台会执行代码, 代码执行完后主动关闭对话框的操作. 于是我把里面的这块代码单独提取出来改造了一下, 实现效果如下.
这是在新接触 WPF 开发中, 学习到的, 如何让主窗体背景磨砂透明, 如何 Grid 背景透明模糊, 如何让 Grid 的控件不随 Grid 来模糊.
下面进入代码:
首先新建一个 WPF 项目, 然后通过 Nuget 引用 HandyControl.
在 App.xaml 中添加以下内容, 来引用 HandyControl 的样式效果.
- <Application.Resources>
- <ResourceDictionary>
- <ResourceDictionary.MergedDictionaries>
- <ResourceDictionary>
- <ResourceDictionary.MergedDictionaries>
- <ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml"/>
- <ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml"/>
- </ResourceDictionary.MergedDictionaries>
- </ResourceDictionary>
- <ResourceDictionary>
- <viewModel:ViewModelLocator x:Key="Locator" />
- </ResourceDictionary>
- </ResourceDictionary.MergedDictionaries>
- </ResourceDictionary>
- </Application.Resources>
添加一个类文件 BlurBehind.cs, 用来实现主窗体透明磨砂感.
- using System;
- using System.Runtime.InteropServices;
- namespace WpfApp1
- {
- /// <summary>
- /// 背景磨砂
- /// </summary>
- public class BlurBehind
- {
- internal enum AccentState
- {
- ACCENT_DISABLED = 1,
- ACCENT_ENABLE_GRADIENT = 0,
- ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
- ACCENT_ENABLE_BLURBEHIND = 3,
- ACCENT_INVALID_STATE = 4,
- ACCENT_ENABLE_ACRYLICBLURBEHIND = 5
- }
- [StructLayout(LayoutKind.Sequential)]
- internal struct AccentPolicy
- {
- public AccentState AccentState;
- public int AccentFlags;
- public int GradientColor;
- public int AnimationId;
- }
- [StructLayout(LayoutKind.Sequential)]
- internal struct WindowCompositionAttributeData
- {
- public WindowCompositionAttribute Attribute;
- public IntPtr Data;
- public int SizeOfData;
- }
- internal enum WindowCompositionAttribute
- {
- // ...
- WCA_ACCENT_POLICY = 19
- // ...
- }
- }
- }
然后新建两个目录: ViewModel 和 Images
在 Images 中放入一张图片, 并设置生成时自动复制
在 ViewModel 中新建三个类文件
DialogDemoViewModel.cs 用来实现弹出框
- using System;
- using GalaSoft.MvvmLight;
- using GalaSoft.MvvmLight.Command;
- using HandyControl.Controls;
- namespace WpfApp1.ViewModel
- {
- public class DialogDemoViewModel : ViewModelBase
- {
- private string _dialogResult;
- public string DialogResult
- {
- get => _dialogResult;
- #if netle40
- set => Set(nameof(DialogResult), ref _dialogResult, value);
- #else
- set => Set(ref _dialogResult, value);
- #endif
- }
- public RelayCommand<TextDialog> ShowTextCmd => new Lazy<RelayCommand<TextDialog>>(() =>
- new RelayCommand<TextDialog>(ShowText)).Value;
- private static void ShowText(TextDialog d)
- {
- Dialog.Show(d);
- // 获得句柄
- //var dialogShow = Dialog.Show(d);
- //var dialogShowHwnd = (HwndSource)PresentationSource.FromVisual(dialogShow);
- //if (dialogShowHwnd == null) return;
- //var hwnd = dialogShowHwnd.Handle;
- }
- }
- }
DialogInfo.cs 用来实现数据绑定给弹出框, 比如指定显示文字
- using System.Windows.Automation.Peers;
- using System.Windows.Automation.Provider;
- using System.ComponentModel;
- namespace WpfApp1.ViewModel
- {
- public class DialogInfo : INotifyPropertyChanged
- {
- public event PropertyChangedEventHandler PropertyChanged;
- public DialogInfo()
- {
- MyTxt = "加载中, 请稍后.";
- }
- private string myTxt;
- public string MyTxt
- {
- get => myTxt;
- set
- {
- myTxt = value;
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("MyTxt"));
- }
- }
- }
- }
ViewModelLocator.cs 用来实现构建弹出框实例
- using System;
- using System.Windows;
- using CommonServiceLocator;
- using GalaSoft.MvvmLight.IoC;
- namespace WpfApp1.ViewModel
- {
- public class ViewModelLocator
- {
- public ViewModelLocator()
- {
- ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
- SimpleIoc.Default.Register<DialogDemoViewModel>();
- }
- public static ViewModelLocator Instance => new Lazy<ViewModelLocator>(() =>
- Application.Current.TryFindResource("Locator") as ViewModelLocator).Value;
- #region Vm
- public DialogDemoViewModel DialogDemo => ServiceLocator.Current.GetInstance<DialogDemoViewModel>();
- #endregion
- }
- }
MainWindow.xaml 主窗体的内容
- <Windows
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- x:Class="WpfApp1.MainWindow"
- mc:Ignorable="d"
- Title="MainWindow" Height="450" Width="800"
- DataContext="{Binding DialogDemo,Source={StaticResource Locator}}"
- Loaded="MainWindow_OnLoaded"
- Background="#727A7A7A"
- AllowsTransparency="True"
- WindowStyle="None"
- MouseDown="MainWindow_OnMouseDown"
- >
- <Grid HorizontalAlignment="Left" Height="397" Margin="10,10,0,0" VerticalAlignment="Top" Width="790" ZIndex="0">
- <Grid Margin="0,10,10,97">
- <Grid.Background>
- <ImageBrush ImageSource="/WpfApp1;component/Images/wow_cataclysm_artwork-wallpaper-960x540.jpg"></ImageBrush>
- </Grid.Background>
- <Grid.Effect>
- <BlurEffect Radius="8"></BlurEffect>
- </Grid.Effect>
- </Grid>
- <Button x:Name="Btn_Show" Content="Button" HorizontalAlignment="Left" Margin="430,185,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click" />
- <TextBlock x:Name="txtBlock" HorizontalAlignment="Left" Margin="614,120,0,0" TextWrapping="Wrap" Text=""VerticalAlignment="Top"Height="120"Width="145"Foreground="White"/>
- </Grid>
- </Windows>
MainWindow.xaml.cs
- using System;
- using System.Linq;
- using System.Runtime.InteropServices;
- using System.Timers;
- using System.Windows;
- using System.Windows.Input;
- using System.Windows.Interop;
- using WpfApp1.ViewModel;
- namespace WpfApp1
- {
- /// <summary>
- /// MainWindow.xaml 的交互逻辑
- /// </summary>
- public partial class MainWindow
- {
- [DllImport("user32.dll")]
- private static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref BlurBehind.WindowCompositionAttributeData data);
- private uint _blurOpacity;
- public double BlurOpacity
- {
- get { return _blurOpacity; }
- set { _blurOpacity = (uint)value; EnableBlur(); }
- }
- private uint _blurBackgroundColor = 0x990000; /* BGR color format */
- public MainWindow()
- {
- InitializeComponent();
- }
- private void Button_Click(object sender, RoutedEventArgs e)
- {
- var newWindow = new TextDialog();
- var dialog = new DialogDemoViewModel();
- if (dialog.ShowTextCmd.CanExecute(newWindow))
- {
- dialog.ShowTextCmd.Execute(newWindow);
- }
- newWindow.info.MyTxt="加载中";
- //if (DataContext is DialogDemoViewModel MyVM && MyVM.ShowTextCmd.CanExecute(newWindow))
- // MyVM.ShowTextCmd.Execute(newWindow);
- var i = 0;
- var timer = new Timer(1000);
- timer.Elapsed+=delegate
- {
- Dispatcher.BeginInvoke(new Action(() =>
- {
- if (i <5)
- {
- txtBlock.Text +=$"{5 - i} 秒后关闭"+ Environment.NewLine;
- i++;
- }
- else
- {
- newWindow.CloseMe();
- }
- }));
- };
- timer.AutoReset = true;
- timer.Enabled = true;
- }
- /// <summary>
- /// 获取当前应用中处于激活的一个窗口
- /// </summary>
- /// <returns></returns>
- private static Windows GetActiveWindow() => Application.Current.Windows.OfType<Windows>().SingleOrDefault(x => x.IsActive);
- private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
- {
- EnableBlur();
- }
- private void EnableBlur()
- {
- var windowHelper = new WindowInteropHelper(this);
- var accent = new BlurBehind.AccentPolicy
- {
- AccentState = BlurBehind.AccentState.ACCENT_ENABLE_BLURBEHIND,
- //GradientColor = (int) ((_blurOpacity <<24) | (_blurBackgroundColor & 0xFFFFFF))
- };
- var accentStructSize = Marshal.SizeOf(accent);
- var accentPtr = Marshal.AllocHGlobal(accentStructSize);
- Marshal.StructureToPtr(accent, accentPtr, false);
- var data = new BlurBehind.WindowCompositionAttributeData
- {
- Attribute = BlurBehind.WindowCompositionAttribute.WCA_ACCENT_POLICY,
- SizeOfData = accentStructSize,
- Data = accentPtr
- };
- SetWindowCompositionAttribute(windowHelper.Handle, ref data);
- Marshal.FreeHGlobal(accentPtr);
- }
- private void MainWindow_OnMouseDown(object sender, MouseButtonEventArgs e)
- {
- if (e.ChangedButton == MouseButton.Left)
- DragMove();
- }
- }
- }
TextDialog.xaml 对话框
- <Border x:Class="WpfApp1.TextDialog"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:hc="https://handyorg.github.io/handycontrol"
- CornerRadius="10"
- Width="400"
- Height="247"
- Background="{DynamicResource RegionBrush}"
- >
- <hc:SimplePanel>
- <TextBlock x:Name="TextBlock" Style="{StaticResource TextBlockLargeBold}" Text="{Binding MyTxt,UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Margin="100,0,0,119" VerticalAlignment="Bottom" Height="68" Width="100"/>
- <Button x:Name="BtnClose" Width="22" Height="22" Command="hc:ControlCommands.Close" Style="{StaticResource ButtonIcon}" Foreground="{DynamicResource PrimaryBrush}" hc:IconElement.Geometry="{StaticResource ErrorGeometry}" Padding="0" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,4,4,0" Visibility="Hidden" />
- </hc:SimplePanel>
- </Border>
TextDialog.xaml.cs 新增了一个 CloseMe 用来后台调用关闭它
- using System.Windows.Automation.Peers;
- using System.Windows.Automation.Provider;
- using WpfApp1.ViewModel;
- namespace WpfApp1
- {
- /// <summary>
- /// TextDialog_.xaml 的交互逻辑
- /// </summary>
- public partial class TextDialog
- {
- public DialogInfo info = new DialogInfo { MyTxt = "加载中......" };
- public TextDialog()
- {
- DataContext = info;
- InitializeComponent();
- }
- public void CloseMe()
- {
- var bam = new ButtonAutomationPeer(BtnClose);
- var iip = bam.GetPattern(PatternInterface.Invoke) as IInvokeProvider;
- iip?.Invoke();
- }
- }
- }
来源: http://www.bubuko.com/infodetail-3106030.html