-1

I found some silimar questions, but these are not exactly what i need. I want to bound stackpanel "IsEnabled" value to bool "!IsIterrupted" value of my Items. Here is my XAML file:

<ListView ItemsSource="{Binding Path=Items}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <StackPanel IsEnabled="{Binding !IsInterrupted, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
                    <Button Command="{Binding Path=StopThreadCommand, Source={StaticResource viewModel}}" CommandParameter="{Binding Id}"/>
                </StackPanel>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

This is how items looks like:

public class ThreadDecorator : BaseThread , INotifyPropertyChanged
{
    ...
    public event PropertyChangedEventHandler PropertyChanged;

    private bool _is_interrupted;
    public bool IsInterrupted
    {
        get { return _is_interrupted; }
        set
        {
            _is_interrupted = value;
            OnPropertyChanged("IsInterrupted");
        }
    }

    protected virtual void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    ...
}

And my ViewModel:

public class ThreadsViewModel : DependencyObject
{

    private ThreadsModel _model;
    public ThreadsModel Model
    {
        get { return _model; }
        set
        {
            _model = value;
        }
    }

    public ICollectionView Items
    {
        get { return (ICollectionView)GetValue(ItemsProperty); }
        set { SetValue(ItemsProperty, value); }
    }

    public static readonly DependencyProperty ItemsProperty =
        DependencyProperty.Register("Items", typeof(ICollectionView), typeof(ThreadsViewModel), new PropertyMetadata(null));

    public StopThreadCommand StopThreadCommand { get; set; }

    public ThreadsViewModel()
    {
        this.Model = new ThreadsModel();
        Items = CollectionViewSource.GetDefaultView(Model.Threads);
        this.StopThreadCommand = new StopThreadCommand(this);
    }

    public void InterruptThread(int id)
    {
        _model.InterruptThread(id);
    }
}

StopThreadCommand:

public class StopThreadCommand : ICommand
{
    public ThreadsViewModel ViewModel {get; set;}
    public StopThreadCommand(ThreadsViewModel viewModel)
    {
        this.ViewModel = viewModel;
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        this.ViewModel.InterruptThread((int)parameter);
    }
}

When I am clicking on Stop button, IsInterrupted value is changing from false to true, and stackpanel have to become disabled, but UI does not update. Help please!

Alex Sappy
  • 13
  • 2
  • What is the data context of the stackpanel ? Do you see any binding errors when you perform this function? – Versatile Sep 22 '16 at 16:49
  • `!IsInterrupted`? You sure that `!` is going to work as you expect? `Binding.Path` is not an arbitrary C# expression. It's a string naming a property of the `DataContext` (or a property of one of its properties, if `Foo.Bar`). – 15ee8f99-57ff-4f92-890c-b56153 Sep 22 '16 at 16:55
  • Here are some solutions to binding to a reversed boolean: http://stackoverflow.com/a/1039681/424129 -- in fact one guy even wrote a `Binding` replacement that *does* let you negate booleans: http://stackoverflow.com/a/27324780/424129 – 15ee8f99-57ff-4f92-890c-b56153 Sep 22 '16 at 16:57

1 Answers1

1

The default property of Binding is Path, which is a path to a property/sub-property of the DataContext. It's not an arbitrary C# expression. So you're setting Binding.Path to "!IsInterrupted". !IsInterrupted won't evaluate to the boolean inverse of IsInterrupted; it won't evaluate to anything. It'll get you this in the debug output stream:

System.Windows.Data Error: 40 : BindingExpression path error: '!IsInterrupted' property not found on 'object' 'ThreadDecorator' blah blah blah

<StackPanel 
    IsEnabled="{Binding !IsInterrupted, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">

One way to do this is to write a boolean-inverse value converter (stolen verbatim from Chris Nicol's answer at the other end of that link):

[ValueConversion(typeof(bool), typeof(bool))]
public class InverseBooleanConverter: IValueConverter
{
    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter,
        System.Globalization.CultureInfo culture)
    {
        if (targetType != typeof(bool))
            throw new InvalidOperationException("The target must be a boolean");

        return !(bool)value;
    }

    public object ConvertBack(object value, Type targetType, object parameter,
        System.Globalization.CultureInfo culture)
    {
        throw new NotSupportedException();
    }

    #endregion
}

Usage:

<UserControl.Resources>
    <local:InverseBooleanConverter x:Key="InverseBooleanConverter" />
</UserControl.Resources>

<!-- stuff etc. -->

IsEnabled="{Binding Path=IsReadOnly, Converter={StaticResource InverseBooleanConverter}}"

You could also write a Style with a DataTrigger that sets IsEnabled to False if IsInterrupted is true.

Community
  • 1
  • 1