Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I am trying to understand how to bind visibility of child elements to the size of their parent element. My strategy was to build a converter that has an int threshold value (which will correspond with the width of a parent control), and based on that threshold value it decides whether a child element is to be visible or not:

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;
            isVisible = true;
        isVisible = Negate ? !isVisible : isVisible;
        if (isVisible)
            return Visibility.Visible;
            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;
            return VisibilityThreshold - 1;
    public bool Negate { get; set; }
    public int VisibilityThreshold { get; set; }
    public Visibility FalseVisibility { get; set; }

To test how that works, I built a very simple UI that looks like this:

<Window.Resources>
    <ResourceDictionary>
        <local:IntToVisibilityConverter x:Key="IntConverter" VisibilityThreshold="600" Negate="True" />
    </ResourceDictionary>
</Window.Resources>
    <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>

This works well with no problems - When I stretch the window and the button reaches a certain width the ellipse disappears and reappears when I contrast the window back.

However, I run into strange behavior once I add another column to the grid like this:

<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>

Now, when I run this application, if I expand the window such that the width of the buttons pass the threshold, instead of the ellipses disappearing, they start to flicker. It's like with each change of the width they just change their visibility back and forth instead of just permanently collapsing.

Can anybody explain why it's doing that, and how could I make it so it will work with no surprises?

Thanks.

The primary bug is that your converter is trying to do the math using int even though all dimension metrics in WPF are double. Even in the supposedly "working" example, the ellipse will only disappear when the window width is an exact integer. Otherwise, the converter fails on the TryParse() method and then returns what is for the Visibility property a complete nonsensical value (i.e. the double that was originally passed to it).

I changed your converter so it looks like this and both examples now work perfectly for me:

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;
            isVisible = true;
        isVisible = Negate ? !isVisible : isVisible;
        if (isVisible)
            //System.Diagnostics.Debug.WriteLine("isVisible");
            return Visibility.Visible;
            //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;
            return VisibilityThreshold - 1;
    public bool Negate { get; set; }
    public double VisibilityThreshold { get; set; }
    public Visibility FalseVisibility { get; set; }

If you like, you can do extra checking on the valueObject parameter and return Binding.DoNothing if valueObject is double fails. IMHO it's unnecessary, but it will avoid the converter throwing an exception momentarily during initialization in the event you get something like DependencyProperty.UnsetValue as the value for some reason.

I'll note that the original logic was somewhat more verbose than it needs to be, and new pattern-matching and string interpolation features not available when the original code above was written can also help. A somewhat more concise but IMHO more expressive and readable version of the converter methods might look like this:

    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;
        

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.