WPF MVVM Button Control Binding in DataTemplate
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*" />
开发者_StackOverflow社区 </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?
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}" />
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>
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.
精彩评论