David Sandor

Build succeeded.

WPF Databinding visibility of a control to your ViewModel / DataContext boolean value.

clock May 5, 2010 18:57 by author dsandor

In many cases you will need to set the visibility of a control based on a value of a property of your ViewModel class.  There are several ways to accomplish this.  The most reusable manner of accomplishing this is to use a value converter to convert the boolean value to a Visibility enumerator.  It sounds complicated but it is super easy.

Note: Before I continue I would like to state that this is an example simplified in order to describe this concept.  The code in this example databinds the IsChecked property of a CheckBox to the view model then databinds that boolean property to the Visibility property of the Button control.  In XAML you can property bind the two controls but that is not what this example is about.  Instead of making this a long example with database calls and other complications I wanted to make this as simple and clear cut as can be.  So if you are looking to databind the property of one control to the property of another this is not the right article for you.

image

So to demonstrate the value converter we will DataBind the (bool) IsChecked property of the CheckBox to the (bool) IsButtonVisible property of the MainWindowViewModel class.  The Visibility property of the Button control is databound to the same (bool) IsButtonVisible property of the MainWindowViewModel class.

So the XAML for this Window looks like this:

 

<Window x:Class="ValueConverterExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:ValueConverterExample.Local"
        Title="MainWindow" Height="218" Width="196">
    <Grid>
        <Grid.Resources>
            <local:BooleanVisibilityValueConverter x:Key="BoolToVisible" />
        </Grid.Resources>
        <Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="51,110,0,0"
          Name="button1" VerticalAlignment="Top" Width="75" 
          Visibility="{Binding Path=IsButtonVisible,Converter={StaticResource BoolToVisible}}" />
        <CheckBox Content="Is Button Visible?" Height="16" HorizontalAlignment="Left"
          Margin="36,56,0,0" Name="checkBox1" VerticalAlignment="Top" 
          IsChecked="{Binding IsButtonVisible}" />
    </Grid>
</Window>

Note there is a Grid.Resources section in the XAML.  This section is making available to the controls on the Grid the BooleanVisibilityValueConverter object and is naming it BoolToVisible.  This will allow any control on this Grid to use that value converter.  In order to do this, I had to include a namespace that I called local in the Window tag declaration.  This sets up a reference to the Namespace where the Value Converter class resides.

Next, in the Button control’s XAML you see that the Visibility Property is DataBound to the IsButtonVisible property of the DataContext.  Below is the code behind for this Window:

namespace ValueConverterExample
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        MainWindowViewModel viewModel = new MainWindowViewModel();

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

And the code for the MainWindowViewModel is located below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

namespace ValueConverterExample.ViewModel
{
    public class MainWindowViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;


        private bool _IsButtonVisible;
        public bool IsButtonVisible
        {
            get { return _IsButtonVisible; }
            set
            {
                _IsButtonVisible = value;
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("IsButtonVisible"));
            }
        }

    }
}

As you can see the IsButtonVisible property which both the CheckBox and Button are databound is a boolean.  This is possible via the ValueConverter:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Data;
using System.Globalization;
using System.Windows.Media;
using System.Windows;

namespace ValueConverterExample.Local
{
    public class BooleanVisibilityValueConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value != null)
            {
                if (((bool)value) == true)
                    return Visibility.Visible;
                else
                    return Visibility.Collapsed;
            }

            return Visibility.Collapsed;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {

            throw new Exception("The method or operation is not implemented.");

        }


    }
}

This value converter code will accept a Boolean value in, and spit out a Visibility enum.  The magic happens in this databinding expression for the button control’s visibility property:

Visibility="{Binding Path=IsButtonVisible,Converter={StaticResource BoolToVisible}}"

Here we are setting the binding to the IsButtonVisible property of the DataContext.  In addition we are telling the DataBinder to use the converter named BoolToVisible.  Remember we gave our BooleanVisibilityValueConverter class a key name of BoolToVisible when we defined the Grid Resources.

Attached is a sample project.  I hope this helps someone!

Download Source Code: ValueConverterExample.zip



