【问题标题】:How to center a popup in window (Windows store apps)如何在窗口中居中弹出窗口(Windows 商店应用程序)
【发布时间】:2013-09-26 15:43:50
【问题描述】:

我有一个以编程方式加载的自定义弹出窗口(作为用户控件)。我无法将它集中在 x 轴上,只能在垂直方向上。弹出窗口不会添加到 xaml 文件中,而是添加到根窗口中。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using System.Windows;
using Windows.UI.Core;

// The User Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234236

namespace QSTLibrary.WIN8.Tools
{
    public sealed partial class CustomProgressRingPopup : UserControl
    {

        public CustomProgressRingPopup()
        {
            this.InitializeComponent();
        }

        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Text.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
                        "Text", 
                        typeof(string), 
                        typeof(CustomProgressRingPopup),
                        new PropertyMetadata("", OnTextChanged));


        public void OpenPopup()
        {
            this.ParentPopup.IsOpen = true;
        }

        public void ClosePopup()
        {
            this.ParentPopup.IsOpen = false;
        }

        private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var instance = d as CustomProgressRingPopup;
            var newValue = e.NewValue as string;
            if (instance != null && newValue != null)
            {
                instance.CustomTextBlock.Text = newValue;
            }
        }

        private void OnPopupLoaded(object sender, RoutedEventArgs e)
        {
            this.ParentPopup.HorizontalOffset = (Window.Current.Bounds.Width - gdChild.ActualWidth) / 2;
            this.ParentPopup.VerticalOffset = (Window.Current.Bounds.Height - gdChild.ActualHeight) / 2;
        }
    }
}

userControl xaml:

<UserControl
    x:Class="QSTLibrary.WIN8.Tools.CustomProgressRingPopup"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:QSTLibrary.WIN8.Tools"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300"
    d:DesignWidth="400">

    <Popup x:Name="ParentPopup" HorizontalAlignment="Center" VerticalAlignment="Center" Loaded="OnPopupLoaded">
        <Grid x:Name="gdChild" Height="auto" Width="auto" Background="Blue" Margin="20">
            <Grid.RowDefinitions>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <ProgressRing x:Name="CustomProgressRing" Height="40" Width="40" IsActive="true" Grid.Column="0" Margin="20"/>
            <TextBlock x:Name="CustomTextBlock" Height="auto" Width="auto" FontSize="25" Grid.Column="1" Margin="20"/>
        </Grid>
    </Popup>    
</UserControl>

这是我在外部使用它的方式:

_loginProgressRingPopup.Text = "Logging in";
_loginProgressRingPopup.OpenPopup();

