1

在我的 WPF 应用程序中有一个包含项目的列表框。列表框通过 XAML 中的 xmldataprovider 填充,然后将其绑定到列表框的 Itemssource 属性。

好吧,从 XAML,我通过执行以下操作将命令绑定到列表框:

                      <ListBox.CommandBindings>
                          <CommandBinding 
                              Command="{x:Static local:mainApp.MyCmd}" 
                              CanExecute="CanExecute"
                              Executed ="Executed" />
                      </ListBox.CommandBindings>

但我不知道如何以编程方式将命令绑定到每个列表框项。怎么做?

提前致谢。


首先很抱歉,没有将其作为评论发布。我不能把这一切都放在评论中。

好的,是的,我没有使用 ICommandSource 的 Executed 和 CanExecute 属性,尽管我已经在自定义类中注册并实现了它们(在 xaml 中它们也被注释了)。我已经在 routedCommand 但没有在自定义类中指定它们,我已经在窗口的构造函数中这样做了:

WinMain 后面的代码:

public WinMain()
{
   InitializeComponent();

   // Command binding. If I don't do this Executed and CanExecute are not executed
   CommandBindings.Add(new CommandBinding(rcmd, 
      CommandBinding_Executed, CommandBinding_CanExecute));
}

然后我也在后面的 WinMain 代码中实现这些方法:

// ExecutedRoutedEventHandler
private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
   // Do stuff

}

// CanExecuteRoutedEventHandler
private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{

    // cBgWorkers is a class that check if a background worker is running
    e.CanExecute = !cBgWorkers.isRunning;

    //if (LayoutListBox != null) LayoutListBox.IsEnabled = !cBgWorkers.isRunning;
}

在 WinMain XAML 中,我调用如下命令:

<Classes:CommandListBox x:Name="LayoutListBox"
 Command="{x:Static local:WinMain.rcmd}"
  ... >

 <...>

 </Classes:CommandListBox>

在我的自定义类 CommandListBox 中,我有一个 CanExecuteChanged ,您可以在其中看到我启用或禁用了控件,具体取决于后台工作人员是否完成:

private void CanExecuteChanged(object sender, EventArgs e)
{
    this.Enabled = !cBgWorkers.isRunning;
}

但是在自定义类中,我没有实现你说的事件处理程序 OnSelected。

如果没有实现一切正常,自定义控件调用命令,并且到达 CanExecute 方法,并且 CanExecute 获得正确的值,取决于后台工作人员是否完成,并且当 CanExecute 更改其值时引发自定义控件中的 CanExecuteChanged . 当后台工作人员启动时,它会被禁用,但完成后它不会被启用。我已经调试过,当后台工作人员完成时,我可以看到 CanExecuteChanged 被执行并且 this.Enabled 正在获得正确的值(true),但由于某种原因,在 UI 中,尽管它获得了正确的值并且尽管在 RunWOrkerCompleted(在后台worker) 我强制使用 CommandManager.InvalidateRequerySuggested() 更新 UI。

我通过取消注释行解决了这个问题:

if (LayoutListBox != null) LayoutListBox.IsEnabled = !cBgWorkers.isRunning;

在 CanExecute 方法中。我不明白会发生什么。

那么如果我按照你说的做就没有必要了:

   CommandBindings.Add(new CommandBinding(rcmd, 
      CommandBinding_Executed, CommandBinding_CanExecute));

和 CommandBinding_Executed & CommandBinding_CanExecute 实现。我对吗?

但是如果我删除这些方法,我可以在哪里设置 this.enabled = !cBgWorkers.isRunning ?

我希望 WPF 为我的自定义控件自动设置 isEnabled 属性。这该怎么做?

提前致谢。


我正在应用您所说的有关附加行为的文章,并进行了一些更改以使其适应我的 ListBox。它效果不佳,或者我做错了什么。我想要的是避免在运行长任务(后台工作人员)时可以选择 ListBox 成员(listBoxItems)。所以我修改的文章的方法之一是:

    static void OnListBoxItemSelected(object sender, RoutedEventArgs e)
    {
        // Only react to the Selected event raised by the ListBoxItem
        // whose IsSelected property was modified.  Ignore all ancestors
        // who are merely reporting that a descendant's Selected fired.
        if (!Object.ReferenceEquals(sender, e.OriginalSource))
            return;

        ListBoxItem item = e.OriginalSource as ListBoxItem;
        if (item != null)
        {

            // (*) See comment under
            item.IsEnabled = !cBgWorkers.isRunning;
            if (!cBgWorkers.isRunning)
            {
                item.BringIntoView();
            }
        }
    }

(*) cBgWorkers 是一个具有一些方法和属性的公共静态类。属性之一是 isRunning,表示当前没有后台工作人员正在运行。然后如果没有后台工作人员正在运行,则必须启用列表框成员,否则必须禁用它们,因此当用户单击一个列表框项目时,当前页面不会更改为另一个,因为我之前禁用了它(每个列表框项目都附加了一个我的主应用程序中的页面)。

