I have the Following Collection: ObservableCollection<CheckedFileObject> Files
A CheckedFileObject contains a FileObject and a bool isChecked. Now when a user selects a File the isChecked Value of this file gets changed to true.
If at least one CheckedFileObject contains IsChecked = true, i want to make the Delete Button Visible.
So my Question is, is it possible to toggle the visibility based on an item in an ObservableCollection and how? I doesn't matter how many items there are, but if one is checked the button should be visible.
Maybe something like:
Visibility="{Binding Files[i].IsChecked, Mode=OneWay, Converter={StaticResource BooleanAndToVisibilityConverter}}"
Or perhaps binding binding the ObservablCollection to a bool in the viewmodel which gets updated through something like this:
var isVisible = Files.Any(x => x.IsChecked == true);
when the collection changes. Is this even possible?
CodePudding user response:
you can get the feature of items tracking from ListCollectionView
class and change Button.Visibility based on item count in that collection view:
public ObservableCollection<CheckedFileObject> Files { get; } = new ObservableCollection<CheckedFileObject>();
private ICollectionView _filteredFiles;
public ICollectionView FilteredFiles
{
get
{
if (_filteredFiles == null)
{
_filteredFiles = new ListCollectionView(Files)
{
IsLiveFiltering = true,
LiveFilteringProperties = { nameof(CheckedFileObject.IsChecked) },
Filter = (o) => o is CheckedFileObject file && file.IsChecked
};
}
return _filteredFiles;
}
}
Items are displayed from the original collection. Button is bound to the filtered collection:
<ItemsControl ItemsSource="{Binding Files}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Path=IsChecked}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Content="Delete">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=FilteredFiles.Count}" Value="0">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
CheckedFileObject class must have property change notifications (implement INotifyPropertyChanged):
public class CheckedFileObject : INotifyPropertyChanged
{
private bool isChecked;
public bool IsChecked
{
get => isChecked;
set
{
isChecked = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsChecked)));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
CodePudding user response:
You could try with a converter like the following:
public class CheckedFileObjectToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is ObservableCollection<CheckedFileObject> files)
{
if (files.Any(x => x.IsChecked == true))
return Visibility.Visible;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
Note that if you have this control repeated multiple times in your view, this code might need to be improved in terms of performance.
CodePudding user response:
public class SomeVm
{
public SomeVm()
{
Files = new ObservableCollection<CheckedFileObject>();
Files.CollectionChanged = Files_CollectionChanged;
StartCommand = new DelegateCommand(ExecuteStart, () => Files.Any(f => f.IsChecked));
}
private void Files_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Remove || e.Action == NotifyCollectionChangedAction.Replace)
{
foreach (INotifyPropertyChanged checkedFileObject in e.OldItems)
{
checkedFileObject.PropertyChanged -= FileObject_PropertyChanged;
}
}
if (e.Action == NotifyCollectionChangedAction.Add || e.Action == NotifyCollectionChangedAction.Replace)
{
foreach (INotifyPropertyChanged checkedFileObject in e.NewItems)
{
checkedFileObject.PropertyChanged = FileObject_PropertyChanged;
}
}
}
private void FileObject_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(CheckedFileObject.IsChecked))
StartCommand.RaiseCanExecuteChanged();
}
public ObservableCollection<CheckedFileObject> Files { get; }
public DelegateCommand StartCommand { get; }
private void ExecuteStart() { throw new NotImplementedException(); }
}
public class CheckedFileObject : ObservableObject
{
public bool IsChecked { get; set; }
public FileInfo FileInfo { get; }
}
You could also implement it with multiselection