views:

245

answers:

3

I've been using MVVM's RelayCommand with success to bind actions to XAML, but I'm having a small problem with my ItemsControl.

    <ItemsControl ItemsSource="{Binding Devices}" >
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Grid Width="100" Margin="4" >
                    <Button Command="{Binding Path=SelectDeviceCommand}" >
                        <Grid>
                            <Image Source="img_small.png"></Image>
                            <Image Source="{Binding Path=Logo}" />
                        </Grid>
                    </Button>
                </Grid>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

In my view model:

    public RelayCommand SelectDeviceCommand { get; set; }
    private ObservableCollection<Device> Devices;

    Devices = CreateListOfDevices();

    private void InitializeCommands()
    {
        SelectDeviceCommand = new RelayCommand((s) => MessageBox.Show(s.ToString()));
    }

How do I define my SelectDeviceCommand in my view model in order to receive object that is bound to that item?

My SelectDeviceCommand is not even being called... (but that I guess is because I need to make my Device a mini-viewmodel and implement the SelectDeviceCommand in it, is that correct?)

A: 

Yeah, I've struck this one. I've seen some people work around it with a custom "CommandReference" class that they add as a resource to the window, but I couldn't get that to work.

In the end, I used element binding back to the window (or page) itself, since the ViewModel is the DataContext of the window. First, give your window (or page) a name:

<Window ...
    x:Name="me" />

Then bind to the window's datacontext directly, like this:

<Button Command="{Binding DataContext.SelectDeviceCommand,ElementName=me}">

This has worked for me. It's messy, but I think it's pretty readable.

Matt Hamilton
+2  A: 

Hi,

If you use a ViewModelLocator like in MVVM Light applications, you can get a reference to the MainViewModel from within the DataTemplate with

<Button Command="{Binding Main.SelectDeviceCommand, Source={StaticResource Locator}}">

I find this way cleaner than the ElementName one, but of course it supposes that the Main property is available in the locator, and also that the MainviewModel is instantiated as a singleton. This is not always possible, obviously. In that case, I consider the ElementName workaround acceptable.

In WPF, you can also use a RelativeSource with Mode=FindAncestor, but I find it even messier ;)

Regarding the question "How do I define my SelectDeviceCommand in my view model in order to receive object that is bound to that item?", I am not 100% sure that I understand the question, but if you want to get the item (in this case a Device) that is represented by the DataTemplate, you should use the CommandParameter:

<Button Command="{Binding Main.SelectDeviceCommand, Source={StaticResource Locator}}"
        CommandParameter="{Binding}"}">

Cheers, Laurent

LBugnion
A: 

I have a usercontrol(x:Name="ControlClass") and it is inside a template, it doesnt work on xaml i called it like this

<Button Content="New1" Command="{Binding DataContext.NewTabCommand,ElementName=ControlClass}"/>

namespace Doit_Project.Modules.Tasks.ViewModels { [Export] public class WindoTabViewModel : Doit_ProjectViewModelBase { public WindoTabViewModel() {

    }

    private RelayCommand _newTabCommand;
    public RelayCommand NewTabCommand
    {
        get { return _newTabCommand ?? (_newTabCommand = new RelayCommand(OnNewTab)); }
    }

    public void OnNewTab()
    {

    }
}

}

John