How to disable a button if no items are selected in a ListView

11,324

Solution 1

There are a few things wrong here.

  1. Precedence, if you set IsEnabled on the control itself the style will never be able to change it.

  2. ElementName, it's an ElementName, not a path, just one string that gives the name of one element. Everything beyond that goes into the Path.

  3. Style syntax, if you set a Style.TargetType you should not set the Setter.Property with a type prefix (although leaving it does not break the setter).

By the way, this alone is enough:

<Button IsEnabled="{Binding SelectedItems.Count, ElementName=lv}" ...

Solution 2

It's obvious that you aren't using Commanding (ICommand Interface). You should either use that (and preferably the Model-View-ViewModel architecture).

But, if you want to stick with code-behind and XAML:

<ListView SelectionChanged="AccountListView_SelectionChanged" ... />

private void AccountListView_SelectionChanged(Object sender, SelectionChangedEventArgs args)
{
    DebitButton.IsEnabled = (sender != null);

    //etc ...
}

More information on MVVM: http://msdn.microsoft.com/en-us/magazine/dd419663.aspx

You need to set the DataContext of the View (UserControl) to the instance of the ViewModel you want to use. Then, from there, you can bind to properties on the ViewModel, including ICommands. You can either use RelayCommand (see link above) or use Commanding provided by a framework (for example, Prism provides a DelegateCommand). These commands take an Action (Execute) and a Func (CanExecute). Simply provide the logic in your CanExecute. Of course, you'd also have to have your ListView SelectedItem (or SelectedValue) be databound to a property on your ViewModel so you can check to see if it's null within your CanExecute function.

Assuming you use RelayCommand you don't have to explicitly call the RaiseCanExecuteChanged of an ICommand.

public class MyViewModel : ViewModelBase //Implements INotifyPropertyChanged
{
    public MyViewModel()
    {
        DoSomethingCommand = new RelayCommand(DoSomething, CanDoSomething);
    }

    public ObservableCollection<Object> MyItems { get; set; }
    public Object SelectedItem { get; set; }

    public RelayCommand DoSomethingCommand { get; set; }


    public void DoSomething() { }

    public Boolean CanDoSomething() { return (SelectedItem != null); }
}
<ListView ItemsSource="{Binding MyItems}" SelectedItem="{Binding SelectedItem}" ... />
<Button Command="{Binding DoSomethingCommand}" ... />
Share:
11,324
Jonathan
Author by

Jonathan

Updated on June 05, 2022

