使用 WPF 绑定传递两个命令参数

我使用以下标准语法从 XAML 文件执行一个命令:

<Button Content="Zoom" Command="{Binding MyViewModel.ZoomCommand}"/>

这种方法工作得很好,直到我意识到我需要从视图中获得两条信息,才能使这个操作按照用户期望的方式完成(特别是画布的宽度和高度)。

似乎可以将一个数组作为参数传递给我的命令,但是我看不出有什么方法可以在 CommandParameter 中指定绑定到我的两个画布属性:

<Button Content="Zoom"
Command="{Binding MyViewModel.ZoomCommand"
CommandParameter="{Binding ElementName=MyCanvas, Path=Width}"/>

如何将 Width 和 Height 同时传递给我的命令?使用来自 XAML 的命令似乎不可能做到这一点,我需要在代码后台连接一个 click 处理程序,以便将这些信息传递给缩放方法。

205057 次浏览

首先,如果你正在做 MVVM,你通常会有这些信息可用于你的 VM 通过单独的属性绑定从视图。这样可以节省向命令传递任何参数的时间。

但是,您也可以使用多绑定并使用转换器来创建参数:

<Button Content="Zoom" Command="{Binding MyViewModel.ZoomCommand">
<Button.CommandParameter>
<MultiBinding Converter="{StaticResource YourConverter}">
<Binding Path="Width" ElementName="MyCanvas"/>
<Binding Path="Height" ElementName="MyCanvas"/>
</MultiBinding>
</Button.CommandParameter>
</Button>

在你的转换器中:

public class YourConverter : IMultiValueConverter
{
public object Convert(object[] values, ...)
{
return values.Clone();
}


...
}

然后,在命令执行逻辑中:

public void OnExecute(object parameter)
{
var values = (object[])parameter;
var width = (double)values[0];
var height = (double)values[1];
}

在 Converter 中使用 Tuple,在 onExecute 中将参数对象强制转换回 Tuple。

public class YourConverter : IMultiValueConverter
{
public object Convert(object[] values, ...)
{
Tuple<string, string> tuple = new Tuple<string, string>(
(string)values[0], (string)values[1]);
return (object)tuple;
}
}


// ...


public void OnExecute(object parameter)
{
var param = (Tuple<string, string>) parameter;
}

在所选解决方案的转换器中,应该添加值。否则,Clone ()命令中的参数结束为空

public class YourConverter : IMultiValueConverter
{
public object Convert(object[] values, ...)
{
return values.Clone();
}


...
}

关于在 Converter 中使用 Tuple,最好使用“ object”而不是“ string”,这样它可以适用于所有类型的对象,而不受“ string”对象的限制。

public class YourConverter : IMultiValueConverter
{
public object Convert(object[] values, ...)
{
Tuple<object, object> tuple = new Tuple<object, object>(values[0], values[1]);
return tuple;
}
}

那么 Command 中的执行逻辑可以是这样的

public void OnExecute(object parameter)
{
var param = (Tuple<object, object>) parameter;


// e.g. for two TextBox object
var txtZip = (System.Windows.Controls.TextBox)param.Item1;
var txtCity = (System.Windows.Controls.TextBox)param.Item2;
}

以及使用转换器创建参数的多绑定(使用两个 TextBox 对象)

<Button Content="Zip/City paste" Command="{Binding PasteClick}" >
<Button.CommandParameter>
<MultiBinding Converter="{StaticResource YourConvert}">
<Binding ElementName="txtZip"/>
<Binding ElementName="txtCity"/>
</MultiBinding>
</Button.CommandParameter>
</Button>

如果您的值是静态的,您可以使用 x:Array:

<Button Command="{Binding MyCommand}">10
<Button.CommandParameter>
<x:Array Type="system:Object">
<system:String>Y</system:String>
<system:Double>10</system:Double>
</x:Array>
</Button.CommandParameter>
</Button>

这个任务也可以用不同的方法来解决。不需要编写转换器并在 XAML 中放大代码,您还可以在 ViewModel 中聚合各种参数。因此,ViewModel 还有一个包含所有参数的属性。

我当前应用程序的一个示例,它也让我处理这个主题。 需要一个通用的 RelayCommand: < a href = “ https://stackoverflow.com/a/22286816/7678085”> https://stackoverflow.com/a/22286816/7678085

这里通过 SaveAndClose 命令扩展 ViewModelBase。泛型类型是表示各种参数的命名元组。

public ICommand SaveAndCloseCommand => saveAndCloseCommand ??= new RelayCommand<(IBaseModel Item, Window Window)>
(execute =>
{
execute.Item.Save();
execute.Window?.Close(); // if NULL it isn't closed.
},
canExecute =>
{
return canExecute.Item?.IsItemValide ?? false;
});
private ICommand saveAndCloseCommand;

然后它根据泛型类型包含一个属性:

public (IBaseModel Item, Window Window) SaveAndCloseParameter
{
get => saveAndCloseParameter ;
set
{
SetProperty(ref saveAndCloseParameter, value);
}
}
private (IBaseModel Item, Window Window) saveAndCloseParameter;

视图的 XAML 代码如下所示: (注意经典的点击事件)

<Button
Command="{Binding SaveAndCloseCommand}"
CommandParameter="{Binding SaveAndCloseParameter}"
Click="ButtonApply_Click"
Content="Apply"
Height="25" Width="100" />
<Button
Command="{Binding SaveAndCloseCommand}"
CommandParameter="{Binding SaveAndCloseParameter}"
Click="ButtonSave_Click"
Content="Save"
Height="25" Width="100" />

在视图后面的代码中,然后计算单击事件,然后设置参数属性。

private void ButtonApply_Click(object sender, RoutedEventArgs e)
{
computerViewModel.SaveAndCloseParameter = (computerViewModel.Computer, null);
}


private void ButtonSave_Click(object sender, RoutedEventArgs e)
{
computerViewModel.SaveAndCloseParameter = (computerViewModel.Computer, this);
}

就个人而言,我认为使用点击事件并不是与 MVVM 模式的决裂。程序流控制仍然位于 ViewModel 区域中。