Silverlight element databinding like WPF made very easy.

clock November 9, 2009 20:32 by author dsandor

So in WPF you can use RelativeSource binding and Element binding to bind one visual element’s properties to another visual element’s properties.  You probably have discovered this is non-trivial in Silverlight.  If you are using an MVVM patter to develop your Silverlight application your life just got easier!

Why?  Take this example: 

image

You are building a user editor that lets you link roles to a user.  You want the Add Role and Remove Role buttons to only become enabled if the user has clicked a role. 

The solutions is simple.

In your ViewModel class create a boolean property called AvailableRolesSelectedIndex and a property called IsAddRoleEnabled.

private bool _IsAddRoleEnabled;
public bool IsAddRoleEnabled
{
    get { return _IsAddRoleEnabled; }
    set
    {
        _IsAddRoleEnabled = value;
        SafeNotify("IsAddRoleEnabled");
    }
}
 
private int _AvailableRolesSelectedIndex;
public int AvailableRolesSelectedIndex
{
    get { return _AvailableRolesSelectedIndex; }
    set
    {
        if (value >= 0)
            IsAddRoleEnabled = true;
        else
            IsAddRoleEnabled = false;
 
        _AvailableRolesSelectedIndex = value;
        SafeNotify("AvailableRolesSelectedIndex");
    }
}

(*Note I use a ViewModelBase class that defines a function called SafeNotify().  All this does is fire the PropertyChanged event for the INotifyPropertyChanged implementation on my ViewModel.)

Now, in the XAML, simply bind the AvailableRolesSelectedIndex property to the ListBox's SelectedIndex property and bind the IsEnabled property of the Add Role button to the IsAddRoleEnabled property.

 

<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
    <StackPanel Width="200" Margin="5">
        <TextBlock Text="Available Roles" TextWrapping="Wrap" Margin="5,0,0,0" FontWeight="Bold"/>
        <ListBox Name="lbAvailableRoles" Height="184" ItemsSource="{Binding AllRoles}" SelectedIndex="{Binding AvailableRolesSelectedIndex, Mode=TwoWay}" />
  </StackPanel>
  <StackPanel Margin="5" VerticalAlignment="Center">
      <Button Name="btnAddRole" Content="&gt;&gt; Add Role &gt;&gt;" Margin="0,0,0,14" Click="btnAddRole_Click" IsEnabled="{Binding IsAddRoleEnabled}" />
      <Button Name="btnRemoveRole" Content="&lt;&lt; Remove Role &lt;&lt;" Click="btnRemoveRole_Click" />
  </StackPanel>
  <StackPanel Width="200" Margin="5">
      <TextBlock Text="This User's Roles" TextWrapping="Wrap" Margin="5,0,0,0" FontWeight="Bold"/>
      <ListBox Name="lbAssignedRoles" Height="184" ItemsSource="{Binding User.Roles, Mode=TwoWay}" />
  </StackPanel>
</StackPanel>

I also had to add to the constructor of my ViewModel class an initializer for my AvailableRolesSelectedIndex property.  You want it initialized to –1 or when the initial databinding occurs you will receive an error because the integer’s default value is 0, which does not exist in the listbox until the data is there.

public AdminUsersViewModel()
{
    
    if (UserToEdit == null)
    {
        Title = "ADD USER";
        User = new Employee();
    }
    else
    {
        Title = "EDIT USER";
        User = UserToEdit;
    }
 
    EditImageOpacity            = 0;
    DeleteImageOpacity          = 0;
    AvailableRolesSelectedIndex = -1;
 
    initLists();
}

Now I get a great little effect without having to interact directly with the controls on the View from my ViewModel.

Initial state:

image

Item clicked:

image



About the author

David Sandor is a Software Architect working in Chicago, IL.  My development focuses around the Microsoft Stack including Azure, AppFabric, Silverlight, WPF, .NET Framework, and various mobile devices including iOS (iPhone/iTouch), Android, Windows Mobile and Windows Phone 7.

Month List

Sign in