当其中一个后台工作人员(bw)或全部正在运行并且我选择列表框项目时一切正常:列表框项目被禁用,因为有 bw 正在运行,它避免将当前页面更改为另一个页面。当然,如果我禁用了列表框项目(或列表框项目),我将无法再次选择它,因为它已被禁用,这是我的问题,因为我希望当 bw 完成在 bw 运行时已禁用的列表框项目时,他们再次启用。不幸的是,正如我所见,附加行为并不是由 WPF 自动完成的,并且命令具有此优势(控件由 WPF 自动更新)。那么,如何分别在 bw 运行或不运行时禁用/重新启用列表框项目?

据我所知和所见,附加行为的一个优点是我认为它更有效,因为它们不会不断地调用动作(仅当动作,例如选择,产生时)。命令不断(不经常)检查是否可以执行绑定到控件的操作(因此如果可以执行,WPF 会自动启用控件,否则它们会显示为禁用),对吗?

谢谢。

4

5 回答 5

1

您可以尝试创建一个从 ListBoxItem 派生的自定义控件并实现 ICommandSource 接口。到目前为止,我想不出更简单的解决方案。

于 2010-03-31T01:35:23.550 回答
1

我已经完成了你的解决方案。我已经完成了一个从列表框派生的自定义用户控件并按照您所说的实现 ISourceCommand 并且它现在可以工作了!!!!;)

我的自定义类:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Input;

namespace GParts.Classes
{
public class CommandListBox : ListBox, ICommandSource
{
    public CommandListBox() : base()
    {

    }

    // ICommand Interface Members
    // Make Command a dependency property so it can use databinding.
    public static readonly DependencyProperty CommandProperty =
        DependencyProperty.Register(
            "Command",
            typeof(ICommand),
            typeof(CommandListBox),
            new PropertyMetadata((ICommand)null,
            new PropertyChangedCallback(CommandChanged)));

    public ICommand Command
    {
        get 
        {
            return (ICommand)GetValue(CommandProperty);
        }
        set 
        {
            SetValue(CommandProperty, value);
        }
    }

    // Make Command a dependency property so it can use databinding.
    public static readonly DependencyProperty ExecutedProperty =
        DependencyProperty.Register(
            "Executed",
            typeof(object),
            typeof(CommandListBox),
            new PropertyMetadata((object)null));

    public object Executed
    {
        get
        {
            return (object)GetValue(ExecutedProperty);
        }
        set
        {
            SetValue(ExecutedProperty, value);
        }
    }

    // Make Command a dependency property so it can use databinding.
    public static readonly DependencyProperty CanExecuteProperty =
        DependencyProperty.Register(
            "CanExecute",
            typeof(object),
            typeof(CommandListBox),
            new PropertyMetadata((object)null));

    public object CanExecute
    {
        get
        {
            return (object)GetValue(CanExecuteProperty);
        }
        set
        {
            SetValue(CanExecuteProperty, value);
        }
    }

    // Make CommandTarget a dependency property so it can use databinding.
    public static readonly DependencyProperty CommandTargetProperty =
        DependencyProperty.Register(
            "CommandTarget",
            typeof(IInputElement),
            typeof(CommandListBox),
            new PropertyMetadata((IInputElement)null));

    public IInputElement CommandTarget
    {
        get
        {
            return (IInputElement)GetValue(CommandTargetProperty);
        }
        set
        {
            SetValue(CommandTargetProperty, value);
        }
    }

    // Make CommandParameter a dependency property so it can use databinding.
    public static readonly DependencyProperty CommandParameterProperty =
        DependencyProperty.Register(
            "CommandParameter",
            typeof(object),
            typeof(CommandListBox),
            new PropertyMetadata((object)null));

    public object CommandParameter
    {
        get
        {
            return (object)GetValue(CommandParameterProperty);
        }
        set
        {
            SetValue(CommandParameterProperty, value);
        }
    }

    // Command dependency property change callback.
    private static void CommandChanged(DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        CommandListBox clb = (CommandListBox)d;
        clb.HookUpCommand((ICommand)e.OldValue,(ICommand)e.NewValue);
    }
    // Add a new command to the Command Property.
    private void HookUpCommand(ICommand oldCommand, ICommand newCommand)
    {
        // If oldCommand is not null, then we need to remove the handlers.
        if (oldCommand != null)
        {
            RemoveCommand(oldCommand, newCommand);
        }
        AddCommand(oldCommand, newCommand);
    }

    // Remove an old command from the Command Property.
    private void RemoveCommand(ICommand oldCommand, ICommand newCommand)
    {
        EventHandler handler = CanExecuteChanged;
        oldCommand.CanExecuteChanged -= handler;

        //newCommand.Execute(null);
        //newCommand.CanExecute(null);

    }

