3

我正在尝试在 WPF 中实现一种单行选项卡控件,它显示包含选项卡的区域的左右滚动按钮。选项卡在自定义控件中实现。只有当窗口太小而无法显示所有选项卡时,才会显示滚动按钮。当窗口通过拖动其边框来调整大小时,一切都按预期工作。但是当窗口最大化然后恢复时,右滚动按钮仍然隐藏。

只有当Visibility右滚动按钮的属性数据绑定到在自定义控件的 Measure 过程中更新的自定义控件的依赖属性时,才会出现问题。

我的问题是:我在这里正确使用 WPF,还是需要以不同的方式完成某些事情?(请注意:我需要使用数据绑定和自定义控件;因此请避免建议采取完全不同方法的答案。)

这是一个说明问题的小示例程序:

当宽度较小时:

小窗户

当宽度很大时:

大窗户

这些是示例程序的文件:

MainWindow.xaml:

<Window x:Class="GridTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:gridTest="clr-namespace:GridTest"
        Title="MainWindow" Height="350" Width="525">
    <Grid x:Name="theGrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions> 
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Button x:Name="btnScrollLeft" Content="&lt;" Grid.Row="0" Grid.Column="0" Width="30"/>
        <gridTest:MyCustomControl x:Name="cust" Grid.Row="0" Grid.Column="1"/>
        <Button x:Name="btnScrollRight" Content="&gt;" Grid.Row="0" Grid.Column="2" Width="30"
                Visibility="{Binding ElementName=cust, Path=ShowButton}"/>
        <TextBox Text="The content goes here..." Grid.Row="1" Grid.ColumnSpan="3"
                 Background="LightGreen" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"/>
        <Button x:Name="btnRedraw" Grid.Row="1" Grid.Column="1" Content="Redraw" VerticalAlignment="Bottom"
                HorizontalAlignment="Center" Click="btnRedraw_Click" />
    </Grid>
</Window>

MainWindow.xaml.cs:

using System.Windows;
namespace GridTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void btnRedraw_Click(object sender, RoutedEventArgs e)
        {
            theGrid.InvalidateMeasure();
        }
    }
}

MyCustomControl.cs:

using System;
using System.Windows;
using System.Windows.Controls;

namespace GridTest
{
    public class MyCustomControl : Control
    {
        static MyCustomControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl), new FrameworkPropertyMetadata(typeof(MyCustomControl)));
        }

        public Visibility ShowButton
        {
            get { return (Visibility)GetValue(ShowButtonProperty); }
            set { SetValue(ShowButtonProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ShowButton.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ShowButtonProperty =
            DependencyProperty.Register("ShowButton", typeof(Visibility), typeof(MyCustomControl), new UIPropertyMetadata(Visibility.Visible));

        protected override Size MeasureOverride(Size constraint)
        {
            if (constraint.Width > 800)
            {
                ShowButton = Visibility.Collapsed;
            }
            else
            {
                ShowButton = Visibility.Visible;
            }

            double width = Math.Min(2000.0, constraint.Width);
            double height = Math.Min(50.0, constraint.Height);

            return new Size(width, height);
        }
    }
}

通用的.xaml:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:GridTest">

    <Style TargetType="{x:Type local:MyCustomControl}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:MyCustomControl}">
                    <Border Background="LightCyan">
                        <TextBlock VerticalAlignment="Center" TextAlignment="Center">Custom Control</TextBlock>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

该问题可以重现如下:

  • 确保窗口很小,以便可以看到右滚动按钮。
  • 现在最大化窗口。=> 右滚动按钮应该变得不可见。
  • 现在将窗口恢复到原来的大小。=> 右滚动按钮应该再次可见。(问题是:右滚动按钮仍然不可见。)

编辑:仅供参考:我可以重现 VS2010+.NET4.0 和 VS2013+.NET4.51 的问题。

4

2 回答 2

0

您需要将ShowButton可见性更改分派回调度队列以使其工作(为应用程序提供渲染所需的时间并按顺序执行),而不是直接在MeasureOverride方法中执行。

所以说我把你MeasureOverride改成

protected override Size MeasureOverride(Size constraint) {
  if (constraint.Width > 800) {
    Application.Current.Dispatcher.BeginInvoke(
      new Action(() => ShowButton = Visibility.Collapsed));
  } else {
    Application.Current.Dispatcher.BeginInvoke(
      new Action(() => ShowButton = Visibility.Visible));
  }

  double width = Math.Min(2000.0, constraint.Width);
  double height = Math.Min(50.0, constraint.Height);

  return new Size(width, height);
}

你可以看到它工作正常。

使用您发布的原始代码,您可以看到即使您最大化您Window的 ,Button右侧的 也会被隐藏,但实际上并不Collapsed像您设置的那样,这再次是由于控件没有获得新的相同原因尺寸。

同样在恢复并Button保持隐藏状态后,如果您通过拖动它的大小来重新调整Window大小,您可以看到按钮Visible再次变为。

通过调度Visibility更改,如果您的函数中有断点MeasureOverride,您可以看到它被调用两次(一次用于Window大小更改,第二次由于Button隐藏/显示而更改大小),主要是在最大化/恢复窗口时,从而产生正确的尺寸计算和您想要的输出。

于 2013-10-23T13:58:51.253 回答
0

之前使用过一些 custom Panel,我似乎记得传递给MeasureOverride方法的值不是实际使用的大小......尝试将该代码移动到ArrangeOverride方法中,看看会发生什么。

于 2013-10-23T15:36:10.403 回答