3
votes

I've seen other questions that deal with this, but never any explicit code describing the fix. I can't get a button inside of my ItemTemplate to bind to ANY command anywhere. Very frustrating. I am a complete MVVM newbie, btw.

Here's my Window XAML.

<Window x:Class="RET.CMS.Printing.App.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:RET.CMS.Printing.App.ViewModel"
    Height="350" Width="525"
    WindowStartupLocation="CenterScreen"
    Title="{Binding Path=DisplayName}"
    >
<Window.Resources>
    <ResourceDictionary Source="MainWindowResources.xaml" />
</Window.Resources>
<Window.DataContext>
    <local:MainWindowViewModel />
</Window.DataContext>

<DockPanel Margin="10">
    <Grid Margin="10" DockPanel.Dock="Top">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*" />
            <ColumnDefinition Width="1*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
        </Grid.RowDefinitions>
        <Image Grid.Column="0" MaxHeight="75" MinHeight="25" HorizontalAlignment="Left"
                   Source="/RET.CMS.Printing.App;component/Resources/BarkleyREI%20%283%29.png" />
        <TextBlock Grid.Column="1" HorizontalAlignment="Right" 
                    Height="30" VerticalAlignment="Top"
                    Style="{StaticResource TBHyperlinkStyle}"
                    >
            Help
        </TextBlock>
    </Grid>

    <Border Padding="10" DockPanel.Dock="Left">
        <DockPanel>
            <Label Style="{StaticResource H1Style}" DockPanel.Dock="Top">YOUR PRINTERS</Label>
            <StackPanel Margin="10" DockPanel.Dock="Top">
                <Button Style="{StaticResource RegularButton}" HorizontalAlignment="Left" Command="{Binding RefreshPrintersCommand}">Refresh List</Button>
            </StackPanel>
                <ListBox ItemsSource="{Binding Printers}" DockPanel.Dock="Left">
                <ListBox.ItemContainerStyle>
                    <Style TargetType="ListBoxItem">
                        <Setter Property="HorizontalContentAlignment" Value="Stretch" />
                    </Style>
                </ListBox.ItemContainerStyle>
                    <ListBox.ItemTemplate>
                <DataTemplate>
                        <Border Padding="5">
                            <StackPanel>
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="*" />
                                    <ColumnDefinition Width="75" />
                                </Grid.ColumnDefinitions>
                                <Grid>
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="*" />
                                        <RowDefinition Height="*" />
                                    </Grid.RowDefinitions>
                                    <Grid Grid.Row="0">
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="*" />
                                            <ColumnDefinition Width="*" />
                                            <ColumnDefinition Width="50" />
                                        </Grid.ColumnDefinitions>
                                            <TextBlock Style="{StaticResource TBHyperlinkStyle}" Text="{Binding Printer.Name}" Grid.Column="0" Margin="2" />
                                            <TextBlock Text="{Binding Printer.Status}" Grid.Column="1" Margin="2"/>
                                        <Image Grid.Column="2" Margin="2"  />
                                    </Grid>
                                    <TextBlock Text="{Binding Printer.Debug}" Grid.Row="1"/>
                                </Grid>
                            <Button Grid.Column="1" Content="Pause"
                                    Command="{Binding Path=PausePrinterCommand}"
                                    ></Button>
                        </Grid>

                        </StackPanel>
                        </Border>
                    </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        </DockPanel>
    </Border>

</DockPanel>

And here's my ViewModel for it:

 public class MainWindowViewModel:ViewModelBase
{
    #region Fields
    private ObservableCollection<PrinterViewModel> _Printers = new ObservableCollection<PrinterViewModel>();
    #endregion Fields

    #region Properties
    public ObservableCollection<PrinterViewModel> Printers
    {
        get
        {
            if(_Printers==null)
                LoadPrinters();
            return _Printers;
        }
    }
    #endregion Properties

    #region Constructor
    public MainWindowViewModel()
    {
        base.DisplayName = Resources.MainWindowViewModel_DisplayName;

        //bind commands
        RefreshPrintersCommand = new RelayCommand(param =>this.LoadPrinters());
        PausePrinterCommand = new RelayCommand( param => this.PausePrinter(param));
    }
    #endregion Constructor
    #region Private Members

    private void LoadPrinters()
    {
        PrintersRepository pr = new PrintersRepository(Config.SettingsLoader.GetPrintServers());
        _Printers.Clear();
        pr.GetPrinters().ForEach(i =>
            _Printers.Add(new PrinterViewModel(i)));
        OnPropertyChanged("Printers");
    }
    private void PausePrinter(object printerFullName)
    {
        var p = _Printers.Where(i => i.Printer.FullName == printerFullName as string);
    }
    #endregion Private Members

    #region Commands
    public ICommand RefreshPrintersCommand
    {
        get;
        private set;
    }
    public ICommand PausePrinterCommand
    {
        get;
        private set;
    }

    #endregion Commands
}

Here's my PrinterViewModel:

 public class PrinterViewModel:ViewModelBase
{
    private Printer _Printer;

    private RelayCommand _PauseCommand;
    public PrinterViewModel(Printer p)
    {
        _Printer = p;

    }
    public Printer Printer { get { return _Printer; } }

    public RelayCommand PauseCommand
    {
        get
        {
            if (_PauseCommand == null)
                _PauseCommand = new RelayCommand(param=>_Printer.Pause());
            return _PauseCommand;
        }
    }

}

Please help! Why can't i get this?

3
The button i'm trying to bind is this button with the PausePrinterCommand in the DataTemplate... should've mentioned thatMicah
Looks like it should work. You need to learn how to debug binding issues better. Go to the Debug menu, hit Options and Settings, then select Output Window on the left, and under WPF Trace Settings switch Dependency Properties to All. Now debug your app. Watch the output window for binding errors. Note, with All set you'll get some false positives. But watch for errors that happen late that involve your button. You should get some info on exactly what's happening.user1228
Will-- really appreciate you pointing that out to me. Being new with this stuff, its nice to know what tricks more experienced WPF devs are using.Micah

3 Answers

8
votes

The DataContext for the Button inside your ItemTemplate is a PrinterViewModel object (which has the PauseCommand). However, you're trying to bind the PausePrinterCommand to it, which is a property of the MainWindowViewModel.

In order to make this work (i.e. execute the MainViewModel.PausePrinterCommand using a Button in your ItemTemplate), you'll have to somehow get the MainViewModel first.

One way to do this is to use a RelativeSource for your binding, find the Window, and use DataContext.PausePrinterCommand as the Binding path. Like this:

<Button Grid.Column="1" Content="Pause"
        Command="{Binding RelativeSource={RelativeSource Window},
                  Path=DataContext.PausePrinterCommand}" />
3
votes

use element binding

<Window x:Class="RET.CMS.Printing.App.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:RET.CMS.Printing.App.ViewModel"
    Height="350" Width="525"
    x:Name="rootWindow"
    WindowStartupLocation="CenterScreen"
    Title="{Binding Path=DisplayName}" >
       .
       .
       .
  <Button Grid.Column="1" Content="Pause" Command="{Binding ElementName=rootWindow, Path=DataContext.PausePrinterCommand}"></Button>
2
votes

The button in the datatemplate should bind to command PauseCommand since it's datacontext is PrinterViewModel and not MainWindowViewModel. If you look in the debug output VS will clearly tell you that.