【发布时间】:2019-10-01 05:08:22
【问题描述】:
我正在研究使用 MVVM 中的多重绑定来聚合 Leonid 的命令的解决方案。 https://www.codeproject.com/Articles/990113/MultiBinding-for-WPF-Command-Combining?msg=5666640#xx5666640xx 在菜单中使用此解决方案时,我遇到了问题。我添加了一个包含两个项目的简单菜单:item1 和 item2。如果选择了 menuitem item1,则 menuitem item1 和 item2 都会触发,这不是我想要的。我不理解的另一个方面是在选择MenuItem Item 1时,如果没有评论<Button.Command>的XAML部分,那么<Button.Command>尚未评论所有四个北,西,南和东部指挥火。按钮的多重绑定似乎并不严格绑定到按钮,并且可用于其他控件。有什么想法吗?
我知道 Josh Smith 的另一个命令聚合解决方案,但我读到他的解决方案并不完全适合 MVVM 上下文。
主窗口 XAML
<Window x:Class="MultiCommandButtonNoParams.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ice="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"
xmlns:vm="clr-namespace:MultiCommandButtonNoParams.ViewModels"
xmlns:bc="clr-namespace:MultiCommandButtonNoParams.BindingConverters"
Title="MultiCommandButtonNoParams" Height="350" Width="525">
<Window.Resources>
<vm:ViewModel x:Key="viewModel" />
<bc:MultiCommandConverter x:Key="multiCommandConverter"/>
</Window.Resources>
<Window.DataContext>
<StaticResource ResourceKey="viewModel"/>
</Window.DataContext>
<Grid ShowGridLines="False" Background="Ivory">
<Grid.RowDefinitions>
<RowDefinition Height="0.5*"></RowDefinition>
<RowDefinition Height="3*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<Menu Focusable="False">
<MenuItem Header="Menu">
<MenuItem Header="item1">
<MenuItem.Command>
<MultiBinding Converter="{StaticResource multiCommandConverter}" >
<Binding Path="NorthActionCommand"/>
</MultiBinding>
</MenuItem.Command>
</MenuItem>
<MenuItem Header="item2">
<MenuItem.Command>
<MultiBinding Converter="{StaticResource multiCommandConverter}" >
<Binding Path="WestActionCommand"/>
</MultiBinding>
</MenuItem.Command>
</MenuItem>
</MenuItem>
</Menu>
</StackPanel>
<StackPanel Grid.Row="1">
<TextBlock VerticalAlignment="Top" TextAlignment="Center" TextWrapping="Wrap" LineHeight="53">
<TextBlock.Inlines>
<Run Text="{Binding Path=NorthCommandManifest}" FontWeight="Bold" FontSize="18" Style="{StaticResource runForeground}" />
<LineBreak/>
<Run Text="{Binding Path=WestCommandManifest}" FontWeight="Heavy" FontSize="18" FontStyle="Italic" Style="{StaticResource runForeground}"/>
<LineBreak/>
<Run Text="{Binding Path=SouthCommandManifest}" FontWeight="ExtraBold" FontSize="18" FontStyle="Oblique" Style="{StaticResource runForeground}"/>
<LineBreak/>
<Run Text="{Binding Path=EastCommandManifest}" FontWeight="DemiBold" FontSize="18" FontStyle="Normal" Style="{StaticResource runForeground}"/>
</TextBlock.Inlines>
</TextBlock>
</StackPanel>
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button Grid.Column="1" Margin="10,2,10,2" Style="{StaticResource buttonBackground}" Focusable="False">
<!--<Button.Command>
Multicommand construction that consists of a set of sequentially executed commands.
Each command sends a message about execution to the TextBlock defined above.
<MultiBinding Converter="{StaticResource multiCommandConverter}" >
<Binding Path="NorthActionCommand"/>
<Binding Path="WestActionCommand"/>
<Binding Path="SouthActionCommand"/>
<Binding Path="EastActionCommand"/>
</MultiBinding>
</Button.Command>-->
<TextBlock FontWeight="Heavy" FontSize="18" TextAlignment="Center" TextWrapping="Wrap" Style="{StaticResource buttonForeground}">
Multi Command Button
</TextBlock>
</Button>
</Grid>
</Grid>
MultiCommandConverter.cs
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Windows.Data;
using MultiCommandButtonNoParams.Commands;
namespace MultiCommandButtonNoParams.BindingConverters
{
public class MultiCommandConverter : IMultiValueConverter
{
private List<object> _value = new List<object>( );
/// <summary>
/// dobbin of the converter
/// </summary>
/// <param name="value">commands binded by means of multibiniding</param>
/// <returns>compound Relay command</returns>
public object Convert( object[ ] value, Type targetType,
object parameter, CultureInfo culture )
{
_value.AddRange( value );
return new RelayCommand( GetCompoundExecute( ), GetCompoundCanExecute( ) );
}
/// <summary>
/// here - mandatory duty
/// </summary>
public object[ ] ConvertBack( object value, Type[ ] targetTypes,
object parameter, CultureInfo culture )
{
return null;
}
/// <summary>
/// for execution of all commands
/// </summary>
/// <returns>Action<object> that plays a role of the joint Execute</returns>
private Action<object> GetCompoundExecute( )
{
return ( parameter ) =>
{
foreach ( RelayCommand command in _value )
{
if ( command != default( RelayCommand ) )
command.Execute( parameter );
}
};
}
/// <summary>
/// for check if execution of all commands is possible
/// </summary>
/// <returns>Predicate<object> that plays a role of the joint CanExecute</returns>
private Predicate<object> GetCompoundCanExecute( )
{
return ( parameter ) =>
{
bool res = true;
foreach ( RelayCommand command in _value )
if ( command != default( RelayCommand ) )
res &= command.CanExecute( parameter );
return res;
};
}
}
}
【问题讨论】:
-
我将常用功能分解为方法,然后由任何需要它们的命令调用。在极少数情况下,我想要更复杂的功能,我会使用任务列表。将命令的功能分解为一个任务,每个命令一个。如果我有一些其他命令应该运行其中的几个,我会在这些任务的列表中执行 task.whenall。 Plinq 如果我需要按顺序运行它们。虽然因为我有这样的要求,但现在我可能会使用异步流来按顺序运行任务。
-
MultiBinding用于触发多个命令。如果您不想这样做,为什么需要MultiBinding?为什么不能只将Command属性绑定到单个命令? -
@mm8 在某些情况下,我只需要一个命令。在其他情况下需要依次触发多个命令并且需要使用
MultiBinding。 -
@namg_engr:所以当您需要触发单个命令时不要使用
MultiBinding,反之亦然? -
@mm8 是的。好点子。我的错。我只想知道为什么“MenuItem”中的所有命令在只选择一个时都会触发。