2010-03-30 83 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

相關問題