在Silverlight模板化控件上访问ListBox DataTemplate中的按钮单击按钮

时间:2011-04-13 14:09:50

标签: silverlight controls command

我有一个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!
    }

我希望它有点小。

谢谢大家!

凯文

4 个答案:

答案 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}}}"/>
相关问题