访问按钮 单击 Silverlight 模板化控件上的列表框数据模板中的按钮

发布于 2024-10-31 21:21:14 字数 2651 浏览 1 评论 0原文

我有一个 Silverlight 模板控件(不是用户控件),其中包含一个列表框。

在列表框的数据模板中,我有一个按钮,如下所示:

                        <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!
    }

我希望它是一些小东西。

谢谢人们!

凯文

I have a Silverlight Templated Control (not a user control), which contains a ListBox.

In the DataTemplate of the ListBox i have a Button, like so:

                        <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>

See the button there at the end of the template? HOW CAN I ACCESS IT'S CLICK EVENT? I can't use the GetTemplateChild() method since the button is part of the DataTemplate. I've tried Commanding (as you can see above). Seems like that's the way to go, although the Templated Control isn't exactly MVVM.

Any ideas? Maybe something other than Commanding? or else I'm doing the commanding wrong?

here's some relevant code:

...the Dependency Property / Property definitions... (should it be a 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
        }
    }

... in OnApplyTemplate()...

    public override void OnApplyTemplate()
    {
        ....
        DeleteCommand = new DelegateCommand(RemoveItemFromList, CanRemove);
        ....
        base.OnApplyTemplate();
    }

...the ICommand Action...

    private void RemoveItemFromList(object commandParameter)
    {
        //NEVER GETTING HERE!
    }

I hope it's something small.

Thanks people!

Kevin

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

与之呼应 2024-11-07 21:21:14

我已将命令作为属性添加到绑定到 ListBoxes(和其他 ItemsControl)的 ItemSource 中的对象的类中。这确实意味着我必须更改我的“数据”对象来处理 GUI 事件 - 这通常看起来是错误的和黑客的。

我还派生了 ItemsControl(但由于列表框是 ItemsControl,这可能仍然适用)。我将自己的属性添加到我最终想要从项目访问的派生控件中。在您的情况下是按钮命令处理程序。设置这些属性应该很容易,因为它们没有锁定在该嵌套模板中。

接下来,我在该派生类中重写了 GetContainerForItemOverride() 并返回另一个类,即我自己的派生 ContentPresenter。这个新的 ContentPresenter 也应该具有相同的命令属性 - 在构造它时将其设置为与 GetContainerForItemOverride 中的 ItemControl 的命令相同。

现在,在 DataTemplate 中使用 TemplateBinding(不是常规的 Binding)来访问该命令。

我已经讨论了尝试制作所有这些的通用/可重用版本的项目。

编辑,基本示例:

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 实例进行进一步的初始化 - 如果您想要执行的操作取决于它所绑定到的对象。

I've added a command as a property to the class of the objects I bind into ListBoxes's (and other ItemsControl's) ItemSource. This does mean I have to change my "data" objects to handle GUI events - which often seemed wrong and hacky.

I've also derived ItemsControl (but since a listbox is an ItemsControl this may still apply). I add my own properties the derived control that I'll ultimately want to access from the items. In your case the button command handler. It should be easy to set these properties since they aren't locked-up in that nested template.

Next, I overrided GetContainerForItemOverride() in that derived class and return another class, my own derived ContentPresenter. This new ContentPresenter should also have that same command property - set it equal to ItemControl's command in GetContainerForItemOverride when you construct it.

Now in the DataTemplate use TemplateBinding (not regular Binding) to get to that Command.

I've kicked around the item of trying to make a generic/reusable version of all of this.

Edit, basic example :

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.
   }

Edit again:

I've also put code in ItemsControl.PrepareContainerForItemOverride. This method gives you both the ContentControl (your own one if you're overriding GetContainerForItemOverride) and the current "Item" in the list. In here you can also do further initialization of the ContentControl instance - if what you want to do depends on the object that it's being bound to.

半﹌身腐败 2024-11-07 21:21:14

我建议您使用单个中继命令:

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" />

这将执行的操作是每次单击按钮时,它都会调用相同的删除命令,但会将当前项目作为参数传递。

希望这有帮助

I suggest you use a single 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" />

what this will do is everytime you click on the button, it will invoke the same deletecommand, but will pass the current item as parameter.

Hope this helps

2024-11-07 21:21:14

我在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;

I've come across this idea in MSDN, I have not tried it but I figured it was worth sharing here:

The DataContext of the items in the list box is not the same as the views DataContext. Each item's DataContext refers to an item in the collection that is bound to the list box's ItemsSource property.

A solution is to bind the command property to a static resource and set the value of the static resource to the command you want to bind. This is illustrated in the following XAML from the Stock Trader RI.

<!--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}" />

Then in the code-behind of the view, you must specify that the value of the resource actually points to the command on the presentation model. The following is an example of this from the Stock Trader RI, where the BuyCommand property on the presentation model is put in the resources.

((ObservableCommand)this.Resources["BuyCommand"]).Value = value != null ? value.BuyCommand : null;
芸娘子的小脾气 2024-11-07 21:21:14

您好,您可以使用相对源和 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}}}"/>

Hi you can use relative source and AncesterType. Then its works fine for me.

Refer the below code.

<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}}}"/>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文