繁体   English   中英

XAML:基于实际宽度的可见性在网格中表现得很奇怪

[英]XAML: Visibility based on actual width behaves strangely in a grid

我试图了解如何将子元素的可见性与其父元素的大小绑定。 我的策略是构建一个具有 int 阈值(与父控件的宽度相对应)的转换器,并根据该阈值决定子元素是否可见:

public class IntToVisibilityConverter : IValueConverter
{
    public IntToVisibilityConverter()
    {
        FalseVisibility = Visibility.Collapsed;
        Negate = false;
        VisibilityThreshold = 0;
    }

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        int iVal;
        bool result = int.TryParse(value.ToString(), out iVal);
        if (!result) return value;
        bool isVisible;

        if (iVal < VisibilityThreshold)
        {
            isVisible = false;
        }
        else
        {
            isVisible = true;
        }

        isVisible = Negate ? !isVisible : isVisible;

        if (isVisible)
        {
            return Visibility.Visible;
        }
        else
        {
            return FalseVisibility;
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        Visibility? val = null;
        if (value is Visibility) val = (Visibility)value;

        if (!val.HasValue) return value;

        bool result = val == Visibility.Visible;

        result = Negate ? !result : result;

        if (result)
        {
            return VisibilityThreshold;
        }
        else
        {
            return VisibilityThreshold - 1;
        }
    }

    public bool Negate { get; set; }
    public int VisibilityThreshold { get; set; }
    public Visibility FalseVisibility { get; set; }
}

为了测试它是如何工作的,我构建了一个非常简单的 UI,如下所示:

<Window.Resources>
    <ResourceDictionary>
        <local:IntToVisibilityConverter x:Key="IntConverter" VisibilityThreshold="600" Negate="True" />
    </ResourceDictionary>
</Window.Resources>

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="50" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <Button Grid.Column="1" x:Name="button1">
        <Ellipse Fill="Orange" Width="100" Height="100" Visibility="{Binding ElementName=button1, Path=ActualWidth, Converter={StaticResource IntConverter}}" />
    </Button>
</Grid>

这很有效,没有问题 - 当我拉伸窗口并且按钮达到一定宽度时,椭圆消失并在我对比窗口时重新出现。

但是,一旦将另一列添加到网格中,我就会遇到奇怪的行为,如下所示:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="50" />
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <Button Grid.Column="1" x:Name="button1">
        <Ellipse Fill="Orange" Width="100" Height="100" Visibility="{Binding ElementName=button1, Path=ActualWidth, Converter={StaticResource IntConverter}}" />
    </Button>
    <Button Grid.Column="2" x:Name="button2">
        <Ellipse Fill="DarkOrange" Width="100" Height="100" Visibility="{Binding ElementName=button2, Path=ActualWidth, Converter={StaticResource IntConverter}}" />
    </Button>
</Grid>

现在,当我运行这个应用程序时,如果我扩展窗口使得按钮的宽度超过阈值,而不是椭圆消失,它们开始闪烁。 就像宽度的每次变化一样,它们只是来回改变它们的可见性,而不仅仅是永久折叠。

任何人都可以解释为什么它会这样做,我怎样才能让它毫无意外地工作?

谢谢。

主要错误是您的转换器试图使用int进行数学运算,即使 WPF 中的所有维度指标都是double 即使在所谓的“工作”示例中,椭圆只会在窗口宽度为精确整数时消失。 否则,转换器在TryParse()方法上失败,然后为Visibility属性返回一个完全无意义的值(即最初传递给它的double精度值)。

我改变了你的转换器,所以它看起来像这样,两个例子现在对我来说完美无缺:

public class IntToVisibilityConverter : IValueConverter
{
    public IntToVisibilityConverter()
    {
        FalseVisibility = Visibility.Collapsed;
        Negate = false;
        VisibilityThreshold = 0;
    }

    public object Convert(object valueObject, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        double value = (double)valueObject;
        bool isVisible;

        if (value < VisibilityThreshold)
        {
            isVisible = false;
        }
        else
        {
            isVisible = true;
        }

        isVisible = Negate ? !isVisible : isVisible;

        if (isVisible)
        {
            //System.Diagnostics.Debug.WriteLine("isVisible");
            return Visibility.Visible;
        }
        else
        {
            //System.Diagnostics.Debug.WriteLine("NOT isVisible");
            return FalseVisibility;
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        Visibility? val = null;
        if (value is Visibility) val = (Visibility)value;

        if (!val.HasValue) return value;

        bool result = val == Visibility.Visible;

        result = Negate ? !result : result;

        if (result)
        {
            return VisibilityThreshold;
        }
        else
        {
            return VisibilityThreshold - 1;
        }
    }

    public bool Negate { get; set; }
    public double VisibilityThreshold { get; set; }
    public Visibility FalseVisibility { get; set; }
}

如果您愿意,您可以对valueObject参数进行额外检查,并在valueObject is double失败时返回Binding.DoNothing 恕我直言,这是不必要的,但它会避免转换器在初始化期间暂时抛出异常,以防您因某种原因获得类似DependencyProperty.UnsetValue的值。

我会注意到原始逻辑比它需要的要冗长一些,并且在编写上述原始代码时不可用的新模式匹配和字符串插值功能也可以提供帮助。 转换器方法的一个更简洁但恕我直言更具表现力和可读性的版本可能如下所示:

    public object Convert(object valueObject, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        double value = (double)valueObject;
        bool isVisible = (value >= VisibilityThreshold) ^ Negate;

        //System.Diagnostics.Debug.WriteLine($"{(isVisible ? "" : "NOT ")}isVisible");

        return isVisible ? Visibility.Visible : FalseVisibility;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value is not Visibility val) return value;

        bool result = (val == Visibility.Visible) ^ Negate;

        return result ? VisibilityThreshold : VisibilityThreshold - 1;
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM