Casey Crookston
Casey Crookston

Reputation: 13945

Disable Buttons in a ListView's DataTemplate

So let's say I have a simple little WPF app that looks like this:

-------------------------------
Amount Left: 1,000
[Subtract 1]
[Subtract 5]
[Subtract 15]
[Subtract 30]
-------------------------------

"Amount Left:" and "1,000" are stand alone TextBlocks. The "Subtract x" are all buttons, inside a ListView, inside a DataTemplate. Each time a button is clicked, the amount of the button is subtracted from the 1,000. All of that I have working.

Here's what I can't figure out. When the Amount Left falls below 30, the last button needs to become disabled. When the amount falls below 15, the second to last button becomes disabled. Etc and so on, until the Amount Left is Zero and all buttons are disabled. I can not figure out how to disable the buttons.

This example I'm giving here is not exactly what I'm trying to do, but it's a greatly simplified example that will make this post a lot shorter and simpler. Here, in essence, is what I have now.

XAML:

<DockPanel>
    <TextBlock Text="Amount Left:" />
    <TextBlock x:Name="AmountLeft" Text="1,000.00" />
</DockPanel>
<DockPanel>
    <ListBox x:Name="AuthorListBox">
        <ListView.ItemTemplate>
            <DataTemplate>
                <Button x:Name="SubButtom" Content="{Binding SubtractAmount}" Click="clickSubtract" />
            <DataTemplate>
    </ListBox>
</DockPanel>

XAML.cs

    private void clickSubtract(object sender, RoutedEventArgs e)
    {
        Button button = sender as Button;
         Int32 SubtractAmount = ((Data.AppInformation)button.DataContext).SubtractAmount;  // This is the amount to be subtracted
        // logic to update the amount remaining.  This works.
        //  What I need to figure out is how to disable the buttons
    }

Upvotes: 2

Views: 2139

Answers (3)

Abin
Abin

Reputation: 2956

I would go ahead creating a Converter and bind the IsEnabled property of the button. pass the value and do the logic.

Namespace

System.Windows.Data

System.Globalization

CODE

 public class IsEnabledConverter : IValueConverter
 {
  public object Convert(object value, Type targetType, 
    object parameter, CultureInfo culture)
  {
    // Do the logic
  }

    public object ConvertBack(object value, Type targetType, 
    object parameter, CultureInfo culture)
   {
    // Do the Logic
   }
}

XAML

Add the resurce like this

<Window.Resources>
    <local:IsEnabledConverter  x:Key="converter" />
</Window.Resources>

<Button x:Name="SubButtom" IsEnabled="{Binding Value, Converter=   {StaticResource  converter}}" Content="{Binding SubtractAmount}" Click="clickSubtract" />

You can learn about converters from below link

http://wpftutorial.net/ValueConverters.html

When you build the class with Converter all Xaml Errors will go off.

Upvotes: 1

Glen Thomas
Glen Thomas

Reputation: 10744

Your best option would be to use a command on the viewmodel instead of a click event handler:

public ICommand SubtractCommand = new DelegateCommand<int>(Subtract, i => i <= AmountLeft);

private void Subtract(int amount)
{
    AmountLeft = AmountLeft - amount;
}

XAML:

<ListBox x:Name="AuthorListBox">
    <ListBox.ItemTemplate>
        <DataTemplate>
                <Button x:Name="SubButtom" Content="{Binding SubtractAmount}"
                        Command="{Binding SubtractCommand, RelativeSource={RelativeSource AncestorType=DataGrid}}"
                        CommandParameter="{Binding SubtractAmount}"/>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Upvotes: 0

d.moncada
d.moncada

Reputation: 17392

You can accomplish using MVVM, by having an IsEnabled property for your Button ViewModels. With this approach, you will not need any 'code behind' as you currently have using a click event handler.

Xaml:

<Grid>
    <StackPanel>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="Amount Left:" />
            <TextBlock Text="{Binding CurrentAmount}" />
        </StackPanel>
        <ListBox ItemsSource="{Binding Buttons}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Button Command="{Binding SubtractCommand}" Width="200" Height="75" x:Name="SubButtom" Content="{Binding SubtractAmount}"  IsEnabled="{Binding IsEnabled}"/>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </StackPanel>
</Grid>

Xaml.cs:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new ViewModel();
    }
}

We want the main ViewModel that will have a list of Button ViewModels.

ButtonViewModel.cs:

namespace WpfApplication1
{
    public class ButtonViewModel : INotifyPropertyChanged
    {
        private bool _isEnabled;
        private ViewModel _viewModel;

        public bool IsEnabled
        {
            get { return _isEnabled; }
            set
            {
                _isEnabled = value;
                OnPropertyChanged();
            }
        }

        public int SubtractAmount { get; set; }

        public ICommand SubtractCommand { get; private set; }

        public ButtonViewModel(ViewModel viewModel)
        {
            _viewModel = viewModel;
            IsEnabled = true;

            SubtractCommand = new CommandHandler(() =>
            {
                _viewModel.CurrentAmount -= SubtractAmount;
            }, true);
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public class CommandHandler : ICommand
    {
        private readonly Action _action;
        private readonly bool _canExecute;

        public CommandHandler(Action action, bool canExecute)
        {
            _action = action;
            _canExecute = canExecute;
        }

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

        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)
        {
            _action();
        }
    }
}

and now the main ViewModel.

ViewModel.cs:

namespace WpfApplication1
{
    public class ViewModel : INotifyPropertyChanged
    {
        private int _currentAmount;

        public int CurrentAmount
        {
            get { return _currentAmount; }
            set
            {
                _currentAmount = value;
                OnPropertyChanged();

                if (Buttons != null)
                {
                    foreach (var button in Buttons)
                    {
                        if ((value - button.SubtractAmount) <= 0)
                        {
                            button.IsEnabled = false;
                        }
                    }
                }
            }
        }
        public List<ButtonViewModel> Buttons { get; private set; }

        public ViewModel()
        {
            CurrentAmount = 1000;

            Buttons = new List<ButtonViewModel>
            {
                new ButtonViewModel(this)
                {
                    SubtractAmount = 1
                },
                new ButtonViewModel(this)
                {
                    SubtractAmount = 5
                },
                new ButtonViewModel(this)
                {
                    SubtractAmount = 15
                },
                new ButtonViewModel(this)
                {
                    SubtractAmount = 30
                }
            };
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

As you can see, each Button ViewModel will decrement the CurrentAmount using a Command (the preferred method over a click event). Whenever the CurrentAmount is changed, some simple logic is done by the main ViewModel that will disable associated buttons.

This is tested and works. Let me know if you have any questions.

Upvotes: 1

Related Questions