53

Is there a way to use the existing WPF BooleanToVisibilityConverter converter but have False values convert to Hidden instead of the default Collapsed, or should I just write my own? I'm on a project where it's tremendous overhead to do something simple like this (shared stuff goes into a separate solution, and the rebuild/checkin/merge process is an overgrown mutated behemoth of a process), so I'd prefer if I could just pass a parameter to the existing one than to jump through the hoops just mentioned.

Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
Rich
  • 36,270
  • 31
  • 115
  • 154
  • Check my answer. Once you've got it into your code base you'll be able to reuse it in various configurations without requiring any code changes. I have felt your pain and this was the best solution I found. Good luck. – Drew Noakes Jun 27 '10 at 19:04
  • Does this answer your question? [How do I invert BooleanToVisibilityConverter?](https://stackoverflow.com/questions/534575/how-do-i-invert-booleantovisibilityconverter) – StayOnTarget May 01 '20 at 20:12

7 Answers7

135

I've found the simplest and best solution to be this:

[ValueConversion(typeof(bool), typeof(Visibility))]
public sealed class BoolToVisibilityConverter : IValueConverter
{
  public Visibility TrueValue { get; set; }
  public Visibility FalseValue { get; set; }

  public BoolToVisibilityConverter()
  {
    // set defaults
    TrueValue = Visibility.Visible;
    FalseValue = Visibility.Collapsed;
  }

  public object Convert(object value, Type targetType, 
      object parameter, CultureInfo culture)
  {
    if (!(value is bool))
      return null;
    return (bool)value ? TrueValue : FalseValue;    
  }

  public object ConvertBack(object value, Type targetType, 
      object parameter, CultureInfo culture)
  {
    if (Equals(value, TrueValue))
      return true;
    if (Equals(value, FalseValue))
      return false;
    return null;
  }
}

When using it, just configure a version that does exactly what you need it to in XAML like this:

<Blah.Resources>
  <local:BoolToVisibilityConverter
         x:Key="BoolToHiddenConverter"
         TrueValue="Visible" FalseValue="Hidden" />
</Blah.Resources>

Then use it in one or more bindings like this:

<Foo Visibility="{Binding IsItFridayAlready, 
                          Converter={StaticResource BoolToHiddenConverter}}" />

This simple solution addresses hidden/collapsed preferences as well as reversing/negating the effect.

SILVERLIGHT USERS must drop the [ValueConversion] declaration as that attribute is not part of the Silverlight framework. It's not strictly needed in WPF either, but is consistent with built-in converters.

Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
  • Note that I typed this out in a text editor. If anyone finds an error, please let me know and I'll update the code. – Drew Noakes Jun 28 '10 at 03:34
  • There is the sourcecode for the "IsItFridayAlready" when you have a binding code that is "Binding IsItFridayAlready" in xaml? – HelloWorld1 Aug 10 '11 at 07:59
  • @FullmetalBoy, ah ok, I see what you mean. `IsItFridayAlready` is some property that you are binding to, via whatever is locally set as the DataContext. In the binding expression, it represents that path to bind to. – Drew Noakes Aug 10 '11 at 14:15
  • You can make even a more generic BoolToWhateverConverter based on this – szx Feb 11 '15 at 09:41
  • 1
    @szx, indeed. If you just want to target visibility though, then constraining to `Visibility` will make using it from XAML a bit simpler as you don't have to fully qualify the enum. – Drew Noakes Feb 12 '15 at 20:55
  • @DrewNoakes soz 2 bother u. I have traced your constructor and it works, but can't work out why the Convert or ConvertBack methods wont trigger for my code. any suggestions? – pgee70 Nov 04 '17 at 12:55
  • @pgee70 the must be a problem with your binding. – Drew Noakes Nov 04 '17 at 13:22
  • How about adding this? `#if !SILVERLIGHT [ValueConversion(typeof(bool), typeof(Visibility))] #endif` – Kamil Aug 02 '22 at 21:46
  • Whaaat? We can add converter properties just like that? Looks like I need better WPF books. – Kamil Aug 02 '22 at 21:47
32

Unfortunately, it only converts to Visible or Collapsed, so you'll have to write your own. Here is the Convert method according to Reflector:

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
    bool flag = false;
    if (value is bool)
    {
        flag = (bool)value;
    }
    else if (value is bool?)
    {
        bool? nullable = (bool?)value;
        flag = nullable.HasValue ? nullable.Value : false;
    }
    return (flag ? Visibility.Visible : Visibility.Collapsed);
}
Adam
  • 347
  • 5
  • 23
Quartermeister
  • 57,579
  • 7
  • 124
  • 111
  • I've found that the `(value is bool?)` check is not necessary. if a nullable bool is passed as a parameter this is automatically converted to a normal bool it the value is set and the first check will succed. if the value is not set(i.e. it's null), then it's an object and both checks will fail. – memory of a dream Apr 15 '16 at 15:56
  • 1
    Are you sure this is the original code? Because as it is, it converts to Hidden, not to Collapsed... – motoDrizzt Apr 08 '17 at 13:54
  • @motoDrizzt The edit by RJFalconer has changed the sample to use hidden instead of collapsed making the original preface incorrect. – Adam Jun 09 '17 at 11:52
  • @Adam: now I'm twicely confused. The BooleanToVisibilityConverter convert to collapsed, so I expect the code from the reflector to show it. The above answer say "Here is the Convert method according to Reflector", still does not use Collapsed but Hiddden. Do you mean that basically RJFalconer edited the answer in the wrong way? 'cause if that the case, I'll edit back the Collapsed – motoDrizzt Jun 09 '17 at 12:01
  • 1
    @motoDrizzt Yes, I believe RJFalconer had changed erroneously changed the sample code to "Hidden". I've suggested an edit so the answer will read "Collapsed" again (as would be the behaviour shown by Reflector and what the answer originally said). – Adam Jun 09 '17 at 14:33
  • I just tried this in a .NET Core 3.1 WPF application, and it is possible to return `Visibility.Hidden` – JansthcirlU Sep 30 '20 at 08:01