Comments

  • Jonathan
    Jonathan almost 2 years

    I have a ListView Contained in a UserControl I would like to disabled a button when no items are selected in the UserControl, would it be the right way to do it? So far, it doesn't disable, it just stays enable all the way. I've included the xaml code.

    searchAccountUserControl is the UserControl name property in the xaml. And AccountListView is the ListView name property in the userControl xaml.

    <Button Content="Debit" IsEnabled="true" HorizontalAlignment="Left" Margin="18,175,0,0" Name="DebitButton" Width="128" Grid.Column="1" Height="32" VerticalAlignment="Top" Click="DebitButton_Click">
            <Button.Style>
                <Style TargetType="Button">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding ElementName=searchAccountUserControl.AccountListView, Path=SelectedValue}" Value="{x:Null}" >
                            <Setter Property="Button.IsEnabled" Value="false"/>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Button.Style>
        </Button>
    

    Thanks.

    Finally i've used :

    in my ViewModel :

    private bool _isSelected;
    public bool IsSelected { get { return _isSelected; } 
    set { _isSelected = _account.View.CurrentItem != null;       
    PropertyChanged.SetPropertyAndRaiseEvent(this, ref _isSelected, value,  
    ReflectionUtility.GetPropertyName(() => IsSelected)); } } 
    

    And then Use isEnabled = "{Binding Path=IsSelected}" in the xaml.

  • myermian
    myermian over 12 years
    good points... I completely ignored his xaml-only approach and went with a MVVM lecture + XAML/Code-Behind mix (he's using this mix already).
  • Jonathan
    Jonathan over 12 years
    I am actually using a Model View ViewModel architecture. I just didnt know how to do it so and searched on the internet for a clue.
  • Jonathan
    Jonathan over 12 years
    My Xaml uses a baseWindowViewModel And i'm also using Binding Path. But as said i didnt know how to do it with the Binding so i tried to find a way with the code Behind, Altought i would much rather keep it straight with my ViewModel approach.
  • myermian
    myermian over 12 years
    What framework are you using (if any)? And using events is not MVVM ... binding (including command binding) is MVVM.
  • Jonathan
    Jonathan over 12 years
    So i guess i could use <Button IsEnabled="{Binding CollectionView.View.CurrentItem}"> and make a converter?
  • H.B.
    H.B. over 12 years
    @Jonathan: A trigger would work too if you watch precedence and fix the binding, but if you can bind directly to the ListView you can do the binding as shown above which will result in a conversion of the count to bool: 0 selected -> false = disabled; 1 or more selected -> true = enabled.
  • Jonathan
    Jonathan over 12 years
    Yes i know i'm talking about other parts of the code. With A viewModel and {Binding Path=... Converter ect...}
  • Jonathan
    Jonathan over 12 years
    @Hb : Since the listView is in the userControl, i dont know if i can bind it directly, I dont know what would be the best approach as i said, to stay with the pattern and keep the code clean. Can i do something like <Button IsEnabled="{Binding Usercontrol.ListView.SelectedItems.Count, ElementName=lv}"
  • myermian
    myermian over 12 years
    @Jonathan: The reason I assumed you weren't using MVVM is because you have named XAML controls (unnecessary) and you have a Click event on your button (unnecessary).
  • H.B.
    H.B. over 12 years
    That was an example, in your case you probably want the ElementName to point to the UserControl, you can expose the ListView with a public get-only property on the UserControl then you can bind to it, or you ceate a readonly bindable property which internally binds to the count, then you expose less of your UserControl.
  • Jonathan
    Jonathan over 12 years
    Ok, yes we are, all our viewModels implements INotifyPropertyChanged I guess we are just not using the RelayCommands.
  • myermian
    myermian over 12 years
    @Jonathan: I realize you state you are using MVVM. I said I assumed you weren't because your sample code had conventions that go against MVVM, for exmaple: Naming your XAML controls and using the Click event instead of Command Binding.
  • Jonathan
    Jonathan over 12 years
    Yeah, sorry :) We are not fully using it. Yet.
  • myermian
    myermian over 12 years
    @Jonathan: Well I hope that my samples and links help you get on the right track. Seriously look at using existing frameworks because they come with samples, documentation, and a lot of great code. Personally I use Prism, but that might be a bit advanced to start with.
  • Jonathan
    Jonathan over 12 years
    Finally i've added in my ViewModel : private bool _isSelected; public bool IsSelected { get { return _isSelected; } set { _isSelected = _account.View.CurrentItem != null; PropertyChanged.SetPropertyAndRaiseEvent(this, ref _isSelected, value, ReflectionUtility.GetPropertyName(() => IsSelected)); } } And then Use isEnabled = "{Binding Path=IsSelected}"
  • Jonathan
    Jonathan over 12 years
    Finally i've added in my ViewModel : private bool _isSelected; public bool IsSelected { get { return _isSelected; } set { _isSelected = _account.View.CurrentItem != null; PropertyChanged.SetPropertyAndRaiseEvent(this, ref _isSelected, value, ReflectionUtility.GetPropertyName(() => IsSelected)); } } And then Use isEnabled = "{Binding Path=IsSelected}" And i will look toward Prism. Thanks.
  • Jonathan
    Jonathan over 12 years
    We are also using NHibernate and sessions so i don't know if it can become an issue with full MMVM
  • cd491415
    cd491415 over 5 years
    ElementName does not exist in Xamarin.forms. This works only in WPF I believe