This problem is a little hairy since you have multiple view-model classes involved. Here's a crack at the code to solve this. The only thing I'm missing is that the DataGrid doesn't seem to update your items until you leave the row.

XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<DataGrid AutoGenerateColumns="False"
ItemsSource="{Binding Path=SelectableThings}"
Grid.Row="0" Margin="6">
<DataGrid.Columns>
<DataGridCheckBoxColumn Header="IsSelected"
Binding="{Binding IsSelected}"/>
<DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
</DataGrid.Columns>
</DataGrid>
<Button Content="{Binding Path=SelectedTestCount}"
Command="{Binding SaveCommand}"
Grid.Row="1" Width="75" Height="23"
HorizontalAlignment="Right" Margin="0,0,6,6"/>
</Grid>
</Window>
Thing class:
public class Thing : INotifyPropertyChanged
{
private readonly List<SelectableThing> selectableThings;
private DelegateCommand saveCommand;
public Thing(IEnumerable<SelectableThing> selectableThings)
{
this.selectableThings = new List<SelectableThing>(selectableThings);
this.SelectableThings =
new ObservableCollection<SelectableThing>(this.selectableThings);
this.SaveCommand = this.saveCommand = new DelegateCommand(
o => Save(),
o => SelectableThings.Any(t => t.IsSelected)
);
// Bind children to change event
foreach (var selectableThing in this.selectableThings)
{
selectableThing.PropertyChanged += SelectableThingChanged;
}
SelectableThings.CollectionChanged += SelectableThingsChanged;
}
public ObservableCollection<SelectableThing> SelectableThings
{
get;
private set;
}
public int SelectedTestCount
{
get { return SelectableThings.Where(t => t.IsSelected).Count(); }
}
public ICommand SaveCommand { get; private set; }
private void Save()
{
// Todo: Implement
}
private void SelectableThingChanged(object sender,
PropertyChangedEventArgs args)
{
if (args.PropertyName == "IsSelected")
{
RaisePropertyChanged("SelectedTestCount");
saveCommand.RaiseCanExecuteChanged();
}
}
private void SelectableThingsChanged(object sender,
NotifyCollectionChangedEventArgs e)
{
foreach (SelectableThing selectableThing in
e.OldItems ?? new List<SelectableThing>())
{
selectableThing.PropertyChanged -= SelectableThingChanged;
RaisePropertyChanged("SelectedTestCount");
}
foreach (SelectableThing selectableThing in
e.NewItems ?? new List<SelectableThing>())
{
selectableThing.PropertyChanged += SelectableThingChanged;
RaisePropertyChanged("SelectedTestCount");
}
}
public void RaisePropertyChanged(string propertyName)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
SelectableThing class:
public class SelectableThing : INotifyPropertyChanged
{
private string name;
private bool isSelected;
public SelectableThing(string name)
{
this.name = name;
}
public string Name
{
get { return name; }
set
{
name = value;
RaisePropertyChanged("Name");
}
}
public bool IsSelected
{
get { return isSelected; }
set
{
isSelected = value;
RaisePropertyChanged("IsSelected");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Original Answer:
Bind Command to an ICommand. Set your CanExecute on the ICommand to return false when your condition isn't satisified.
In the setter for that IsSelected property, when the value changes, raise the CanExecuteChanged event.
The Command binding on a Button automatically enables/disables the button based on the result of CanExecute.
For more information on how to do this, including an implementation of ICommand that you could use here, see this mini-MVVM tutorial I wrote up for another question.
To fill out the implementaiton of CanExecute, I'd use something like Linq's .Any method. Then you don't have to bother checking Count, and can terminate the loop early if you find that any item is checked.
For example:
this.SaveCommand = new DelegateCommand(Save, CanSave);
// ...
private void Save(object unusedArg)
{
// Todo: Implement
}
private bool CanSave(object unusedArg)
{
return SelectableThings.Any(t => t.IsSelected);
}
Or since it is short, use a lambda inline:
this.SaveCommand = new DelegateCommand(Save,
o => SelectableThings.Any(t => t.IsSelected)
);