16

リソース Dictionary 内に DataTempate として実装されたビューがあるとします。そして、対応するViewModelがあります。バインド コマンドは簡単です。しかし、View に ListBox などのコントロールが含まれていて、List で変更されている Item に基づいて (Prism の Event Aggregor を使用して) アプリケーション全体のイベントを発行する必要がある場合はどうでしょう。

ListBox がコマンドをサポートしている場合は、それを ViewModel のコマンドにバインドして、イベントを発行するだけです。しかし、Listbox ではそのようなオプションは許可されていません。どうすればこれを橋渡しできますか?

編集:多くの素晴らしい答え。

このリンクを見てください http://blogs.microsoft.co.il/blogs/tomershamam/archive/2009/04/14/wpf-commands-everywhere.aspx

ありがとう

アリエル

4

7 に答える 7

43

アイテムが変更されたときにコマンドをバインドしようとする代わりに、別の方法で問題を調べました。

ListBox の選択した項目を ViewModel のプロパティにバインドすると、そのプロパティが変更されたときにイベントを発行できます。そうすれば、ViewModel はイベントのソースのままであり、アイテムの変更によってトリガーされます。これが必要です。

<ListBox ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem}" />

...

public class ViewModel
{
    public IEnumerable<Item> Items { get; set; } 

    private Item selectedItem;
    public Item SelectedItem
    {
        get { return selectedItem; }
        set
        {
            if (selectedItem == value)
                return;
            selectedItem = value;
            // Publish event when the selected item changes
        }
}
于 2009-05-17T12:49:43.037 に答える
17

コントロールを拡張して ICommandSource をサポートし、コマンドをトリガーするアクションを決定します。

これを Combo Box で行い、コマンドのトリガーとして OnSelectionChanged を使用しました。最初に、CommandComboBox と呼ばれる拡張された Control ComboBox にコマンドをバインドする方法を XAML で示します。次に、ICommandSource のサポートを ComboBox に追加する CommandComboBox のコードを示します。

1) XAML コードで CommandComboBox を使用する:

XAML 名前空間の宣言には次のものが含まれます

   xmlns:custom="clr-namespace:WpfCommandControlsLibrary;assembly=WpfCommandControlsLibrary">

ComboBox の代わりに CommandComboBox を使用し、次のようにコマンドをバインドします。

 <custom:CommandComboBox 
    x:Name="ux_cbSelectLanguage"
    ItemsSource="{Binding Path = ImagesAndCultures}"
    ItemTemplate="{DynamicResource LanguageComboBoxTemplate}"           
    Command="{Binding Path=SetLanguageCommand, Mode=Default}"
    CommandParameter="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=SelectedValue, Mode=Default}"
    IsSynchronizedWithCurrentItem="True" 
    HorizontalAlignment="Right" 
    VerticalAlignment="Center" 
    Grid.Column="1" Margin="0,0,20,0" Style="{DynamicResource GlassyComboBox}" ScrollViewer.IsDeferredScrollingEnabled="True"
 />

2) CommandComboBox のコード

ファイル CommandComboBox.cs のコードを以下に示します。このファイルを WpfCommandControlsLibrary というクラス ライブラリに追加し、それを別のプロジェクトにしたので、拡張コマンドを使用するために必要なソリューションに簡単に追加でき、さらに WPF コントロールを簡単に追加して、ICommandSource インターフェイスをサポートするように拡張できました。

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

namespace WpfCommandControlsLibrary
{
   /// <summary>
   /// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file.
   ///
   /// Step 1a) Using this custom control in a XAML file that exists in the current project.
   /// Add this XmlNamespace attribute to the root element of the markup file where it is 
   /// to be used:
   ///
   ///     xmlns:MyNamespace="clr-namespace:WpfCommandControlsLibrary"
   ///
   ///
   /// Step 1b) Using this custom control in a XAML file that exists in a different project.
   /// Add this XmlNamespace attribute to the root element of the markup file where it is 
   /// to be used:
   ///
   ///     xmlns:MyNamespace="clr-namespace:WpfCommandControlsLibrary;assembly=WpfCommandControlsLibrary"
   ///
   /// You will also need to add a project reference from the project where the XAML file lives
   /// to this project and Rebuild to avoid compilation errors:
   ///
   ///     Right click on the target project in the Solution Explorer and
   ///     "Add Reference"->"Projects"->[Select this project]
   ///
   ///
   /// Step 2)
   /// Go ahead and use your control in the XAML file.
   ///
   ///     <MyNamespace:CustomControl1/>
   ///
   /// </summary>

   public class CommandComboBox : ComboBox, ICommandSource
   {
      public CommandComboBox() : base()
      {
      }

  #region Dependency Properties
  // Make Command a dependency property so it can use databinding.
  public static readonly DependencyProperty CommandProperty =
      DependencyProperty.Register(
          "Command",
          typeof(ICommand),
          typeof(CommandComboBox),
          new PropertyMetadata((ICommand)null,
          new PropertyChangedCallback(CommandChanged)));

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

