2010-03-30 67 views
1

在我的WPF应用程序中有一个包含项目的列表框。该列表框通过XAML中的xmldataprovider进行填充,然后将其绑定到列表框的Itemssource属性。以编程方式将命令添加到WPF中的列表框中

那么从XAML,我绑定一个COMAND到ListBox做:

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

,但我不知道如何将一个命令编程绑定到每个ListBoxItem的。怎么做?

在此先感谢。


先抱歉,不要将它作为评论发布。我无法将这一切置于评论中。

好吧,是的我没有使用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根据后台worker是否完成获取正确的值,true或false,并且自定义控件中的CanExecuteChanged在CanExecute更改其值。当后台工作者启动时,它会被禁用,但是当它完成时它不会被启用。我已经调试过了,当后台工作者完成后,我可以看到CanExecuteChanged被执行,并且this.Enabled获得了正确的值(true),但由于某种原因,在UI中,尽管获得了正确的值,并且尽管在RunWOrkerCompleted(在后台worker)我强制使用CommandManager.InvalidateRequerySuggested()更新UI。

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

如果(!LayoutListBox = NULL)LayoutListBox.IsEnabled = cBgWorkers.isRunning;

in 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自动控制更新)。那么,如何禁用/重新启用列表框项目时,分别运行或没有分别?

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

谢谢。

+0

这个问题可能会有帮助:http://stackoverflow.com/questions/845446/wpf-mvvm-commands-are-easy-how-to-connect-view-and-viewmodel-with-routedevent – 2010-07-13 16:23:11

回答

1

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

+0

呀!我喜欢你的想法,这将是一个简单的解决方案,如你所说。这样做的好处是我可以重用这个用户控件用于其他场合。我会尝试你所说的话,而且罗伯特的解决方案似乎是另一个很好的解决方案。非常感谢您花一点时间帮助我。 – user304602 2010-03-31 06:51:07

+0

嗨迈克,我正在实施您的解决方案,现在我有一个问题。我把自定义控件放到我的窗口中,现在我可以指示我想要的命令,因为我已经将它作为依赖属性注册到类中,但现在如何指示我想要的Executed和CanExecute方法?我想我的自定义控件的行为就像一个按钮,当你定义命令,执行和canExecute。谢谢。 – user304602 2010-03-31 11:19:20

0
+0

优秀的文章!我正在看这个试图将它应用到我的列表框中。我之前没有使用过附加的行为,但我认为这是命令的另一种选择。但查看它我使用命令或附加行为之间有点混淆。有哪些不同? (因为它们很相似)以及何时使用这些或另一个?哪种效率最高? 感谢您的出色答案。 – user304602 2010-03-31 06:57:44

+0

我认为附加行为对这种情况无效。例如,我为MouseEnter注册了一个事件监听器,并且第一次当后台工作器运行时,它工作,当mouseEnter事件第一次引发时,列表框项目显示为禁用。但是,一旦列表框项目(或项目)出现禁用,当后台工作完成时无法返回到启用状态。当然,这是因为它们被禁用并且MouseEnter事件没有引发。所以我认为附加行为不适合重新启用/禁用controls.Once你禁用他们有没有办法自己启用 – user304602 2010-03-31 09:09:08

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

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

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

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自动启用。如果我把这条线,它的作品。发生什么事?

另一件事,你可以在我的代码片段中看到我想要做的和我做一样的按钮,你可以指示命令,执行和canexecute。我已经在类中注册了它们,并且在列表框中我检查了传递方法,但它不起作用。我怎样才能做到这一点?

非常感谢。

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); 
     } 
    } 
} 
+0

请看我刚才的最后一篇文章。放在这里是非常广泛的。谢谢。 – user304602 2010-03-31 19:48:13

+0

好吧,我习惯了Josh Smith的RelayCommand,我一直无法使用RoutedCommand。如果你愿意,你可以使用RelayCommand。在我看来,这种方案更容易使用。无论如何,你有没有在UI调度器上调用CommandManager.InvalidateRequerySuggested()方法?那么,是否有可能在其他调度员中调用它? – 2010-04-01 04:20:57

+0

我不知道我是否理解你。我相信你指的是Control的Dispatcher.Invoke。我不使用Dispatcher.Invoke调用CommandManager.InvalidateRequerySuggested,只是直接调用它。我没有尝试过。据我所知,你只需要调用Dispatcher.Invoke,以防你想从一个不拥有它的线程更新任何UI控件。在我的情况下,没有必要使用它,因为我总是通过调用cmd Manager该控件的所有者。我在另一个线程中听说您必须从主线程调用InvalidateRequerySuggested,否则有时可用,有时不可用。 – user304602 2010-04-01 11:59:18

相关问题