我有一个Silverlight模板控件(不是用户控件),它包含一个ListBox。
在ListBox的DataTemplate中我有一个Button,如下所示:
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ProgressBar Grid.Column="0" Width="70" Height="20" Value="{Binding Path=Percentage}" Minimum="0.0" Maximum="100.0" />
<TextBlock Grid.Column="0" Text="{Binding Path=Percentage, StringFormat='{}{0:##0.0}%'}" Margin="10,3,3,3" HorizontalAlignment="Center" />
<TextBlock Grid.Column="1" Text="{Binding Path=File.Name}" Margin="3" />
<Button Grid.Column="2" Content="Remove" x:Name="RemoveButton" Command="{TemplateBinding DeleteCommand}" Style="{TemplateBinding UploadButtonStyle}" HorizontalAlignment="Right" Margin="0,0,5,0" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
请参阅模板末尾的按钮?我如何访问它的点击事件?我不能使用GetTemplateChild()方法,因为该按钮是DataTemplate的一部分。我试过命令(正如你在上面看到的那样)。虽然模板控制并不完全是MVVM,但这似乎是要走的路。
有什么想法吗?也许除了指挥之外的东西?或者我在做指挥错误?
这是一些相关的代码:
......依赖属性/属性定义......(它应该是Dep Prop吗?)
public static readonly DependencyProperty DeleteCommandProperty =
DependencyProperty.Register("DeleteCommand", typeof(ICommand), typeof(MultipleFileUpload), new PropertyMetadata(null));
public ICommand DeleteCommand
{
get { return (ICommand)GetValue(DeleteCommandProperty); }
set
{
SetValue(DeleteCommandProperty, value);
FirePropertyChanged("DeleteCommand"); //INotifyPropertyChanged stuff
}
}
...在OnApplyTemplate()...
中 public override void OnApplyTemplate()
{
....
DeleteCommand = new DelegateCommand(RemoveItemFromList, CanRemove);
....
base.OnApplyTemplate();
}
...... ICommand行动......
private void RemoveItemFromList(object commandParameter)
{
//NEVER GETTING HERE!
}
我希望它有点小。
谢谢大家!
凯文
答案 0 :(得分:1)
我建议你使用一个relaycommand:
public class RelayCommand<T> : ICommand
{
#region Fields
readonly Action<T> _execute = null;
readonly Predicate<T> _canExecute = null;
#endregion // Fields
#region Constructors
public RelayCommand(Action<T> execute)
: this(execute, null)
{
}
/// <summary>
/// Creates a new command.
/// </summary>
/// <param name="execute">The execution logic.</param>
/// <param name="canExecute">The execution status logic.</param>
public RelayCommand(Action<T> execute, Predicate<T> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute((T)parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_execute((T)parameter);
}
#endregion // ICommand Members
}
XAML:
<Button Grid.Column="2" Content="Remove" x:Name="RemoveButton" Command="{Binding DeleteCommand}" CommandParameter={Binding} Style="{TemplateBinding UploadButtonStyle}" HorizontalAlignment="Right" Margin="0,0,5,0" />
这样做的是每次点击按钮时,它都会调用相同的delete命令,但会将当前项目作为参数传递。
希望这有帮助
答案 1 :(得分:1)
我已经将一个命令作为属性添加到我绑定到ListBoxes(和其他ItemsControl的)ItemSource的对象的类中。这意味着我必须更改我的“数据”对象来处理GUI事件 - 这通常看起来是错误的和hacky。
我还派生了ItemsControl(但由于列表框是ItemsControl,因此可能仍然适用)。我添加自己的属性,我最终想要从项目中访问的派生控件。在你的情况下按钮命令处理程序。设置这些属性应该很容易,因为它们没有锁定在该嵌套模板中。
接下来,我在该派生类中重写了GetContainerForItemOverride()并返回另一个类,即我自己的派生ContentPresenter。这个新的ContentPresenter也应该具有相同的命令属性 - 在构造它时,将它设置为GetContainerForItemOverride中的ItemControl命令。
现在在DataTemplate中使用TemplateBinding(不是常规绑定)来获取该命令。
我已经开始尝试制作所有这些的通用/可重用版本。
编辑,基本示例:
class MyItemsControl : ItemsControl
{
public Command MyCommand {get;set;} // I've often use a full-blown DP here
snip
protected override DependencyObject GetContainerForItemOverride()
{
return new MyContentPresenter(this.MyCommand); // MyContentPresenter is just a derived ContentPresenter with that same property.
}
再次编辑:
我还将代码放在ItemsControl.PrepareContainerForItemOverride中。此方法为您提供ContentControl(如果您覆盖GetContainerForItemOverride,则为您自己的一个)和列表中的当前“Item”。在这里,您还可以进一步初始化ContentControl实例 - 如果您要执行的操作取决于它所绑定的对象。
答案 2 :(得分:1)
我遇到了this idea in MSDN,我还没有尝试过,但我认为值得在这里分享:
列表框中项目的DataContext与视图DataContext不同。每个项目的DataContext引用集合中绑定到列表框的ItemsSource属性的项目。
解决方案是将命令属性绑定到静态资源,并将静态资源的值设置为要绑定的命令。这在Stock Trader RI的以下XAML中有说明。
<!--Specifying the observablecommand in the view's resources-->
<UserControl.Resources>
<Infrastructure:ObservableCommand x:Key="BuyCommand" />
</UserControl.Resources>
<!—Binding the Button Click to the command. This control can sit inside a datagrid or a list box. -->
<Button Commands:Click.Command="{Binding Path=Value, Source={StaticResource BuyCommand}}" Commands:Click.CommandParameter="{Binding Path=TickerSymbol}" />
然后在视图的代码隐藏中,您必须指定资源的值实际指向表示模型上的命令。以下是Stock Trader RI的示例,其中演示模型上的BuyCommand属性放在资源中。
((ObservableCommand)this.Resources["BuyCommand"]).Value = value != null ? value.BuyCommand : null;
答案 3 :(得分:0)
您好,您可以使用相对源和AncesterType。然后它对我来说很好。
请参阅以下代码。
<Button Content="Delete" Command="{Binding DataContext.DeleteCommand,
RelativeSource= {RelativeSource FindAncestor, AncestorType={x:Type ListBox}}}"
CommandParameter="{Binding Path=SelectedItem, RelativeSource= {RelativeSource FindAncestor, AncestorType=
{x:Type ListBox}}}"/>