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 が正しい値 (true または false) を取得し、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 を設定できますか?

カスタム コントロールの isEnabled プロパティを WPF で自動的に設定したいと考えています。これを行う方法?

前もって感謝します。


アタッチされた動作についてあなたが言う記事を適用して、いくつかの変更を加えてリストボックスに適応させています。うまくいかないか、何か間違ったことをしているのかもしれません。私が望むのは、長いタスク (バックグラウンド ワーカー) が実行されているときに、ListBox メンバー (listBoxItems) を選択できないようにすることです。したがって、私が変更した記事の方法の1つは次のとおりです。

    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 は、いくつかのメソッドとプロパティを持つ public static Class です。プロパティの 1 つは、バックグラウンド ワーカーが現在実行されていないことを示す isRunning です。次に、バックグラウンドワーカーが実行されていない場合、リストボックスメンバーを有効にする必要があります。それ以外の場合は無効にする必要があるため、ユーザーがリストボックスアイテムをクリックしても、以前に無効にしたため、現在のページが別のページに変更されません (各リストボックスアイテムには1つが添付されています)私のメインアプリケーションのページ)。

バックグラウンド ワーカー (bw) の 1 つまたはすべてが実行されていて、リストボックス アイテムを選択すると、すべて問題ありません。bw が実行されているため、リストボックス アイテムは無効になり、現在のページを別のページに変更することが回避されます。もちろん、リストボックス項目 (またはリストボックス項目) を無効にすると、無効になっているため再度選択できません。それが私の問題です。bw の実行中に無効になったリストボックス項目を bw が終了したときに、それらは再び有効になります。残念ながら、WPF によって自動的に行われるわけではなく、コマンドにはこの利点があります (コントロールは WPF によって自動的に更新されます)。それで、bwが実行されているかどうかにかかわらず、リストボックス項目を無効/再有効化する方法は?

私が知り、見る限り、アタッチされたビヘイビアーの利点の 1 つは、アクションを常に呼び出すわけではないため (選択などのアクションが生成された場合のみ)、より効率的であると考えられることです。コマンドは、コントロールにバインドされたアクションを実行できるかどうかを常に (頻繁ではありませんが) チェックしています (実行できる場合、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 によって自動的に有効になりません。この行を入れると動作します。どうしたの?

もう 1 つのことは、私のコード スニペットでわかるように、コマンド、実行、および実行可能を示すことができるボタンで行うのと同じことをしたいと考えています。それらをクラスに登録し、リストボックスでメソッドを渡すことを確認しましたが、機能しませんでした。これどうやってするの?

どうもありがとう。

于 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 プロパティを手動で有効/無効にするメソッドに配置された最後の行が気に入らないのです。効率が悪いので、リストボックスの isEnabled プロパティを変更したいのは、CanExecute がその値を変更したときだけです。これを行うイベントがあることを知っている限り、それは CanExecuteChanged ですが、実装方法がわかりません。何か案は?

現在、いくつかの解決策を試した後、マイクの解決策を実装しています。これは、より簡単で明確であり、いくつかの変更を加えるだけで他のコントロールに再利用できると思うからです。

于 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 に答える