    // Add the command.
    private void AddCommand(ICommand oldCommand, ICommand newCommand)
    {
        EventHandler handler = new EventHandler(CanExecuteChanged);
        canExecuteChangedHandler = handler;
        if (newCommand != null)
        {
            newCommand.CanExecuteChanged += canExecuteChangedHandler;

            //newCommand.Execute(Executed);
            //newCommand.CanExecute(CanExecute);
        }
    }
    private void CanExecuteChanged(object sender, EventArgs e)
    {

        if (this.Command != null)
        {
            RoutedCommand command = this.Command as RoutedCommand;

            // If a RoutedCommand.
            if (command != null)
            {
                if (command.CanExecute(CommandParameter, CommandTarget))
                {
                    this.IsEnabled = true;
                }
                else
                {
                    this.IsEnabled = false;
                }
            }
            // If a not RoutedCommand.
            else
            {
                if (Command.CanExecute(CommandParameter))
                {
                    this.IsEnabled = true;
                }
                else
                {
                    this.IsEnabled = false;
                }
            }
        }
    }

    // Keep a copy of the handler so it doesn't get garbage collected.
    private static EventHandler canExecuteChangedHandler;
}
}

在我的 WinMain.xaml 中:

    <Classes:CommandListBox x:Name="LayoutListBox"
     Command="{x:Static local:WinMain.rcmd}"

     <!-- These lines doesn't work I explain it following
     Executed="CommandBinding_Executed"
     CanExecute="CommandBinding_CanExecute" 
     -->

      ... >

     <...>

     </Classes:CommandListBox>

和后面的窗口代码:

    public WinMain()
    {
       InitializeComponent();

       // Command binding. If I don't do this Executed and CanExecute are not executed
       CommandBindings.Add(new CommandBinding(rcmd, 
          CommandBinding_Executed, CommandBinding_CanExecute));
    }

    public static RoutedCommand rcmd = new RoutedCommand();

    // ExecutedRoutedEventHandler
    private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
    {
       // Do stuff

    }

    // CanExecuteRoutedEventHandler
    private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {

        e.CanExecute = !cBgWorkers.isRunning;

        //if (LayoutListBox != null) LayoutListBox.IsEnabled = !cBgWorkers.isRunning;
    }

但我和另一个解决方案有同样的问题。如果我没有放置最后一行(此处出现在 CommandBinding_CanExecute 中的注释),则 wpf 不会在后台工作人员完成时自动启用列表框。如果我把这条线它工作。发生什么事?

另一件事,正如您在我的代码片段中看到的那样,我想做与使用按钮相同的操作,您可以在其中指示命令、已执行和可执行。我已经在课堂上注册了它们,并且在列表框中我检查了传递方法,但它没有用。我怎样才能做到这一点?

非常感谢。

于 2010-03-31T12:21:57.127 回答
0

看看附加行为

于 2010-03-31T01:45:43.863 回答
0

根据我发布的第一个问题,在列表框中使用 CommandBindings 不起作用。CanExecute 的实现是:

    private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {

        e.CanExecute = !cBgWorkers.isRunning;         

    }

通过执行 WPF 不会根据后台工作状态(运行与否)自动启用/禁用列表框控件,我不明白为什么,因为我有其他控件,例如带有绑定命令的按钮,而 WPF 会自动启用/禁用它们。

所以我做了以下修改:

    private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {

        e.CanExecute = !cBgWorkers.isRunning;

        if (LayoutListBox != null) LayoutListBox.IsEnabled = !cBgWorkers.isRunning;
    }

现在,它起作用了。当没有后台工作人员正在运行和禁用时启用列表框,否则我不喜欢的是放置在我手动启用/禁用列表框属性 isEnabled 的方法中的最后一行。它效率低下,所以我想仅在 CanExecute 更改其值时更改列表框的 isEnabled 属性。据我所知,有一个事件可以做到这一点,它是 CanExecuteChanged 但我不知道如何实现它。有任何想法吗?

现在,在尝试了几种解决方案之后,我正在实施 Mike 的解决方案,因为我认为它更简单、更清晰,并且只需进行一些更改即可重新用于其他控件。

于 2010-03-31T10:35:57.973 回答
0

我一直无法完成整个线程。它很长。无论如何,我以为你想在 ListBoxItem 上放一个命令?据我所知,您继承自 ListBox。您不需要指定 ICommandSource 的 Executed 和 CanExecute 属性。这应该在您的 RoutedCommand 中指定,而不是在您的自定义控件中。要执行您的命令,您需要在自定义控件中提供一个事件处理程序。例如,如果选择了一个项目,则执行该命令。这是一个例子。

protected override void OnSelected(RoutedEventArgs e)   
{
    base.OnSelected(e);

    if (this.Command != null)
    {
        RoutedCommand command = Command as RoutedCommand;

        if (command != null)
        {
            command.Execute(CommandParameter, CommandTarget);
        }
        else
        {
            ((ICommand)Command).Execute(CommandParameter);
        }
    }
}
于 2010-03-31T18:41:03.947 回答