最近、ビュー モデルでコマンドにバインドできるようにするソリューションをコードに実装しました。私が使用した方法へのリンクは次のとおりです: https://code.msdn.microsoft.com/Event-to-Command-24d903c8。リンクの2番目の方法を使用しました。私のコードがこのコードと非常によく似ていると、すべての意図と目的で想定できます。これはうまくいきます。ただし、このダブルクリックにもコマンド パラメーターをバインドする必要があります。どうすればそれを設定できますか?
これが私のプロジェクトの背景です。このプロジェクトの背後にあるセットアップのいくつかは奇妙に思えるかもしれませんが、ここでは詳細を説明しないため、この方法で行う必要があります。最初に注意すべきことは、このバインドのセットアップが多値コンバーターの内部で行われているということです。新しい要素を生成するコードは次のとおりです。
DataTemplate dt = new DataTemplate();
dt.DataType = typeof(Button);
FrameworkElementFactory btn = new FrameworkElementFactory(typeof(Button));
btn.SetValue(Attached.DoubleClickCommandProperty, ((CardManagementViewModel)values[1]).ChangeImageCommand);
dt.VisualTree = btn;
values[1] は DataContext で、ここではビューモデルです。ビュー モデルには次のものが含まれます。
private RelayCommand _ChangeImageCommand;
public ICommand ChangeImageCommand
{
get
{
if (_ChangeImageCommand == null)
{
_ChangeImageCommand = new RelayCommand(
param => this.ChangeImage(param)
);
}
return _ChangeImageCommand;
}
}
private void ChangeImage(object cardParam)
{
}
そのコマンドパラメーターを渡すにはどうすればよいですか? 私はこれまで何度も XAML を使用してこれらすべてをバインドしてきましたが、C# からバインドする必要はありませんでした。助けてくれてありがとう!
編集
これが私の問題の完全なサンプルです。このサンプルには、実際にこのように実行する目的がないことはわかっていますが、この問題のために、そのまま実行します。
表示したい文字列の ObservableCollection があるとしましょう。これらはビューモデルに含まれています。
private ObservableCollection<string> _MyList;
public ObservableCollection<string> MyList { get { return _MyList; } set { if (_MyList != value) { _MyList = value; RaisePropertyChanged("MyList"); } } }
public ViewModel()
{
MyList = new ObservableCollection<string>();
MyList.Add("str1");
MyList.Add("str2");
MyList.Add("str3");
}
私のチームのUI担当者が私にこれを渡してくれました
<ContentControl>
<ContentControl.Content>
<MultiBinding Converter="{StaticResource ResourceKey=MyConverter}">
<Binding Path="MyList"/>
<Binding />
</MultiBinding>
</ContentControl.Content>
</ContentControl>
ここで、UI 担当者とプロジェクト マネージャーが共謀して私の人生を生き地獄にしようと決心したとします。そのため、これらの項目を XAML ではなく、 ContentControl のコンテンツがバインドされているコンバーター。だから私はこれを行います:
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
ListBox lb = new ListBox();
lb.ItemsSource = (ObservableCollection<string>)values[0];
DataTemplate dt = new DataTemplate();
dt.DataType = typeof(Button);
FrameworkElementFactory btn = new FrameworkElementFactory(typeof(Button));
btn.SetValue(Button.WidthProperty, 100D);
btn.SetValue(Button.HeightProperty, 50D);
btn.SetBinding(Button.ContentProperty, new Binding());
dt.VisualTree = btn;
lb.ItemTemplate = dt;
return lb;
}
これが成功すると、リストボックスが表示され、すべての項目がボタンとして表示されます。翌日、ばかげたプロジェクト マネージャーがビュー モデルで新しいコマンドを作成します。その目的は、ボタンの 1 つがダブルクリックされた場合に、選択されたアイテムをリストボックスに追加することです。シングルクリックではなく、ダブルクリック!これは、CommandProperty を使用できないこと、さらに重要なことに CommandParameterProperty を使用できないことを意味します。ビューモデルでの彼のコマンドは次のようになります。
private RelayCommand _MyCommand;
public ICommand MyCommand
{
get
{
if (_MyCommand == null)
{
_MyCommand = new RelayCommand(
param => this.MyMethod(param)
);
}
return _MyCommand;
}
}
private void MyMethod(object myParam)
{
MyList.Add(myParam.ToString());
}
グーグルで検索した後、DoubleClick イベントを添付プロパティに変換するクラスを見つけました。そのクラスは次のとおりです。
public class Attached
{
static ICommand command;
public static ICommand GetDoubleClickCommand(DependencyObject obj)
{
return (ICommand)obj.GetValue(DoubleClickCommandProperty);
}
public static void SetDoubleClickCommand(DependencyObject obj, ICommand value)
{
obj.SetValue(DoubleClickCommandProperty, value);
}
// Using a DependencyProperty as the backing store for DoubleClickCommand. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DoubleClickCommandProperty =
DependencyProperty.RegisterAttached("DoubleClickCommand", typeof(ICommand), typeof(Attached), new UIPropertyMetadata(null, CommandChanged));
static void CommandChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var fe = obj as FrameworkElement;
command = e.NewValue as ICommand;
fe.AddHandler(Button.MouseDoubleClickEvent, new RoutedEventHandler(ExecuteCommand));
}
static void ExecuteCommand(object sender, RoutedEventArgs e)
{
var ele = sender as Button;
command.Execute(null);
}
}
次に、コンバーターに戻り、この行をdt.VisualTree = btn;のすぐ上に置きます。:
btn.SetValue(Attached.DoubleClickCommandProperty, ((ViewModel)values[1]).MyCommand);
これでプロジェクト マネージャーのコマンドは正常に実行されましたが、それでもリスト ボックスの選択項目を渡す必要があります。その後、プロジェクト マネージャーから、ビューモデルに触れることはもう許可されていないと言われました。これは私が立ち往生しているところです。ビュー モデルでプロジェクト マネージャーのコマンドにリストボックスの選択項目を送信するにはどうすればよいですか?
この例の完全なコード ファイルは次のとおりです。
ViewModel.cs
using System.Collections.ObjectModel;
using System.Windows.Input;
using WpfApplication2.Helpers;
namespace WpfApplication2
{
public class ViewModel : ObservableObject
{
private ObservableCollection<string> _MyList;
private RelayCommand _MyCommand;
public ObservableCollection<string> MyList { get { return _MyList; } set { if (_MyList != value) { _MyList = value; RaisePropertyChanged("MyList"); } } }
public ViewModel()
{
MyList = new ObservableCollection<string>();
MyList.Add("str1");
MyList.Add("str2");
MyList.Add("str3");
}
public ICommand MyCommand
{
get
{
if (_MyCommand == null)
{
_MyCommand = new RelayCommand(
param => this.MyMethod(param)
);
}
return _MyCommand;
}
}
private void MyMethod(object myParam)
{
MyList.Add(myParam.ToString());
}
}
}
MainWindow.xaml
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:helpers="clr-namespace:WpfApplication2.Helpers"
xmlns:Converters="clr-namespace:WpfApplication2.Helpers.Converters"
xmlns:local="clr-namespace:WpfApplication2"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<Window.Resources>
<Converters:MyConverter x:Key="MyConverter"/>
</Window.Resources>
<ContentControl>
<ContentControl.Content>
<MultiBinding Converter="{StaticResource ResourceKey=MyConverter}">
<Binding Path="MyList"/>
<Binding />
</MultiBinding>
</ContentControl.Content>
</ContentControl>
</Window>
MyConverter.cs
using System;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace WpfApplication2.Helpers.Converters
{
public class MyConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
ListBox lb = new ListBox();
lb.ItemsSource = (ObservableCollection<string>)values[0];
DataTemplate dt = new DataTemplate();
dt.DataType = typeof(Button);
FrameworkElementFactory btn = new FrameworkElementFactory(typeof(Button));
btn.SetValue(Button.WidthProperty, 100D);
btn.SetValue(Button.HeightProperty, 50D);
btn.SetBinding(Button.ContentProperty, new Binding());
btn.SetValue(Attached.DoubleClickCommandProperty, ((ViewModel)values[1]).MyCommand);
// Somehow create binding so that I can pass the selected item of the listbox to the
// above command when the button is double clicked.
dt.VisualTree = btn;
lb.ItemTemplate = dt;
return lb;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
添付.cs
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace WpfApplication2.Helpers
{
public class Attached
{
static ICommand command;
public static ICommand GetDoubleClickCommand(DependencyObject obj)
{
return (ICommand)obj.GetValue(DoubleClickCommandProperty);
}
public static void SetDoubleClickCommand(DependencyObject obj, ICommand value)
{
obj.SetValue(DoubleClickCommandProperty, value);
}
// Using a DependencyProperty as the backing store for DoubleClickCommand. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DoubleClickCommandProperty =
DependencyProperty.RegisterAttached("DoubleClickCommand", typeof(ICommand), typeof(Attached), new UIPropertyMetadata(null, CommandChanged));
static void CommandChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var fe = obj as FrameworkElement;
command = e.NewValue as ICommand;
fe.AddHandler(Button.MouseDoubleClickEvent, new RoutedEventHandler(ExecuteCommand));
}
static void ExecuteCommand(object sender, RoutedEventArgs e)
{
var ele = sender as Button;
command.Execute(null);
}
}
}
ObservableObject.cs
using System;
using System.ComponentModel;
using System.Diagnostics;
namespace WpfApplication2.Helpers
{
public class ObservableObject : INotifyPropertyChanged
{
#region Debugging Aides
/// <summary>
/// Warns the developer if this object does not have
/// a public property with the specified name. This
/// method does not exist in a Release build.
/// </summary>
[Conditional("DEBUG")]
[DebuggerStepThrough]
public virtual void VerifyPropertyName(string propertyName)
{
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = "Invalid property name: " + propertyName;
if (this.ThrowOnInvalidPropertyName)
throw new Exception(msg);
else
Debug.Fail(msg);
}
}
/// <summary>
/// Returns whether an exception is thrown, or if a Debug.Fail() is used
/// when an invalid property name is passed to the VerifyPropertyName method.
/// The default value is false, but subclasses used by unit tests might
/// override this property's getter to return true.
/// </summary>
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
#endregion // Debugging Aides
#region INotifyPropertyChanged Members
/// <summary>
/// Raises the PropertyChange event for the property specified
/// </summary>
/// <param name="propertyName">Property name to update. Is case-sensitive.</param>
public virtual void RaisePropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
OnPropertyChanged(propertyName);
}
/// <summary>
/// Raised when a property on this object has a new value.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises this object's PropertyChanged event.
/// </summary>
/// <param name="propertyName">The property that has a new value.</param>
protected virtual void OnPropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
#endregion // INotifyPropertyChanged Members
}
}
RelayCommand.cs
using System;
using System.Diagnostics;
using System.Windows.Input;
namespace WpfApplication2.Helpers
{
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
/// <summary>
/// Creates a new command that can always execute.
/// </summary>
/// <param name="execute">The execution logic.</param>
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
/// <summary>
/// Creates a new command.
/// </summary>
/// <param name="execute">The execution logic.</param>
/// <param name="canExecute">The execution status logic.</param>
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameters)
{
return _canExecute == null ? true : _canExecute(parameters);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameters)
{
_execute(parameters);
}
#endregion // ICommand Members
}
}
繰り返しますが、助けてくれてありがとう!!!