Hannish
Hannish

Reputation: 1532

WPF MVVM: How to enable/disable buttons in ListBox if I'm using a DataTemplate

I have a WPF/MVVM app with a ListBox which displays data through a DataTemplate. I managed to change the selected item in the ListBox when pressing a button so the CommandParameter is linked to the ListBox's SelectedItem, but I cannot get the buttons to be enabled/disabled correctly in the same way. For example, if I have 2 items and the button should be enabled in one and disabled in the other, when I select an element BOTH buttons have the same state, and they BOTH change state when I select another item.

I am using a RelayCommand as used in many MVVM Frameworks.

Here is my XAML (removed "not interesting" parts):

<UserControl.Resources>

    <DataTemplate x:Key="ItemTemplate">
            <Grid>
                <Button Content="Something" Name="EnabledDisabledButton" Click="Button_Click"
                            Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=DataContext.SomeCommand}"
                            CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBox}}, Path=SelectedItem}"/>
            </Grid>
    </DataTemplate>


    <Style TargetType="{x:Type ListBoxItem}" x:Key="ContainerStyle">
        <Setter Property="ContentTemplate" Value="{StaticResource ItemTemplate}" />
    </Style>

</UserControl.Resources>

   <ListBox x:Name="myListBox" ItemsSource="{Binding ElementList}"
             IsSynchronizedWithCurrentItem="True" ItemContainerStyle="{StaticResource ContainerStyle}"/>

I tried to pass the SelectedItem as a parameter to the RelayCommand's CanExecute method, but the result was the same as before.

Is there a way to pass the actual ListBoxItem in which the button "lives in" as a parameter to the command, so each one will be processed separately by the CanExecute method? Would it work if I got this? (right now I am handling the Click event to select the correct item in the list before executing the command).

In my CanExecute method I am evaluating some property of the SelectedItem in order to enable/disable the corresponding button. An alternative would be to evaluate this property for all elements, but I cannot think of a way to do it inside the ViewModel, and then communicate to the view the result (if it is even possible while using a DataTemplate for the items).

Thanks for your input, regards!

Upvotes: 0

Views: 2016

Answers (2)

Fede
Fede

Reputation: 44038

Converting My comment into an answer:

Why not just CommandParameter="{Binding}"?

Upvotes: 1

Mats Magnem
Mats Magnem

Reputation: 1405

You mention "MVVM" in the question, but it seems you use the MVVM way to your full advantage.

I would not have a Button_Click event in the style at all. That is because it is in fact a style, which per definition could be changed to another style which does not have the same event, which again will make the application stop working as wanted if you choose to have a style-based app in the future. A rule I use is that a style is a style. A style has to do with the UI and "looks" of the app. Functionality should be separate from the UI. The programmer can define the Command, and the designer can decide how the user will use that in the best way.

That's exactly where the code separation from the MVVM pattern cames into grip. To separate the "looks" and user behavior and the app's logic. Like...it should not matter to the model if a command fires from a button, a menu, a datacontext or a key stroke.

If this particular problem was handled to ME, I would solve it by having a HOLDER-class. This is a class (DependencyObject which implements INotifyPropertyChanged) that holds a ICommand property as well as the "row" that will be displayed in the various rows in the ListBox.

The ICommand property will be bound to the Button, having the row (class) itself as CommandParameter to the call. Then the actual row would be used in the ItemTemplate on the ListBox, with Bindings to different elements (proprty with or withouy Converters) to make whatever desired display available.

I hope I explained good enough... Feel free to ask more if you want more details to my solution alternative.

Upvotes: 1

Related Questions