【问题讨论】:

    标签: c# xaml windows-store-apps


    【解决方案1】:

    我在cs文件中添加了弹窗的LayoutUpdate回调,现在可以使用了。

    private void OnLayoutUpdated(object sender, object e)
            {
                if(gdChild.ActualWidth == 0 && gdChild.ActualHeight == 0)
                {
                    return;
                }
    
                var coordinates = ParentPopup.TransformToVisual(Window.Current.Content).TransformPoint(new Point(0, 0));            
    
                double ActualHorizontalOffset = ParentPopup.HorizontalOffset;
                double ActualVerticalOffset = ParentPopup.VerticalOffset;
    
                double NewHorizontalOffset = ((Window.Current.Bounds.Width - gdChild.ActualWidth) / 2) - coordinates.X;
                double NewVerticalOffset = ((Window.Current.Bounds.Height - gdChild.ActualHeight) / 2) - coordinates.Y;
    
                if (ActualHorizontalOffset != NewHorizontalOffset || ActualVerticalOffset != NewVerticalOffset)
                {
                    this.ParentPopup.HorizontalOffset = NewHorizontalOffset;
                    this.ParentPopup.VerticalOffset = NewVerticalOffset;
                }
            }
    

    以及来自 XAML 的弹出窗口:

    <Popup x:Name="ParentPopup" LayoutUpdated="OnLayoutUpdated">
    

    【讨论】:

    • “Window.Current.Bounds”导致我的应用程序崩溃并出现错误“检测到布局周期。布局无法完成​​。”通过将其更改为我正在使用的顶级容器,它解决了它。我还更改了 (NewHorizo​​ntalOffset != double.NaN && (ActualHorizo​​ntalOffset != NewHorizo​​ntalOffset || ActualVerticalOffset != NewVerticalOffset)) - 检查是否发生了 NaN。
    • @DaveFriedel 我们面临同样的问题,但无法实施您的建议。我们有一个弹出窗口,我们在其中使用具有此代码的用户控件,我们应该如何解决这个问题,以便不抛出循环布局异常
    • @Teja - 你能发布一个样本吗(我不想问这个问题,因为这样做通常太复杂了)我建议将“Window.Current.Bounds.Width”替换为位于堆栈顶部的网格根或其他对象。我发现“窗口”导致了我的问题,但如果我命名一个包装对象,它会起作用。
    • 看来 Windows 不喜欢你在 LayoutUpdated 处理程序中调整影响布局的属性。建议改用SizeChanged事件,好像没有这个问题。
    • 还有“检测到布局周期。布局无法完成​​”的问题,但通过将 NewHorizo​​ntal 和 Vertical 偏移量转换为 int 来修复它
    【解决方案2】:

    在 UWP Windows 应用商店应用中定位 Popup 并不像想象的那么简单。这就是我最终解决它的方法。与其观察 LayoutUpdated 事件(这可能导致布局循环),不如观察视图顶层元素的 SizeChanged 事件,并使用它来重新定位它包含的弹出窗口。

    在我的例子中,我将对话框定位在窗口的中心,因此使用Window.Current。您还需要转换坐标,因为弹出窗口将根据您的布局中实际定义的 Popup 元素的位置使用自己的相对坐标系:

    private void MyDialog_SizeChanged(object sender, SizeChangedEventArgs e) {
        var transform = Window.Current.Content.TransformToVisual(_popup);
        Point point = transform.TransformPoint(new Point(0, 0)); // gets the window's (0,0) coordinate relative to the popup
    
        double hOffset = (Window.Current.Bounds.Width - this.ActualWidth) / 2;
        double vOffset = (Window.Current.Bounds.Height - this.ActualHeight) / 2;
    
        _popup.HorizontalOffset = point.X + hOffset;
        _popup.VerticalOffset = point.Y + vOffset;
    }
    

    【讨论】:

      【解决方案3】:

      您可以在 XAML 文件中居中:

      <Popup x:Name="ParentPopup" PlacementTarget="{Binding ElementName=MainPanel}" Placement="Center" />
      

      【讨论】:

      • 我遇到了错误:在“Popup”类型中找不到属性“PlacementTarget”。 “展示位置”也是如此。
      • @AlexandruCircus PlacementTarget 是您想要居中的面板。 MainPanel 默认存在,但您可以在另一个面板中居中。
      • 好的,我明白了。但似乎 PlacementTarget 属性不能应用于 Popup 元素,这就是我从错误中理解的:“在类型 'Popup' 中找不到属性 'PlacementTarget'。”此外,当我编写 Placemen 时,它无法在代码中自动完成......它应该向 PlacementTarget 提供自动完成建议
      • 该项目是一个 Windows Store 应用程序。也许 PlacementTarget 不适用于此类项目。我是 C#、.NET 的新手......我是一名 java 程序员
      • 您是否尝试过仅使用Grid 而不是Popup?在OpenPopup() 方法中,您需要设置VisibilityGrid。如果您只使用Grid,您也不需要处理Popup 的偏移属性。
      【解决方案4】:

      我修改了 Butzke 的答案,使其更具普遍性:

      <Popup PlacementTarget="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}" Placement="Center" />
      

      【讨论】:

      • 这适用于 WPF,但 PlacementTarget 和 FindAncestor 模式在 uwp 应用程序中不可用
      猜你喜欢
      • 2014-02-09
      • 1970-01-01
      • 2013-12-15
      • 2016-05-10
      • 1970-01-01
      • 2015-05-04
      • 1970-01-01
      • 1970-01-01
      • 2014-04-19
      相关资源
      最近更新 更多