gpt4 book ai didi

c# - 在传递命令参数时绑定(bind)到代码中的命令

转载 作者:行者123 更新时间:2023-12-03 10:26:48 25 4
gpt4 key购买 nike

我最近在我的代码中实现了一个解决方案,允许我在我的 View 模型中绑定(bind)到我的命令。这是我使用的方法的链接:https://code.msdn.microsoft.com/Event-to-Command-24d903c8 .我在链接中使用了第二种方法。您可以出于所有意图和目的假设我的代码与此代码非常相似。这工作得很好。但是,我还需要为此双击绑定(bind)一个命令参数。我该如何设置?

这是我的项目的一些背景。这个项目背后的一些设置可能看起来很奇怪,但必须以这种方式完成,因为我不会在这里讨论很多细节。首先要注意的是,这种绑定(bind)设置发生在多值转换器内部。这是我生成新元素的代码:

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,也就是这里的viewmodel。 View 模型包含以下内容:
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 绑定(bind)所有这些东西,但从来没有从 C# 中完成。感谢您的任何帮助!

编辑

这是我的问题的完整示例。虽然我知道这个示例并没有实际目的来按照它的方式做事,但为了这个问题,我们将使用它来运行。

假设我有一个要显示的字符串的 ObservableCollection。这些包含在 View 模型中。
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 人员和我的项目经理决定密谋反对我,让我的生活变成一个活生生的 hell ,所以他们告诉我,我需要创建一个列表框来将这些项目显示为按钮,而不是在 XAML 中,而是在ContentControl 的内容绑定(bind)到的转换器。所以我这样做:
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;
}

这成功显示了列表框,所有项目都作为按钮。第二天,我的白痴项目经理在 View 模型中创建了一个新命令。它的目的是如果双击其中一个按钮,则将所选项目添加到列表框中。不是单击,而是双击!这意味着我不能使用 CommandProperty,或者更重要的是,不能使用 CommandParameterProperty。他在 View 模型中的命令看起来像这样:
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);

这成功地达到了我的项目经理的命令,但我仍然需要传递列表框的选定项。然后我的项目经理告诉我不再允许我触摸 View 模型。这就是我卡住的地方。如何仍将列表框的选定项发送到 View 模型中的项目经理的命令?

以下是此示例的完整代码文件:

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

再次感谢您的帮助!!!

最佳答案

您发布的代码实际上不是 最小 完整例子。至少,它缺少 CardManagementViewModel类型,当然该示例似乎是基于原始代码的,并没有尝试将其简化为最小示例。

因此,我没有花太多时间查看所有代码,更不用说我费心编译和运行它了。但是,原始编辑中缺少的主要内容是附加属性的实现。所以有了这个,我建议你改变你的Attached类,所以它看起来像这样:

public class Attached
{
public static ICommand GetDoubleClickCommand(DependencyObject obj)
{
return (ICommand)obj.GetValue(DoubleClickCommandProperty);
}

public static void SetDoubleClickCommand(DependencyObject obj, ICommand value)
{
obj.SetValue(DoubleClickCommandProperty, value);
}

public static object GetDoubleClickCommandParameter(DependencyObject obj)
{
return obj.GetValue(DoubleClickCommandParameterProperty);
}

public static void SetDoubleClickCommandParameter(DependencyObject obj, object value)
{
obj.SetValue(DoubleClickCommandParameterProperty, 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));
public static readonly DependencyProperty DoubleClickCommandParameterProperty =
DependencyProperty.RegisterAttached("DoubleClickCommandParameter", typeof(object), typeof(Attached));

static void CommandChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var fe = obj as FrameworkElement;

if (e.OldValue == null && e.NewValue != null)
{
fe.AddHandler(Button.MouseDoubleClickEvent, ExecuteCommand);
}
else if (e.OldValue != null && e.NewValue == null)
{
fe.RemoveHandler(Button.MouseDoubleClickEvent, ExecuteCommand);
}
}

static void ExecuteCommand(object sender, RoutedEventArgs e)
{
var ele = sender as Button;
ICommand command = GetDoubleClickCommand(ele);
object parameter = GetDoubleClickCommandParameter(ele);

command.Execute(parameter);
}
}

警告:以上内容只是在网络浏览器中输入的。因为缺少好的 Minimal, Complete, and Verifiable code example ,我懒得尝试编译了,没关系运行,上面。我相信如果有打印或逻辑错误,它们是最小的,您将能够根据您的目标轻松理解代码实际应该是什么。

这里的主要内容是我添加了 Attached.DoubleClickCommandParameter附属属性(property)。这将允许您在设置命令本身的同时设置命令参数。

我还更改了其他一些实现细节:
  • 引发事件时为给定对象检索命令及其参数,而不是保存 ICommandstatic字段,因为您的实现有它。按照你的代码的方式,你一次只能有一个命令。如果您尝试在多个元素上设置附加属性,并且使用了多个 ICommand值,你仍然只能得到最近设置的 ICommand .使用我的更改,您将始终获得您设置的命令。
  • 我更改了处理属性更改的代码,以便它仅在前一个值为 null 且新值为非 null 时添加处理程序,并且我还更改了代码以在值为时删除处理程序曾经从非空值变回空值。

  • 然后你可以像这样在代码隐藏中使用附加的属性:
    Attached.SetDoubleClickCommand(btn, ((CardManagementViewModel)values[1]).ChangeImageCommand);
    Attached.SetDoubleClickCommandParameter(btn, ((CardManagementViewModel)values[1]).ChangeImageCommandParameter);

    请注意,我假设您有一个 ChangeImageCommandParameter存储要发送的参数的属性。您当然可以将属性值设置为您想要的任何值,例如引用所选项目或其他内容的值。

    我还将设置更改为调用 Attached类的属性 setter 方法,这是对 WPF 中附加属性抽象的更恰当的使用。当然,在大多数实现中,它与调用 SetValue() 完全相同。方法,但最好通过附加属性的方法,以防它们以某种方式自定义行为。

    现在,说了这么多,我将重申你的更广泛的设计在几个不同的方面是非常错误的。通过忽略 MVVM 或类似的传统方法,将 UI 配置和行为绑定(bind)到 View 模型,尤其是使用转换器作为实际修改对象状态的地方,您正在创建一个可能有一个数字的系统其中包含微妙的、难以找到的和几乎不可能修复的错误。

    但这主要与如何使用附加属性(property)的问题无关。即使在设计良好的 WPF 程序中,附加属性也有其位置,我希望以上内容能让您更好地了解如何扩展现有附加属性以支持附加值(例如 CommandProperty 值)。

    关于c# - 在传递命令参数时绑定(bind)到代码中的命令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39965583/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com