6

Can you not just use a style instead of a converter? The code would be something like:

<Style x:Key="Triggers" TargetType="Button">
    <Style.Triggers>
    <Trigger Property="{Binding ...}" Value="false">
        <Setter Property = "Visibility" Value="Hidden"/>
    </Trigger>
    </Style.Triggers>
</Style>

You'll need to provide the property binding yourself to point to your bool property.

cristobalito
  • 4,192
  • 1
  • 29
  • 41
  • 2
    It's quite wasteful to use styles and triggers every time for such a common conversion - that's why we have these common converters! – Dan Puzey Jun 27 '10 at 16:50
  • 1
    I agree. This totally works, but I hate how much code it adds to my xaml. This project has tons of this kind of stuff going on and I find it makes reading it really difficult the more you have. I'd love to know if there's a performance difference to the two approaches. – Rich Jun 27 '10 at 18:38
  • I don't honestly know - I'm a newbie to the whole WPF stuff. Until Dan mentioned there might be a performance issue, I certainly never had considered it, but it's an interesting question. Does this really add that much code to your XAML though - it's a keyed style, so you can just reference it where needed. Alternatively, you can also base styles of other styles, thereby adding possible re-use points. See http://msdn.microsoft.com/en-us/library/system.windows.style.basedon.aspx – cristobalito Jun 27 '10 at 18:57
  • It's only an issue when you've got a really big project like the one I'm working on. What it really comes down to is on a per-case basis. In my case, making anything new that is reusable means rebuilding an external project and deploying the dll's, and when you have at least 50 devs on the same project checking in all day long, you look to avoid this at all cost. Plus, our xaml files keep growing and growing, so I'm really looking to keep everything as minimal as possible unless there's a significant performance gain with the more verbose solution. – Rich Jun 28 '10 at 15:43
  • I find this a very useful solution, only if it works! I get the following error: "A 'Binding' cannot be set on the 'Property' property of type 'Trigger'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject." This solution would be great in cases where it is one time thing; for example, applying it to a Navigation Bar that will be available everywhere in the app. – usefulBee May 05 '16 at 16:06
  • @usefulBee Use a DataTrigger instead of Trigger to fix your error. – odyth Mar 08 '20 at 18:53
4

I like to use the parameter for reversing the visibility logic: To reverse logic simply put: ConverterParameter=Reverse in your xaml code

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
    bool flag = false;
    if (value is bool)
    {
        flag = (bool)value;
    }

    var reverse = parameter as string;
    if(reverse != null && reverse == "Reverse")
        flag != flag;

    return (flag ? Visibility.Visible : Visibility.Collapsed);
}
hkon
  • 1,035
  • 7
  • 18
1

I wrote BoolToVisibilityConverte where you can pass invisible state in Parameter:

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
    var boolValue = (bool) value;
    return boolValue ? Visibility.Visible : (parameter ?? Visibility.Hidden);
}

So you can bind like this:

Visibility="{Binding SomeBool, Converter={StaticResource ResourceKey=BooleanToVisibilityConverter}, ConverterParameter={x:Static Visibility.Collapsed}}"

Hope this helps :)

FredM
  • 454
  • 9
  • 20
Krzysztof Skowronek
  • 2,796
  • 1
  • 13
  • 29
0

I had this issue crop up and my solution was probably very situational but I'll share it anyway. Due to my circumstance I was able to mimic the converter, without a converter, with a simple bit of code. I had visibility change only if a variable bound to a textbox made number box( through a Regex to ensure its a number) was not 0. The entire code for this is below however the WPF and first bit of C# is all you really need if you are going to change your boolean elsewhere in code. wpf:

    Visibility="{Binding Path=Visible}"

C#

    public class foo : INotifyPropertyChanged
    {
        private bool vis = false;
        public object Visible
        {
            get 
            { 
                if (vis == true) 
                { 
                    return Visibility.Visible; 
                } 
                else 
                { 
                    return Visibility.Hidden; 
                } 
            }
            set
            { 
                vis = (bool)value; 
                OnPropertyChanged(nameof(Visible)); 
            }
        }
        public int Value_b
        {
            get 
            { 
                return base_value;
            }
            set
            { 
                base_value = value; 
                OnPropertyChanged(nameof(Value_b));
                if (base_value == 0) 
                { 
                    Visible = false; 
                } 
                else 
                { 
                    Visible = true; 
                } 
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new 
            PropertyChangedEventArgs(propertyName));
        }
    }
0

Using Caliburn.Micro, I have the following code that works for my simple use case and may work for others.

In my View, I have a button that I want to be hidden unless one of my variables has a positive length string:

<Button x:Name="SelectBinaryFilePath" Content="Select" Visibility="{Binding CanSelectBinaryFile}" />

In my ViewModel I have the following property:

public Visibility CanSelectBinaryFile
{
    get
    {
        return String.IsNullOrEmpty(FileSystemPath) ? Visibility.Hidden : Visibility.Visible;
    }
}

Ensure to do a NotifyOfPropertyChange to ensure the CanSelectBinaryFile prop is updated.

Michael Murphy
  • 1,921
  • 2
  • 18
  • 21