【问题标题】:How can you ensure that SizeChangedEventHandler runs LATER than a PropertyChanged result?如何确保 SizeChangedEventHandler 比 PropertyChanged 结果运行晚?
【发布时间】:2020-07-17 20:12:07
【问题描述】:

我有一个SizeChangedEventHandler,它会在每次窗口宽度发生变化时设置窗口的位置。 (如果窗口变为 100 宽,其Left 属性将设置为距屏幕右边缘 100,因此窗口始终与屏幕右边缘对齐。)

使宽度发生变化的是按钮的“可见性”在“可见”和“折叠”之间切换,在我的代码隐藏中使用 Binding 到布尔值和 BooleanToVisibilityConverter(全部演示如下)。

问题是,似乎是根据按钮消失之前的宽度而不是之后来计算位置。这使得窗口不会按照需要与屏幕的右边缘对齐。宽度为 80 时距离为 100,宽度为 100 时距离为 80。

<!-- MainWindow.xaml -->

<Window
    x:Class="blah.MainWindow"
    WindowStyle="None"
    ResizeMode="NoResize"
    Topmost="True"
    SizeToContent="WidthAndHeight"
    ...
>
    <Grid>
        <WrapPanel Margin="3,0,3,3">
            // IF is my shortened name for BooleanToVisibilityConverter
            // In an <Application.Resources> I have <BooleanToVisibilityConverter x:Key="IF" />
            <StackPanel Visibility="{Binding showThing, Converter={StaticResource IF}}">
                <Button />
            </StackPanel>
            <Button />
            <Button />
            <Button />
            <Button Click="handleClick" />
        </WrapPanel>
    </Grid>
</Window>

然后是它的代码隐藏

// MainWindow.xaml.cs

namespace blah
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private bool _showThing = false;
        public bool showThing
        {
            get { return _showThing; }
            set { _showThing = value; this.OnPropertyChanged(); }
        }

        #region INotifyPropertyChanged Members
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        #endregion

        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;

            this.Loaded += new RoutedEventHandler(this.setPosition_EventHandler);
            this.SizeChanged += new SizeChangedEventHandler(this.setPosition_EventHandler);
        }

        private void setPosition_EventHandler(object sender, RoutedEventArgs e)
        {
            Rect desktopWorkingArea = SystemParameters.WorkArea;
            this.Left = desktopWorkingArea.Right - this.Width;
            this.Top = desktopWorkingArea.Bottom - this.Height;
        }

        private void handleClick(object sender, RoutedEventArgs e)
        {
            showThing = !showThing;
        }
    }
}

其他尝试

showThing 在单击右键时正确设置,左键正确折叠然后可见,setPosition_EventHandler 每次都被调用,因此窗口重新定位。只是它重新定位,好像按钮的可见性与其应有的可见性相反。

我尝试添加一个与setPosition_EventHandler 相同的setPosition,并在showThing 的设置器中调用它:

set { _showThing = value; this.OnPropertyChanged(); setPosition(); }

但它似乎仍然发生在属性更改导致按钮的可见性切换并因此改变窗口宽度之前。

有谁知道我如何确保尺寸更改事件处理程序确实发生在之后由于按钮被折叠而宽度发生了变化?

【问题讨论】:

  • The problem is, it appears that the position is being calculated based on the width before the button disappears, instead of after. 你能解释一下before the button disappears 的意思吗,上面的代码没有用按钮做任何事情。您是否也尝试过将按钮的可见性绑定设置为与堆栈面板相同?
  • 是的,当然。请参阅StackPanelVisibility="..."。它里面有一个Button,这就是占用空间的地方。从技术上讲,我可以说“StackPanel 消失了”。
  • 感谢您的评论。问题是,由于调度程序,绑定实际上比 Windows 消息发生晚。简而言之,处理 UI 的消息将在绑定发生之前发生;你无法控制这个。你必须做一些古怪的添加/删除处理程序等。
  • 啊,有趣,谢谢。那么有没有更合适的地方来运行setPosition,而不是像现在这样运行SizeChangedEventHandler?这样按钮消失或重新出现后,然后计算并设置位置。
  • 您也许可以尝试LayoutUpdatedIsVisibleChanged 事件并在那里处理位置;此事件是控件的一部分。

标签: c# wpf xaml data-binding


【解决方案1】:

如果需要,您可以调整调度程序优先级或切换到依赖项属性。这样做可能实际上会产生您想要的最终结果。但从根本上说,这里真正的问题只是您使用了错误的属性来检索窗口的宽度和高度。

作为the documentation explainsSizeChanged 事件:

ActualHeightActualWidth 属性更改此元素的值时发生。

为了修复程序,您真正需要做的就是在计算新窗口位置时使用ActualHeightActualWidth 属性,因为这些 是实际保证的属性引发事件时已更改。

这是一个说明该技术的实际最小、完整的代码示例:

<Window x:Class="TestSO62961161AnchorBottomRightWindow.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:p="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        mc:Ignorable="d"
        SizeChanged="Window_SizeChanged"
        Title="MainWindow" SizeToContent="WidthAndHeight">
  <Grid>
    <Grid.Style>
      <p:Style TargetType="Grid">
        <Setter Property="Width" Value="800"/>
        <Setter Property="Height" Value="450"/>
        <p:Style.Triggers>
          <DataTrigger Binding="{Binding Toggle, RelativeSource={RelativeSource AncestorType=Window}}" Value="True">
            <Setter Property="Width" Value="700"/>
            <Setter Property="Height" Value="400"/>
          </DataTrigger>
        </p:Style.Triggers>
      </p:Style>
    </Grid.Style>

    <Button Content="Click" Click="Button_Click"
            HorizontalAlignment="Left" VerticalAlignment="Top"/>
  </Grid>
</Window>
public partial class MainWindow : Window, INotifyPropertyChanged
{
    private bool _toggle;

    public bool Toggle
    {
        get => _toggle;
        set
        {
            if (_toggle != value)
            {
                _toggle = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Toggle)));
            }
        }
    }

    public MainWindow()
    {
        InitializeComponent();
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        Toggle = !Toggle;
    }

    private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        Rect desktopWorkingArea = SystemParameters.WorkArea;

        this.Left = desktopWorkingArea.Right - this.ActualWidth;
        this.Top = desktopWorkingArea.Bottom - this.ActualHeight;
    }
}

【讨论】:

  • 这种技术效果很好!然而,有一个视觉“闪光”,因为重新定位在触发它的调整大小之后发生得如此轻微(对于具有更多内容的窗口来说更明显)。我相信这是最好的,它只是 WPF 的一个缺点,尽管向右/向下扩展窗口大小非常容易和平滑,但不支持向左/向上平滑。
猜你喜欢
  • 2021-02-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-18
  • 1970-01-01
  • 2021-12-25
  • 1970-01-01
  • 2015-05-10
相关资源
最近更新 更多