  // Make CommandTarget a dependency property so it can use databinding.
  public static readonly DependencyProperty CommandTargetProperty =
      DependencyProperty.Register(
          "CommandTarget",
          typeof(IInputElement),
          typeof(CommandComboBox),
          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(CommandComboBox),
          new PropertyMetadata((object)null));

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

  #endregion

  // Command dependency property change callback.
  private static void CommandChanged(DependencyObject d,
      DependencyPropertyChangedEventArgs e)
  {
     CommandComboBox cb = (CommandComboBox)d;
     cb.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;
  }

  // Add the command.
  private void AddCommand(ICommand oldCommand, ICommand newCommand)
  {
     EventHandler handler = new EventHandler(CanExecuteChanged);
     canExecuteChangedHandler = handler;
     if (newCommand != null)
     {
        newCommand.CanExecuteChanged += canExecuteChangedHandler;
     }
  }
  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;
           }
        }
     }
  }

  // If Command is defined, selecting a combo box item will invoke the command;
  // Otherwise, combo box will behave normally.
  protected override void OnSelectionChanged(SelectionChangedEventArgs e)
  {
     base.OnSelectionChanged(e);

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

        if (command != null)
        {
           command.Execute(CommandParameter, CommandTarget);
        }
        else
        {
           ((ICommand)Command).Execute(CommandParameter);
        }
     }
  }

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

  }
}
于 2009-05-12T15:01:28.687 に答える
7

1 つのオプションは、問題のコントロールを拡張し、必要な特定のコマンドのサポートを追加することです。たとえば、イベントと関連するコマンドをサポートするために、以前に ListView を変更しました。ItemActivated

于 2009-05-11T13:11:04.063 に答える
2

この種の問題に対する優れた解決策は、添付プロパティを使用することです。Marlon Grech は、Attached Command Behaviorsを作成することで、Attached Properties の使用法を次のレベルに引き上げました。これらを使用すると、ViewModel に存在する Command をビューに存在する Event にバインドできます。

これは、ListBoxes に関する同様の問題に対処するためによく使用するもので、ダブルクリックで開いたり、編集したり、何らかのアクションを実行したりします。

この例では、古いバージョンの Attached Command Behaviors を使用していますが、効果は同じです。私は明示的にキーイングしている ListBoxItems に使用されるスタイルを持っています。ただし、より高いレベルでコマンドを設定するすべての ListBoxItems に適用されるアプリケーションまたはウィンドウ全体のスタイルを作成するのは簡単です。次に、CommandBehavior.Event プロパティに添付された ListBoxItem のイベントが発生するたびに、代わりに添付された Command を開始します。

<!-- acb is the namespace reference to the Attached Command Behaviors -->
<Style x:Key="Local_OpenListItemCommandStyle">
    <Setter Property="acb:CommandBehavior.Event"
            Value="MouseDoubleClick" />
    <Setter Property="acb:CommandBehavior.Command"
            Value="{Binding ElementName=uiMyListBorder, Path=DataContext.OpenListItemCommand}" />
    <Setter Property="acb:CommandBehavior.CommandParameter"
            Value="{Binding}" />
</Style>

<DataTemplate x:Key="MyView">
<Border x:Name="uiMyListBorder">
<ListBox  ItemsSource="{Binding MyItems}"
          ItemContainerStyle="{StaticResource local_OpenListItemCommandStyle}" />
</Border>
</DataTemplate>
于 2009-05-12T00:00:26.740 に答える
2

さて、誰も答えませんでした。だから私はあきらめて、ディクショナリの外側のビューの実装を通常のユーザーコントロールに移動し、ビューモデルへの参照を彼に注入しました。

ListBox がイベントを発生させると、ViewModel が呼び出され、そこからすべてが再び可能になります。

アリエル

于 2009-05-10T15:47:32.397 に答える
1

これを行うためにビヘイビア (添付プロパティ) を作成してきましたが、まだ必要な場合があります。

ただし、イベントをコマンドにバインドする通常のケースでは、Blend SDK 4 がインストールされていれば、Xaml ですべてを行うことができます。System.Windows.Interactivity.dll への参照を追加し、このアセンブリを再配布する必要があることに注意してください。

この例では、Grid の DragEnter イベントが発生したときに、ViewModel で ICommand DragEnterCommand を呼び出しています。

<UserControl xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" >
    <Grid>
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="DragEnter">
                <i:InvokeCommandAction Command="{Binding DragEnterCommand}" CommandParameter="{Binding ...}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </Grid>
</UserControl>
于 2013-03-25T15:16:38.180 に答える
0

Prism2を使用してみてください。

コマンドの優れた拡張機能が付属しており、多くの新しい可能性を開きます(ビジュアルツリーに結び付けるコマンドなど)。

于 2009-05-10T16:21:44.917 に答える