WinUI3中实现点击ContentDialog外部区域关闭对话框的问题
WinUI3 中实现点击 ContentDialog 外部空白区域关闭的方案
在WinUI3中,Microsoft.UI.Xaml.Window.Current.CoreWindow为空是因为WinUI3的窗口模型与UWP不同,无法直接依赖Window.Current获取CoreWindow。可以通过ContentDialog的XamlRoot关联对应的CoreWindow,实现点击外部关闭的功能。
修改后的核心实现逻辑
替换原InitializeObserving方法,并新增点击区域判断逻辑:
private void InitializeObserving() { Opened += (s, e) => { isHide = false; // 通过XamlRoot获取当前关联窗口的CoreWindow var coreWindow = XamlRoot.ContentIslandEnvironment.CoreWindow; coreWindow.PointerPressed += CoreWindow_PointerPressed; }; Closed += (s, e) => { isHide = true; // 移除事件监听,避免内存泄漏 var coreWindow = XamlRoot.ContentIslandEnvironment.CoreWindow; coreWindow.PointerPressed -= CoreWindow_PointerPressed; }; } private void CoreWindow_PointerPressed(Windows.UI.Core.CoreWindow sender, Windows.UI.Core.PointerEventArgs args) { if (!isHide) { // 获取对话框的屏幕坐标范围 var dialogBounds = GetBoundingRect(); var pointerPosition = args.CurrentPoint.Position; // 仅当点击位置在对话框外部时执行关闭逻辑 if (!dialogBounds.Contains(pointerPosition)) { if (_actionCommand != null) _actionCommand.Execute(null); else Hide(); } } } // 辅助方法:获取ContentDialog的屏幕坐标区域 private Rect GetBoundingRect() { var transform = this.TransformToVisual(null); var position = transform.TransformPoint(new Point(0, 0)); return new Rect(position.X, position.Y, this.ActualWidth, this.ActualHeight); }
关键说明
- CoreWindow获取方式变更:WinUI3中
ContentDialog的XamlRoot.ContentIslandEnvironment.CoreWindow可正确获取当前关联的窗口核心对象,取代UWP中Window.Current.CoreWindow的用法。 - 点击区域精准判断:新增
GetBoundingRect方法获取对话框的屏幕范围,避免点击对话框内部区域误触发关闭逻辑。 - 事件清理:在
Closed事件中必须移除PointerPressed监听,防止内存泄漏。
完整修改后的CustomContentDialog类代码
public sealed partial class CustomContentDialog : ContentDialog, INotifyPropertyChanged { private string _titleText = string.Empty; private string _messageText = string.Empty; private string _buttonText = string.Empty; private DispatcherTimer _timer = null; private int _timerInterval = 0; private IMvxCommand _actionCommand = null; public bool isHide = true; public CustomContentDialog() { InitializeComponent(); InitializeObserving(); InitializeView(); } public CustomContentDialog(string titleText, string messageText, string buttonText, IMvxCommand actionCommand = null) { _titleText = titleText; _messageText = messageText; _buttonText = buttonText; _actionCommand = actionCommand; InitializeComponent(); InitializeObserving(); InitializeView(); } public CustomContentDialog(string titleText, string messageText, string buttonText, int timerInterval, IMvxCommand actionCommand = null) { _titleText = titleText; _messageText = messageText; _buttonText = buttonText; _timerInterval = timerInterval; _actionCommand = actionCommand; InitializeComponent(); InitializeObserving(); InitializeView(); } private void InitializeObserving() { Opened += (s, e) => { isHide = false; var coreWindow = XamlRoot.ContentIslandEnvironment.CoreWindow; coreWindow.PointerPressed += CoreWindow_PointerPressed; }; Closed += (s, e) => { isHide = true; var coreWindow = XamlRoot.ContentIslandEnvironment.CoreWindow; coreWindow.PointerPressed -= CoreWindow_PointerPressed; }; } private void CoreWindow_PointerPressed(Windows.UI.Core.CoreWindow sender, Windows.UI.Core.PointerEventArgs args) { if (!isHide) { var dialogBounds = GetBoundingRect(); var pointerPosition = args.CurrentPoint.Position; if (!dialogBounds.Contains(pointerPosition)) { if (_actionCommand != null) _actionCommand.Execute(null); else Hide(); } } } private Rect GetBoundingRect() { var transform = this.TransformToVisual(null); var position = transform.TransformPoint(new Point(0, 0)); return new Rect(position.X, position.Y, this.ActualWidth, this.ActualHeight); } private void InitializeView() { Title.Text = _titleText; Message.Text = _messageText; Button.Content = _buttonText; ProgressBar.Visibility = Visibility.Collapsed; if (_timerInterval > 0) { ProgressBar.Visibility = Visibility.Visible; ProgressBar.Maximum = _timerInterval; _timer = new DispatcherTimer() { Interval = TimeSpan.FromSeconds(0.5) }; _timer.Tick += TimerTick; _timer.Start(); } if (_actionCommand == null) { Button.Click += delegate { Hide(); }; } else { Button.Click += delegate { _actionCommand.Execute(null); }; } } private void TimerTick(object sender, object e) { if (TimerValue <= _timerInterval) { TimerValue += 0.5; } else { _timer.Stop(); _timer.Tick -= TimerTick; } } private double _timerValue = 1; public double TimerValue { get => _timerValue; set { _timerValue = value; NotifyPropertyChanged(nameof(TimerValue)); } } public event PropertyChangedEventHandler PropertyChanged; private void NotifyPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
内容的提问来源于stack exchange,提问作者pnk
相关产品推荐
相关产品推荐

